Repository navigation
Conversation
The flows and reaper containers run `tsx src/*-daemon.ts`. apps/web is a
CommonJS package, so tsx compiles their module graph to require() calls.
@opencode/client is "type": "module" and its exports declare only `import`
and `types`, so the static imports in the opencode-v2 adapter crashed both
daemons at startup with ERR_PACKAGE_PATH_NOT_EXPORTED. The Next.js server
was unaffected because webpack bundles the package.
Load the client with a plain dynamic import() through loadOpenCode(). Under
tsx it becomes a native ESM import that honours the `import` condition; under
Next/webpack it is still bundled, so desktop standalone tracing is unchanged.
createConnectionClient is now async accordingly.
Rejected alternatives:
- `tsx --conditions=import` / NODE_OPTIONS: a global condition also switches
pg to its ESM wrapper, and require('pg') then fails with "Class extends
value #<Object>"; it would also have to be repeated in package.json, both
compose templates and the one-click generator.
- Function('return import(x)') as in the v1 adapter: hides the package from
webpack and file tracing, so the desktop standalone build would need extra
outputFileTracingIncludes for the client and its dependencies.
Only the web image changes; compose files and launchers stay as they are.
Tests: daemons-runtime.test.ts starts the real daemon entrypoints with tsx
(no database, nothing contacted) and fails before this change with the
production error; client-runtime.test.ts loads the v2 client and events under
tsx. client.test.ts now rejects on an already-aborted signal like real fetch,
since the extra await lets the abort land before fetch is called.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
subscribeOpenCodeEvents duplicated the baseUrl and Authorization setup and needed loadOpenCode exported. createConnectionClient now accepts an optional fetch, so the loader stays private to client.ts. The comment records why a literal import() is kept instead of Function(). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- pre-warm the ESM client import so fake-timer tests pass when run alone - keep true in-flight cancellation in the losing-probe test: drop the already-aborted fetch guard and abort once fetch has been called - run the client runtime test through the tsx CLI on a temp script that also loads the events module, with an explicit timeout - move daemons-runtime.test.ts under src/__tests__ and force the flow daemon's startup failure through a missing KB config path Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Type it from the package's exported OpenCode.ClientOptions instead of deriving it from OpenCode.make's parameters. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Build the client before arming the deadline timer so a cold ESM import cannot consume the health budget, and return early when the caller signal is already aborted. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Pin the reaper failure cause, use a constant missing KB config path, write the client entry script under apps/web so it compiles as CommonJS like the daemons, and report the child's stderr on failure. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This was referenced Oct 23, 2026
Contributor
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Since #504 the
flowsandreapercontainers crash at startup, so scheduled flows never run and the reaper is down:Both daemons run
tsx src/*-daemon.ts.apps/webis a CommonJS package, so tsx compiles their module graph torequire().@opencode/client@2.0.32is"type": "module"and itsexportsonly declareimport/types, so the static value imports in the opencode-v2 adapter (client.ts,events.ts) cannot be required. The Next.js server is unaffected because webpack bundles the package.Fix
import('@opencode/client')(module-private loader inclient.ts). Under tsx it stays a native ESM import that honours theimportcondition; under Next/webpack it is still bundled and traced, so the desktop standalone build is unchanged.createConnectionClientis now async and takes an optional{ fetch };events.tsbuilds its client through it instead of duplicating the setup.checkHealthkeepstimeoutMsas a total budget exactly as before, except for the one-time cold module load, and returns early for an already-aborted caller signal.Rejected alternatives:
tsx --conditions=import/NODE_OPTIONS: a global condition also switchespgto its ESM wrapper and the daemons then die withTypeError: Class extends value #<Object>; it would also have to be repeated in package.json, both compose templates and the one-click generator.Function('return import(x)')(v1importRuntimeModulepattern): hides the package from webpack/file tracing, so the desktop standalone build would need extra tracing includes.Only the web image changes; compose files and launchers are untouched.
Tests
src/__tests__/daemons-runtime.test.tsstarts the real daemon entrypoints with thetsxCLI (no DB, nothing contacted) and asserts they get past module resolution and fail only on the expected missing config (DATABASE_URL is required/kb_unavailable: <path>). Fails onmainwith the production error.opencode-v2/__tests__/client-runtime.test.tsloads the v2 client and events modules through thetsxCLI from insideapps/web(CommonJS format) and builds a client.client.test.ts/runtime-execution.test.ts: pre-warm the client so fake-timer tests do not race the cold import when run alone; the losing-probe test aborts while the request is in flight again; new tests prove the module load is outside the health budget and a slow instance lookup still yieldshealthcheck_timeout.pnpm test: 6211 passed, 0 failed.pnpm lint: 0 errors.scripts/check-podman-images.sh: passed.Verified on a real deployment
Deployed
main(a978693) + this branch to a single-host deployment:[flow-daemon] Flow daemon started,[reaper-daemon] Reaper daemon started, the reaper pre-archived the V1 sessions, and the workspace started on V2. Reviewed with two independent code reviews, an adversarial review and a verifier pass.🤖 Generated with Claude Code