Update oauth cred tool

This commit is contained in:
Siddharth Ganesan
2026-03-10 23:35:01 -07:00
parent aa3be4b1d0
commit b024d63dbf

View File

@@ -28,6 +28,110 @@ const logger = createLogger('CopilotSseToolExecution')
const OUTPUT_PATH_TOOLS = new Set(['function_execute', 'user_table'])
/**
* Try to pull a flat array of row-objects out of the various shapes that
* `function_execute` and `user_table` can return.
*/
function extractTabularData(output: unknown): Record<string, unknown>[] | null {
if (!output || typeof output !== 'object') return null
if (Array.isArray(output)) {
if (output.length > 0 && typeof output[0] === 'object' && output[0] !== null) {
return output as Record<string, unknown>[]
}
return null
}
const obj = output as Record<string, unknown>
// function_execute shape: { result: [...], stdout: "..." }
if (Array.isArray(obj.result)) {
const rows = obj.result
if (rows.length > 0 && typeof rows[0] === 'object' && rows[0] !== null) {
return rows as Record<string, unknown>[]
}
}
// user_table query_rows shape: { data: { rows: [{ data: {...} }], totalCount } }
if (obj.data && typeof obj.data === 'object' && !Array.isArray(obj.data)) {
const data = obj.data as Record<string, unknown>
if (Array.isArray(data.rows) && data.rows.length > 0) {
const rows = data.rows as Record<string, unknown>[]
// user_table rows nest actual values inside .data
if (typeof rows[0].data === 'object' && rows[0].data !== null) {
return rows.map((r) => r.data as Record<string, unknown>)
}
return rows
}
}
return null
}
function escapeCsvValue(value: unknown): string {
if (value === null || value === undefined) return ''
const str = typeof value === 'object' ? JSON.stringify(value) : String(value)
if (str.includes(',') || str.includes('"') || str.includes('\n') || str.includes('\r')) {
return `"${str.replace(/"/g, '""')}"`
}
return str
}
function convertRowsToCsv(rows: Record<string, unknown>[]): string {
if (rows.length === 0) return ''
const headerSet = new Set<string>()
for (const row of rows) {
for (const key of Object.keys(row)) {
headerSet.add(key)
}
}
const headers = [...headerSet]
const lines = [headers.map(escapeCsvValue).join(',')]
for (const row of rows) {
lines.push(headers.map((h) => escapeCsvValue(row[h])).join(','))
}
return lines.join('\n')
}
type OutputFormat = 'json' | 'csv' | 'txt' | 'md' | 'html'
const EXT_TO_FORMAT: Record<string, OutputFormat> = {
'.json': 'json',
'.csv': 'csv',
'.txt': 'txt',
'.md': 'md',
'.html': 'html',
}
const FORMAT_TO_CONTENT_TYPE: Record<OutputFormat, string> = {
json: 'application/json',
csv: 'text/csv',
txt: 'text/plain',
md: 'text/markdown',
html: 'text/html',
}
function resolveOutputFormat(fileName: string, explicit?: string): OutputFormat {
if (explicit && explicit in FORMAT_TO_CONTENT_TYPE) return explicit as OutputFormat
const ext = fileName.slice(fileName.lastIndexOf('.')).toLowerCase()
return EXT_TO_FORMAT[ext] ?? 'json'
}
function serializeOutputForFile(output: unknown, format: OutputFormat): string {
if (typeof output === 'string') return output
if (format === 'csv') {
const rows = extractTabularData(output)
if (rows && rows.length > 0) {
return convertRowsToCsv(rows)
}
}
return JSON.stringify(output, null, 2)
}
async function maybeWriteOutputToFile(
toolName: string,
params: Record<string, unknown> | undefined,
@@ -38,30 +142,19 @@ async function maybeWriteOutputToFile(
if (!OUTPUT_PATH_TOOLS.has(toolName)) return result
if (!context.workspaceId || !context.userId) return result
const args = params?.args as Record<string, unknown> | undefined
const outputPath =
(params?.outputPath as string | undefined) ??
((params?.args as Record<string, unknown> | undefined)?.outputPath as string | undefined)
(params?.outputPath as string | undefined) ?? (args?.outputPath as string | undefined)
if (!outputPath) return result
const explicitFormat =
(params?.outputFormat as string | undefined) ?? (args?.outputFormat as string | undefined)
const fileName = outputPath.replace(/^files\//, '')
const format = resolveOutputFormat(fileName, explicitFormat)
try {
let content: string
if (typeof result.output === 'string') {
content = result.output
} else {
content = JSON.stringify(result.output, null, 2)
}
const contentType = fileName.endsWith('.json')
? 'application/json'
: fileName.endsWith('.csv')
? 'text/csv'
: fileName.endsWith('.md')
? 'text/markdown'
: fileName.endsWith('.html')
? 'text/html'
: 'text/plain'
const content = serializeOutputForFile(result.output, format)
const contentType = FORMAT_TO_CONTENT_TYPE[format]
const buffer = Buffer.from(content, 'utf-8')
const uploaded = await uploadWorkspaceFile(