Repository navigation
Conversation
…orker-javascript exec skill
🦋 Changeset detectedLatest commit: da8a3b1 The changes in this PR will be included in the next version bump. This PR includes changesets to release 4 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
| // recorded as changes and reaches the container on the next | ||
| // push. If materialize() then fails, the rollback below leaves | ||
| // the root empty rather than restoring the old copy. | ||
| if (stale.has(root)) await fs.rm(root, { recursive: true, force: true }); |
There was a problem hiding this comment.
🔴 Bundle refresh deletes existing workspace files
When a versioned mount refreshes, fs.rm deletes files that predated its first index. The new bundle cannot restore those files, so deployment loses workspace data.
Learn more
A mount's first index leaves existing files beneath its root intact. On a later version change, recursively removing that root also removes files the mount never created. The replacement bundle cannot recreate those files.
Example: A workspace has /workspace/.agents/skills/custom/SKILL.md before installing the bundled exec/SKILL.md. Both survive the first index. Updating the bundle removes custom/SKILL.md, although it never belonged to the bundle.
Recommended fix: Track the entries owned by the mount and delete only those during refresh, or preserve pre-existing entries. Test a version refresh after indexing a populated root.
Was this helpful? React with 👍 or 👎 to provide feedback.
| ); | ||
| const versionMatches = mount.version === undefined || row?.version === mount.version; | ||
| status.set(root, row?.indexed === 1 && versionMatches); | ||
| if (row?.indexed === 1 && !versionMatches) stale.add(root); |
There was a problem hiding this comment.
🔴 Interrupted refresh leaves stale bundle files
After an interrupted refresh, stale excludes the unindexed mount and a retry keeps its partial subtree. Files removed from the new bundle remain visible after indexing succeeds.
Learn more
A mount refresh updates its row to indexed=0 before removing the old subtree and writing the new one. Each filesystem write commits independently. If the durable object restarts between those operations, the next boot finds an unindexed row and does not clear the partial subtree. A successful retry writes its current files over that subtree, leaving any unrelated old files behind.
Example: Bundle v1 has a.txt and removed.txt; v2 drops removed.txt. A restart after setting indexed=0 but before removing the subtree leaves both old files. Retrying v2 overwrites a.txt and marks the mount indexed, but removed.txt still exists.
Recommended fix: Clear the mount subtree on every retry when an existing row has indexed=0, except when explicitly preserving content from a first index. Persist enough state to distinguish an interrupted refresh from a fresh index, and test interruption at the row update and mid-materialization.
Was this helpful? React with 👍 or 👎 to provide feedback.
| const key = [root, options.filter?.toString() ?? "", options.fileMode?.toString() ?? ""].join( | ||
| "\0", | ||
| ); | ||
| const cached = versionCache.get(key); | ||
| if (cached !== undefined) return cached; |
There was a problem hiding this comment.
🟡 Session filters reuse another bundle version
When filter or fileMode closes over session data, versionCache reuses the first session's hash. Later sessions can skip indexing changed selections and keep stale files.
Learn more
The default version represents the filtered paths, file modes, and bytes. Two closure instances with identical function source can nevertheless select different entries or modes. The module-wide cache keys only the directory and function text, so the second instance gets the first one's hash without evaluating its own filter. A workspace with the first hash stored skips refreshing, even though its desired contents have changed.
Example: A mount factory closes over a session's selected locale: filter: ({path}) => path.startsWith(locale). An en mount hashes first; another fr mount has the same function text and receives the en version. If a workspace previously indexed en, switching its selection to fr does not replace the files.
Recommended fix: Hash each mount instance when callbacks are supplied, or require an explicit stable cache key for closure-dependent options. Retain a cache only for options whose output depends solely on immutable bundle contents.
Was this helpful? React with 👍 or 👎 to provide feedback.
| if (!isDirectory(sourceDir)) { | ||
| this.error(`workerBundle: ${sourceDir} does not exist or is not a directory`); | ||
| } | ||
| for (const file of listFiles(sourceDir)) this.addWatchFile(file); |
There was a problem hiding this comment.
🟡 New bundle files do not trigger rebuild
During Vite watch builds, addWatchFile registers only existing files. Adding a file under the bundle directory triggers no rebuild, so preview keeps shipping the old tree.
Learn more
The plugin emits all files under its configured directory on each build. Vite's watch dependency list here includes only files present when buildStart runs. New files have not been registered and do not trigger another build, so the emitted asset list and Data modules remain unchanged.
Example: Start vite build --watch with src/templates/a.txt, then add src/templates/b.txt. No registered watched file changed, so the output still contains only a.txt until a different file forces a rebuild.
Recommended fix: Watch the directory itself or register a directory-specific watcher that invalidates the Worker environment when children are added or removed; test additions and removals during watch mode.
Was this helpful? React with 👍 or 👎 to provide feedback.
| return { | ||
| name: "@cloudflare/computer:worker-bundle", | ||
| apply: "build", |
There was a problem hiding this comment.
🔍 Bundled skills unavailable in Vite development
vite dev does not populate /bundle, so WorkerBundle rejects when constructing the workspace. Review whether build-and-preview is an acceptable development loop for consumers of the new Vite plugin.
Was this helpful? React with 👍 or 👎 to provide feedback.
commit: |
WorkerBundle()copies a directory from deployed Worker bundle into the workspace as a read-only mount. An additional Vite plugin is required when using Vite to ensure the files are copied into the bundle.This primarily exists to make it easier to ship skills with your application.
1. The provider:
WorkerBundle(path, options?)The code is in
packages/computer/src/mounts/providers/worker-bundle.ts, exported from@cloudflare/computer.Options
Deploys are versioned. Pushing a new bundle will update the files in the workspace.
The examples have been updated:
worker-shell,worker-javascriptandcontainereach shipsrc/skills/exec/SKILL.mdand mount it: