diff --git a/deno.json b/deno.json index 34123bf..46fa3d5 100644 --- a/deno.json +++ b/deno.json @@ -20,7 +20,7 @@ "@runt/pyodide-runtime-agent": "./packages/pyodide-runtime-agent/src/mod.ts", "@runt/python-runtime-agent": "./packages/python-runtime-agent/mod.ts", "@runt/tui": "./packages/tui/src/cli.tsx", - "jsr:@runtimed/schema": "jsr:@runtimed/schema@0.1.12", + "jsr:@runtimed/schema": "jsr:@runtimed/schema@0.3.0", "jsr:@std/cli": "jsr:@std/cli@^1.0.22", "jsr:@std/testing": "jsr:@std/testing@^1.0.15" }, diff --git a/packages/pyodide-runtime-agent/src/pyodide-agent.ts b/packages/pyodide-runtime-agent/src/pyodide-agent.ts index 3249804..82f60c8 100644 --- a/packages/pyodide-runtime-agent/src/pyodide-agent.ts +++ b/packages/pyodide-runtime-agent/src/pyodide-agent.ts @@ -374,6 +374,20 @@ export class PyodideRuntimeAgent extends RuntimeAgent { mountData, }); + // Send uploaded files to worker + const files = this.store.query(tables.files.select()); + // This is a bit of a hack because we don't have access to the artifact client in the worker. + // We send the base URL to the worker for now. + const artifactBaseUrl = this.config.artifactClient.getArtifactUrl(""); + + if (files.length > 0) { + this.sendWorkerMessage("files", { files, artifactBaseUrl }); + } + + this.onFilesUpload((files) => { + this.sendWorkerMessage("files", { files, artifactBaseUrl }); + }); + this.isInitialized = true; logger.info("Pyodide worker initialized successfully"); } catch (error) { diff --git a/packages/pyodide-runtime-agent/src/pyodide-worker.ts b/packages/pyodide-runtime-agent/src/pyodide-worker.ts index d97ce16..684753a 100644 --- a/packages/pyodide-runtime-agent/src/pyodide-worker.ts +++ b/packages/pyodide-runtime-agent/src/pyodide-worker.ts @@ -14,6 +14,7 @@ import { getEssentialPackages, isFirstRun, } from "./cache-utils.ts"; +import type { FileData } from "jsr:@runtimed/schema"; declare const self: DedicatedWorkerGlobalScope; @@ -112,6 +113,76 @@ await run_registered_tool("${data.toolName}", kwargs_string) break; } + case "files": { + if (!pyodide) { + throw new Error("Pyodide not initialized"); + } + + try { + // Cast the files to the expected type + const files = data.files as readonly FileData[]; + // Cast the artifact base URL to the expected type + const artifactBaseUrl = data.artifactBaseUrl as string; + + if (!artifactBaseUrl) { + throw new Error("Artifact base URL is required"); + } + + const filesInDirectory = pyodide.FS.readdir("./"); + + // Write/delete the new files + for (const file of files) { + if (file.deletedAt) { + if (!filesInDirectory.includes(file.fileName)) { + continue; + } + console.log("worker deleting file", { file }); + // Just in case we couldn't match the file, we want to put a try/catch + // to avoid the worker crashing + try { + pyodide.FS.unlink(`./${file.fileName}`); + } catch (error) { + // File doesn't exist, nothing to delete + console.log("file does not exist, skipping deletion", { + fileName: file.fileName, + error, + }); + } + } else { + console.log("worker fetching file", { file }); + console.log("artifactBaseUrl", artifactBaseUrl); + const response = await fetch( + artifactBaseUrl + file.artifactId, + ); + + // Handle different file types based on mimeType + if (file.mimeType && file.mimeType.startsWith("text/")) { + // Text files + const content = await response.text(); + pyodide.FS.writeFile(`./${file.fileName}`, content); + } else { + // Binary files (images, etc.) + const arrayBuffer = await response.arrayBuffer(); + const uint8Array = new Uint8Array(arrayBuffer); + pyodide.FS.writeFile(`./${file.fileName}`, uint8Array); + } + } + } + + console.log("worker wrote files and cleaned up old files", { data }); + + self.postMessage({ id, type: "response", data: { success: true } }); + } catch (error) { + console.error("Error in files message handler", error); + self.postMessage({ + id, + type: "response", + error: error instanceof Error ? error.message : String(error), + }); + } + break; + } + case "shutdown": { await shutdownWorker(); self.postMessage({ id, type: "response", data: { success: true } }); diff --git a/packages/schema/deno.json b/packages/schema/deno.json index 5b8ba7c..43b0bfd 100644 --- a/packages/schema/deno.json +++ b/packages/schema/deno.json @@ -19,7 +19,7 @@ }, "imports": { "fractional-indexing": "npm:fractional-indexing@^3.2.0", - "@runtimed/schema": "jsr:@runtimed/schema@0.1.12", + "@runtimed/schema": "jsr:@runtimed/schema@0.3.0", "jsr:@std/testing": "jsr:@std/testing@^1.0.15" }, "lint": {