From 64c0804aa563486b93f635ae309b0f5fa36d96e2 Mon Sep 17 00:00:00 2001 From: Andreas Gassmann Date: Fri, 10 Apr 2026 14:23:26 +0200 Subject: [PATCH] feat: add file upload --- examples/devtools-test.js | 44 +++++++++++++++++++++++ src/acurast/createJob.ts | 1 - src/devtools/acurast-processor.d.ts | 16 +++++++++ src/devtools/devtools-snippet.ts | 50 +++++++++++++++++++++++++-- src/devtools/injectDevtoolsSnippet.ts | 2 -- test/devtools.test.ts | 32 +---------------- 6 files changed, 108 insertions(+), 37 deletions(-) diff --git a/examples/devtools-test.js b/examples/devtools-test.js index 72c1269..700c762 100644 --- a/examples/devtools-test.js +++ b/examples/devtools-test.js @@ -3,6 +3,20 @@ console.log("Devtools test started", { jobId: _STD_.job.getId(), device: _STD_.device.getAddress() }) console.info("Processor info", { timestamp: Date.now() }) +// Probe runtime for HTTP/upload primitives +console.log("Runtime probe", { + fetch: typeof fetch, + FormData: typeof FormData, + Blob: typeof Blob, + XMLHttpRequest: typeof XMLHttpRequest, + Request: typeof Request, + Response: typeof Response, + Headers: typeof Headers, + Buffer: typeof Buffer, + btoa: typeof btoa, + atob: typeof atob, +}) + let tick = 0 const interval = setInterval(() => { tick++ @@ -20,6 +34,36 @@ const interval = setInterval(() => { } } + if (tick === 2) { + const filename = "test-upload.json" + const content = JSON.stringify({ + message: "hello from acurast processor", + jobId: _STD_.job.getId().id, + device: _STD_.device.getAddress(), + timestamp: Date.now(), + }) + + console.log("Uploading file: " + filename + " (" + content.length + " bytes)") + + _DEVTOOLS_.uploadFile( + filename, + content, + "application/json", + (fileInfo) => { + console.log("Upload succeeded", { + id: fileInfo.id, + filename: fileInfo.filename, + mimeType: fileInfo.mimeType, + fileSize: fileInfo.fileSize, + createdAt: fileInfo.createdAt, + }) + }, + (error) => { + console.error("Upload failed:", error) + } + ) + } + if (tick >= 12) { clearInterval(interval) console.log("Devtools test complete after " + tick + " ticks") diff --git a/src/acurast/createJob.ts b/src/acurast/createJob.ts index bdfcd34..2066ef6 100644 --- a/src/acurast/createJob.ts +++ b/src/acurast/createJob.ts @@ -84,7 +84,6 @@ export const createJob = async ( zipPath, config.entrypoint ?? basename(config.fileUrl), devtoolsApiUrl, - wallet.address, snippetDir ) filelogger.debug('Devtools snippet injected into bundle') diff --git a/src/devtools/acurast-processor.d.ts b/src/devtools/acurast-processor.d.ts index f110dc4..d236439 100644 --- a/src/devtools/acurast-processor.d.ts +++ b/src/devtools/acurast-processor.d.ts @@ -8,6 +8,22 @@ declare function httpPOST( onError: (error: string) => void ): void +declare const _DEVTOOLS_: { + uploadFile( + filename: string, + content: string, + mimeType: string, + onSuccess: (fileInfo: { + id: number + filename: string + mimeType: string + fileSize: number + createdAt: string + }) => void, + onError: (error: string) => void + ): void +} + declare const _STD_: { job: { getId(): { origin: { kind: string; source: string }; id: string } diff --git a/src/devtools/devtools-snippet.ts b/src/devtools/devtools-snippet.ts index eca2a86..49b3c2d 100644 --- a/src/devtools/devtools-snippet.ts +++ b/src/devtools/devtools-snippet.ts @@ -2,12 +2,10 @@ // of user scripts when enableDevtools is true. It overrides console methods to // forward logs to the Acurast DevTools API. // -// Placeholders __DEVTOOLS_API_URL__ and __DEVTOOLS_DEPLOYER__ are replaced at -// injection time by the CLI. +// Placeholder __DEVTOOLS_API_URL__ is replaced at injection time by the CLI. ;(() => { const DEVTOOLS_API_URL = '__DEVTOOLS_API_URL__' - const DEVTOOLS_DEPLOYER = '__DEVTOOLS_DEPLOYER__' const originalConsole = { log: console.log, @@ -133,6 +131,52 @@ } } + // --- File upload via /v1/files --- + const uploadFile = ( + filename: string, + content: string, + mimeType: string, + onSuccess: (fileInfo: { id: number; filename: string; mimeType: string; fileSize: number; createdAt: string }) => void, + onError: (error: string) => void + ) => { + if (!apiKey) { + onError('[devtools] file upload failed: not authenticated yet') + return + } + + // The processor's httpPOST JSON-parses the body regardless of Content-Type, + // so we cannot send multipart/form-data through it. Use fetch + FormData + // instead — fetch sets the Content-Type header (with boundary) automatically. + const formData = new FormData() + formData.append('file', new Blob([content], { type: mimeType }), filename) + + fetch(`${DEVTOOLS_API_URL}/v1/files`, { + method: 'POST', + headers: { Authorization: 'Bearer ' + apiKey }, + body: formData, + }) + .then((res: Response) => + res.text().then((text: string) => ({ ok: res.ok, status: res.status, text })) + ) + .then(({ ok, status, text }: { ok: boolean; status: number; text: string }) => { + if (!ok) { + onError('[devtools] file upload failed: HTTP ' + status + ' ' + text) + return + } + try { + onSuccess(JSON.parse(text)) + } catch (_e) { + onError('[devtools] failed to parse upload response: ' + text) + } + }) + .catch((err: any) => { + onError('[devtools] file upload failed: ' + (err?.message ?? String(err))) + }) + } + + // Expose _DEVTOOLS_ global + ;(globalThis as any)._DEVTOOLS_ = { uploadFile } + for (const level of Object.keys(originalConsole) as Array< keyof typeof originalConsole >) { diff --git a/src/devtools/injectDevtoolsSnippet.ts b/src/devtools/injectDevtoolsSnippet.ts index 9d1a170..211a33f 100644 --- a/src/devtools/injectDevtoolsSnippet.ts +++ b/src/devtools/injectDevtoolsSnippet.ts @@ -10,7 +10,6 @@ export async function injectDevtoolsSnippet( zipPath: string, entrypoint: string, devtoolsApiUrl: string, - deployerAddress: string, snippetDir: string ): Promise { const snippetPath = join(snippetDir, 'devtools-snippet.js') @@ -23,7 +22,6 @@ export async function injectDevtoolsSnippet( .trim() snippet = snippet.replace(/__DEVTOOLS_API_URL__/g, devtoolsApiUrl) - snippet = snippet.replace(/__DEVTOOLS_DEPLOYER__/g, deployerAddress) const zip = new AdmZip(zipPath) const entry = zip.getEntry(entrypoint) diff --git a/test/devtools.test.ts b/test/devtools.test.ts index a5995ff..69daacc 100644 --- a/test/devtools.test.ts +++ b/test/devtools.test.ts @@ -44,7 +44,6 @@ describe('devtools snippet injection', () => { zipPath, 'index.js', 'https://api.devtools.acurast.com', - '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY', SNIPPET_DIR ) @@ -64,13 +63,7 @@ describe('devtools snippet injection', () => { const zipPath = createTestZip('index.js', '// user code') const apiUrl = 'https://custom-api.example.com' - await injectDevtoolsSnippet( - zipPath, - 'index.js', - apiUrl, - '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY', - SNIPPET_DIR - ) + await injectDevtoolsSnippet(zipPath, 'index.js', apiUrl, SNIPPET_DIR) const zip = new AdmZip(zipPath) const content = zip.getEntry('index.js')!.getData().toString('utf-8') @@ -79,25 +72,6 @@ describe('devtools snippet injection', () => { expect(content).not.toContain('__DEVTOOLS_API_URL__') }) - it('should replace the deployer placeholder', async () => { - const zipPath = createTestZip('index.js', '// user code') - const deployer = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY' - - await injectDevtoolsSnippet( - zipPath, - 'index.js', - 'https://api.devtools.acurast.com', - deployer, - SNIPPET_DIR - ) - - const zip = new AdmZip(zipPath) - const content = zip.getEntry('index.js')!.getData().toString('utf-8') - - expect(content).toContain(deployer) - expect(content).not.toContain('__DEVTOOLS_DEPLOYER__') - }) - it('should strip TSC artifacts (export {}, sourcemap comment)', async () => { const zipPath = createTestZip('index.js', '// user code') @@ -105,7 +79,6 @@ describe('devtools snippet injection', () => { zipPath, 'index.js', 'https://api.devtools.acurast.com', - '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY', SNIPPET_DIR ) @@ -124,7 +97,6 @@ describe('devtools snippet injection', () => { zipPath, 'nonexistent.js', 'https://api.devtools.acurast.com', - '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY', SNIPPET_DIR ) ).rejects.toThrow('Could not find entrypoint') @@ -142,7 +114,6 @@ describe('devtools snippet injection', () => { zipPath, 'index.js', 'https://api.devtools.acurast.com', - '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY', SNIPPET_DIR ) @@ -163,7 +134,6 @@ describe('devtools snippet injection', () => { zipPath, 'index.js', 'https://api.devtools.acurast.com', - '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY', SNIPPET_DIR )