Describe the bug
@cloudflare/codemode@0.4.x's main entry (dist/index.js) has a static, top-level import { tool } from "ai", while ai is declared as an optional peer dependency (peerDependenciesMeta.ai.optional = true). Because DynamicWorkerExecutor and normalizeCode are exported only from that same entry module, importing the core executor pulls in the static ai import. When ai is not installed, bundlers that honor the optional-peer contract (Vite + @cloudflare/vite-plugin, in our case) replace ai with an empty stub and the named import tool fails to resolve — breaking the build of code that never touches the AI-SDK surface. The executor is functionally independent of the Vercel AI SDK, so this looks like a packaging issue (a static import of an optional peer in the shared entry) rather than an intended hard requirement.
To Reproduce
Steps to reproduce the behavior:
- In a Cloudflare Workers project built with Vite +
@cloudflare/vite-plugin, do not install ai.
- Import only the core executor:
import { DynamicWorkerExecutor, normalizeCode } from "@cloudflare/codemode";
- Run the build (
vite build).
- See the error:
[MISSING_EXPORT] "tool" is not exported by "__vite-optional-peer-dep:ai:@cloudflare/codemode".
node_modules/@cloudflare/codemode/dist/index.js:6:10
Expected behavior
Importing DynamicWorkerExecutor / normalizeCode (the non-AI-SDK core) should not require ai to be installed, consistent with ai being declared an optional peer.
Screenshots
N/A — build-time bundler error, full text included under "To Reproduce".
Version:
@cloudflare/codemode@0.4.1 (also reproduces on 0.4.0). Bundler: vite@8 + @cloudflare/vite-plugin@1.42 (Cloudflare Workers build).
Additional context
Root cause, in dist/index.js:
- line 6:
import { tool } from "ai"; (static, top-level)
tool is only used in the connector/runtime helpers (tool(...) at ~L1712 and a tool(options) { ... } method at ~L1845), i.e. createCodemodeRuntime / CodemodeConnector — not in DynamicWorkerExecutor (defined ~L185) or normalizeCode.
- All of these are exported from the same
index.js (~L1927), so the executor exports can't be reached without the ai-importing module being evaluated/linked.
Tree-shaking doesn't help: named-binding resolution against the optional-peer stub happens at link time, before unused-export elimination.
Suggested fix — make the ai dependency genuinely lazy so the optional peer is optional for bundlers:
- Use a dynamic
await import("ai") inside the connector/runtime helpers that call tool(...), or
- Move those AI-SDK-coupled helpers entirely behind the existing
./ai subpath (which already imports ai) and keep DynamicWorkerExecutor / normalizeCode on a main entry that has no ai import.
Either keeps import { DynamicWorkerExecutor } from "@cloudflare/codemode" working without ai installed.
Describe the bug
@cloudflare/codemode@0.4.x's main entry (dist/index.js) has a static, top-levelimport { tool } from "ai", whileaiis declared as an optional peer dependency (peerDependenciesMeta.ai.optional = true). BecauseDynamicWorkerExecutorandnormalizeCodeare exported only from that same entry module, importing the core executor pulls in the staticaiimport. Whenaiis not installed, bundlers that honor the optional-peer contract (Vite +@cloudflare/vite-plugin, in our case) replaceaiwith an empty stub and the named importtoolfails to resolve — breaking the build of code that never touches the AI-SDK surface. The executor is functionally independent of the Vercel AI SDK, so this looks like a packaging issue (a static import of an optional peer in the shared entry) rather than an intended hard requirement.To Reproduce
Steps to reproduce the behavior:
@cloudflare/vite-plugin, do not installai.import { DynamicWorkerExecutor, normalizeCode } from "@cloudflare/codemode";vite build).Expected behavior
Importing
DynamicWorkerExecutor/normalizeCode(the non-AI-SDK core) should not requireaito be installed, consistent withaibeing declared an optional peer.Screenshots
N/A — build-time bundler error, full text included under "To Reproduce".
Version:
@cloudflare/codemode@0.4.1(also reproduces on 0.4.0). Bundler:vite@8+@cloudflare/vite-plugin@1.42(Cloudflare Workers build).Additional context
Root cause, in
dist/index.js:import { tool } from "ai";(static, top-level)toolis only used in the connector/runtime helpers (tool(...)at ~L1712 and atool(options) { ... }method at ~L1845), i.e.createCodemodeRuntime/CodemodeConnector— not inDynamicWorkerExecutor(defined ~L185) ornormalizeCode.index.js(~L1927), so the executor exports can't be reached without theai-importing module being evaluated/linked.Tree-shaking doesn't help: named-binding resolution against the optional-peer stub happens at link time, before unused-export elimination.
Suggested fix — make the
aidependency genuinely lazy so the optional peer is optional for bundlers:await import("ai")inside the connector/runtime helpers that calltool(...), or./aisubpath (which already importsai) and keepDynamicWorkerExecutor/normalizeCodeon a main entry that has noaiimport.Either keeps
import { DynamicWorkerExecutor } from "@cloudflare/codemode"working withoutaiinstalled.