// Starts a server-side task export, waits for it and prints the download link. import { resolveApiUrl } from './tela-api.ts' type ExportJob = { id: string status: 'pending' | 'processing' | 'completed' | 'failed' | 'expired' totalTasks?: number processedTasks: number errorMessage?: string } const apiUrl = resolveApiUrl(process.argv[2]) const dataToken = process.env.DATA_TOKEN if (!dataToken) { console.error('DATA_TOKEN is required') process.exit(1) } const body = process.argv[3] if (!body) { console.error('Usage: bun run ./.claude/skills/workstation/export.ts \'{"promptApplicationId": "...", "filterCriteria": {...}, "options": {...}}\'') process.exit(1) } const payload = JSON.parse(body) as { filterCriteria?: Record } const filters = payload.filterCriteria ?? {} const list = (value: unknown) => value === undefined || value === null || value === '' ? undefined : [value].flat() function range(value: unknown) { const [since, until] = [value].flat() as Array return since || until ? [since || '', until || ''] : undefined } // page_context.filters mirrors the table's filter state, which allows single values; the export // API takes arrays, and a two-item date range where an empty string leaves that side open. payload.filterCriteria = { taskIds: list(filters.taskIds), status: list(filters.status), taskName: filters.taskName || undefined, approvedDate: range(filters.approvedDate), createdDate: range(filters.createdDate), updatedDate: range(filters.updatedDate), approvedBy: list(filters.approvedBy), createdBy: list(filters.createdBy), appVersion: list(filters.appVersion)?.[0], } const headers = { 'x-data-token': dataToken, 'Content-Type': 'application/json', } async function request(method: string, path: string, requestBody?: string): Promise { const response = await fetch(`${apiUrl}${path}`, { method, headers, body: requestBody }) if (!response.ok) { console.error(`${method} ${path} failed with status ${response.status}: ${await response.text()}`) process.exit(1) } return await response.json() as T } let job = await request('POST', '/task/export-job', JSON.stringify(payload)) const deadline = Date.now() + 10 * 60 * 1000 while (job.status === 'pending' || job.status === 'processing') { if (Date.now() > deadline) { console.error(`Export ${job.id} still ${job.status} after 10 minutes (${job.processedTasks}/${job.totalTasks ?? '?'} tasks).`) process.exit(1) } await Bun.sleep(3000) job = await request('GET', `/task/export-job/${job.id}`) } if (job.status !== 'completed') { console.error(`Export ${job.id} ${job.status}${job.errorMessage ? `: ${job.errorMessage}` : ''}`) process.exit(1) } const { url, expiresIn } = await request<{ url: string, expiresIn: number }>('GET', `/task/export-job/${job.id}/download`) process.stdout.write(`Exported ${job.totalTasks ?? job.processedTasks} tasks\nDownload (expires in ${Math.round(expiresIn / 60)} minutes): ${url}\n`)