// Uploads a file the user attached in the chat to Vault and prints the entry a task's file // variable takes: { file_url, file_name, mime_type }. import { realpathSync } from 'node:fs' import { basename, relative } from 'node:path' import { resolveApiUrl } from './tela-api.ts' const apiUrl = resolveApiUrl(process.argv[2]) const dataToken = process.env.DATA_TOKEN if (!dataToken) { console.error('DATA_TOKEN is required') process.exit(1) } const filePath = process.argv[3] if (!filePath) { console.error('Usage: bun run ./.claude/skills/workstation/attach.ts ') process.exit(1) } let realPath: string try { realPath = realpathSync(filePath) } catch { console.error(`File not found: ${filePath}`) process.exit(1) } // Only the user's own attachments, so the agent's repository files cannot be uploaded into a task. // Checked on the resolved path, so a symlink under attachments/ cannot point elsewhere. if (!/^(?:attachments|input)\//.test(relative(realpathSync(process.cwd()), realPath))) { console.error('Refused: only files the user attached (under attachments/ or input/) can be uploaded') process.exit(1) } const file = Bun.file(realPath) const content = await file.arrayBuffer() const fileName = basename(filePath) const mimeType = file.type.split(';')[0] || 'application/octet-stream' const created = await fetch(`${apiUrl}/_services/vault/files`, { method: 'POST', headers: { 'x-data-token': dataToken, 'Content-Type': 'application/json' }, body: JSON.stringify({ size: content.byteLength, mimeType, fileName, sha256sum: new Bun.CryptoHasher('sha256').update(content).digest('hex'), }), }) if (!created.ok) { console.error(`Vault upload failed with status ${created.status}: ${await created.text()}`) process.exit(1) } const { id, uploadUrl } = await created.json() as { id: string, uploadUrl: string } const uploaded = await fetch(uploadUrl, { method: 'PUT', headers: { 'Content-Type': mimeType }, body: content }) if (!uploaded.ok) { console.error(`Vault upload failed with status ${uploaded.status}: ${await uploaded.text()}`) process.exit(1) } process.stdout.write(`${JSON.stringify({ file_url: `vault://${id}`, file_name: fileName, mime_type: mimeType })}\n`)