@wrenai/genbi 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +141 -0
- package/bin/genbi.mjs +31 -0
- package/dist/assets/echarts-LTFC-ysd.js +40 -0
- package/dist/assets/index-Bo--QMUN.js +441 -0
- package/dist/assets/index-Xs4q7pNo.css +1 -0
- package/dist/index.html +14 -0
- package/dist/wrenai_logo.png +0 -0
- package/dist/wrenai_logo_white.png +0 -0
- package/dist-server/harness/auth/detect.d.ts +18 -0
- package/dist-server/harness/auth/detect.js +37 -0
- package/dist-server/harness/auth/index.d.ts +4 -0
- package/dist-server/harness/auth/index.js +2 -0
- package/dist-server/harness/auth/probe.d.ts +18 -0
- package/dist-server/harness/auth/probe.js +52 -0
- package/dist-server/harness/auth/types.d.ts +42 -0
- package/dist-server/harness/auth/types.js +9 -0
- package/dist-server/harness/bundle/loader.d.ts +32 -0
- package/dist-server/harness/bundle/loader.js +43 -0
- package/dist-server/harness/bundle/schema.d.ts +199 -0
- package/dist-server/harness/bundle/schema.js +127 -0
- package/dist-server/harness/bundle/version.d.ts +10 -0
- package/dist-server/harness/bundle/version.js +43 -0
- package/dist-server/harness/capability/gate.d.ts +6 -0
- package/dist-server/harness/capability/gate.js +29 -0
- package/dist-server/harness/capability/registry.d.ts +6 -0
- package/dist-server/harness/capability/registry.js +17 -0
- package/dist-server/harness/cli-args.d.ts +123 -0
- package/dist-server/harness/cli-args.js +247 -0
- package/dist-server/harness/cli.d.ts +2 -0
- package/dist-server/harness/cli.js +151 -0
- package/dist-server/harness/compile/cache.d.ts +23 -0
- package/dist-server/harness/compile/cache.js +109 -0
- package/dist-server/harness/compile/compose-profile.d.ts +34 -0
- package/dist-server/harness/compile/compose-profile.js +95 -0
- package/dist-server/harness/compile/errors.d.ts +22 -0
- package/dist-server/harness/compile/errors.js +41 -0
- package/dist-server/harness/compile/fingerprint.d.ts +22 -0
- package/dist-server/harness/compile/fingerprint.js +84 -0
- package/dist-server/harness/compile/index.d.ts +8 -0
- package/dist-server/harness/compile/index.js +7 -0
- package/dist-server/harness/compile/pipeline.d.ts +66 -0
- package/dist-server/harness/compile/pipeline.js +176 -0
- package/dist-server/harness/compile/resolve-binary.d.ts +55 -0
- package/dist-server/harness/compile/resolve-binary.js +133 -0
- package/dist-server/harness/compile/types.d.ts +107 -0
- package/dist-server/harness/compile/types.js +1 -0
- package/dist-server/harness/compile/warble-identity.d.ts +12 -0
- package/dist-server/harness/compile/warble-identity.js +33 -0
- package/dist-server/harness/compliance/gate.d.ts +35 -0
- package/dist-server/harness/compliance/gate.js +48 -0
- package/dist-server/harness/compliance/index.d.ts +2 -0
- package/dist-server/harness/compliance/index.js +1 -0
- package/dist-server/harness/compliance/types.d.ts +16 -0
- package/dist-server/harness/compliance/types.js +1 -0
- package/dist-server/harness/events/emitter.d.ts +34 -0
- package/dist-server/harness/events/emitter.js +25 -0
- package/dist-server/harness/events/index.d.ts +4 -0
- package/dist-server/harness/events/index.js +2 -0
- package/dist-server/harness/events/sse.d.ts +20 -0
- package/dist-server/harness/events/sse.js +48 -0
- package/dist-server/harness/events/types.d.ts +131 -0
- package/dist-server/harness/events/types.js +1 -0
- package/dist-server/harness/exec/errors.d.ts +16 -0
- package/dist-server/harness/exec/errors.js +29 -0
- package/dist-server/harness/exec/index.d.ts +5 -0
- package/dist-server/harness/exec/index.js +2 -0
- package/dist-server/harness/exec/local.d.ts +32 -0
- package/dist-server/harness/exec/local.js +134 -0
- package/dist-server/harness/exec/policy.d.ts +12 -0
- package/dist-server/harness/exec/policy.js +1 -0
- package/dist-server/harness/exec/types.d.ts +63 -0
- package/dist-server/harness/exec/types.js +1 -0
- package/dist-server/harness/guardrails/index.d.ts +2 -0
- package/dist-server/harness/guardrails/index.js +1 -0
- package/dist-server/harness/guardrails/policy.d.ts +45 -0
- package/dist-server/harness/guardrails/policy.js +57 -0
- package/dist-server/harness/index.d.ts +37 -0
- package/dist-server/harness/index.js +19 -0
- package/dist-server/harness/loop/errors.d.ts +53 -0
- package/dist-server/harness/loop/errors.js +72 -0
- package/dist-server/harness/loop/executor.d.ts +123 -0
- package/dist-server/harness/loop/executor.js +380 -0
- package/dist-server/harness/loop/guard.d.ts +16 -0
- package/dist-server/harness/loop/guard.js +1 -0
- package/dist-server/harness/loop/index.d.ts +5 -0
- package/dist-server/harness/loop/index.js +3 -0
- package/dist-server/harness/native-producer/index.d.ts +109 -0
- package/dist-server/harness/native-producer/index.js +302 -0
- package/dist-server/harness/npm-package-resolve.d.ts +28 -0
- package/dist-server/harness/npm-package-resolve.js +47 -0
- package/dist-server/harness/providers/adapters/anthropic.d.ts +7 -0
- package/dist-server/harness/providers/adapters/anthropic.js +6 -0
- package/dist-server/harness/providers/adapters/mock.d.ts +4 -0
- package/dist-server/harness/providers/adapters/mock.js +5 -0
- package/dist-server/harness/providers/adapters/openai-compatible.d.ts +10 -0
- package/dist-server/harness/providers/adapters/openai-compatible.js +10 -0
- package/dist-server/harness/providers/binding.d.ts +23 -0
- package/dist-server/harness/providers/binding.js +13 -0
- package/dist-server/harness/providers/errors.d.ts +6 -0
- package/dist-server/harness/providers/errors.js +12 -0
- package/dist-server/harness/providers/index.d.ts +18 -0
- package/dist-server/harness/providers/index.js +22 -0
- package/dist-server/harness/providers/registry.d.ts +16 -0
- package/dist-server/harness/providers/registry.js +24 -0
- package/dist-server/harness/render/envelope.d.ts +145 -0
- package/dist-server/harness/render/envelope.js +428 -0
- package/dist-server/harness/render/errors.d.ts +33 -0
- package/dist-server/harness/render/errors.js +46 -0
- package/dist-server/harness/render/index.d.ts +5 -0
- package/dist-server/harness/render/index.js +3 -0
- package/dist-server/harness/render/validate.d.ts +13 -0
- package/dist-server/harness/render/validate.js +88 -0
- package/dist-server/harness/route/adapter-spec.d.ts +40 -0
- package/dist-server/harness/route/adapter-spec.js +61 -0
- package/dist-server/harness/route/agent-sdk-cli.d.ts +30 -0
- package/dist-server/harness/route/agent-sdk-cli.js +86 -0
- package/dist-server/harness/route/agent-sdk-manifest.d.ts +31 -0
- package/dist-server/harness/route/agent-sdk-manifest.js +56 -0
- package/dist-server/harness/route/artifact-content.d.ts +53 -0
- package/dist-server/harness/route/artifact-content.js +83 -0
- package/dist-server/harness/route/chat-event-mapper.d.ts +95 -0
- package/dist-server/harness/route/chat-event-mapper.js +164 -0
- package/dist-server/harness/route/codex-ask-events.d.ts +25 -0
- package/dist-server/harness/route/codex-ask-events.js +395 -0
- package/dist-server/harness/route/codex-ask.d.ts +15 -0
- package/dist-server/harness/route/codex-ask.js +242 -0
- package/dist-server/harness/route/codex-local-cli.d.ts +20 -0
- package/dist-server/harness/route/codex-local-cli.js +78 -0
- package/dist-server/harness/route/codex-local-manifest.d.ts +22 -0
- package/dist-server/harness/route/codex-local-manifest.js +180 -0
- package/dist-server/harness/route/describe.d.ts +54 -0
- package/dist-server/harness/route/describe.js +84 -0
- package/dist-server/harness/route/dispatched.d.ts +138 -0
- package/dist-server/harness/route/dispatched.js +339 -0
- package/dist-server/harness/route/in-process.d.ts +43 -0
- package/dist-server/harness/route/in-process.js +106 -0
- package/dist-server/harness/route/index.d.ts +19 -0
- package/dist-server/harness/route/index.js +12 -0
- package/dist-server/harness/route/profile-source.d.ts +37 -0
- package/dist-server/harness/route/profile-source.js +76 -0
- package/dist-server/harness/route/route.d.ts +33 -0
- package/dist-server/harness/route/route.js +114 -0
- package/dist-server/harness/route/tier-binding.d.ts +36 -0
- package/dist-server/harness/route/tier-binding.js +63 -0
- package/dist-server/harness/route/types.d.ts +286 -0
- package/dist-server/harness/route/types.js +1 -0
- package/dist-server/harness/session/errors.d.ts +4 -0
- package/dist-server/harness/session/errors.js +7 -0
- package/dist-server/harness/session/gate.d.ts +11 -0
- package/dist-server/harness/session/gate.js +13 -0
- package/dist-server/harness/session/index.d.ts +5 -0
- package/dist-server/harness/session/index.js +3 -0
- package/dist-server/harness/session/run.d.ts +60 -0
- package/dist-server/harness/session/run.js +333 -0
- package/dist-server/harness/session/types.d.ts +29 -0
- package/dist-server/harness/session/types.js +1 -0
- package/dist-server/harness/setup/codex-events.d.ts +12 -0
- package/dist-server/harness/setup/codex-events.js +108 -0
- package/dist-server/harness/setup/runner.d.ts +497 -0
- package/dist-server/harness/setup/runner.js +1314 -0
- package/dist-server/harness/tools/errors.d.ts +107 -0
- package/dist-server/harness/tools/errors.js +141 -0
- package/dist-server/harness/tools/index.d.ts +13 -0
- package/dist-server/harness/tools/index.js +8 -0
- package/dist-server/harness/tools/mcp.d.ts +26 -0
- package/dist-server/harness/tools/mcp.js +15 -0
- package/dist-server/harness/tools/native.d.ts +179 -0
- package/dist-server/harness/tools/native.js +322 -0
- package/dist-server/harness/tools/parse.d.ts +14 -0
- package/dist-server/harness/tools/parse.js +18 -0
- package/dist-server/harness/tools/resolve-wren-binary.d.ts +12 -0
- package/dist-server/harness/tools/resolve-wren-binary.js +21 -0
- package/dist-server/harness/tools/resolve.d.ts +51 -0
- package/dist-server/harness/tools/resolve.js +79 -0
- package/dist-server/harness/tools/setup-native.d.ts +84 -0
- package/dist-server/harness/tools/setup-native.js +353 -0
- package/dist-server/harness/tools/wiring.d.ts +10 -0
- package/dist-server/harness/tools/wiring.js +16 -0
- package/dist-server/providers/setup.provider.yaml +31 -0
- package/dist-server/providers/wren.provider.yaml +33 -0
- package/dist-server/server/adopt.d.ts +87 -0
- package/dist-server/server/adopt.js +263 -0
- package/dist-server/server/app.d.ts +6 -0
- package/dist-server/server/app.js +2521 -0
- package/dist-server/server/auth-choice.d.ts +16 -0
- package/dist-server/server/auth-choice.js +36 -0
- package/dist-server/server/bin.d.ts +2 -0
- package/dist-server/server/bin.js +601 -0
- package/dist-server/server/bootstrap-project-binding.d.ts +31 -0
- package/dist-server/server/bootstrap-project-binding.js +71 -0
- package/dist-server/server/clarify.d.ts +10 -0
- package/dist-server/server/clarify.js +17 -0
- package/dist-server/server/codex-setup-mcp.d.ts +2 -0
- package/dist-server/server/codex-setup-mcp.js +147 -0
- package/dist-server/server/compose.d.ts +89 -0
- package/dist-server/server/compose.js +271 -0
- package/dist-server/server/conn-config.d.ts +30 -0
- package/dist-server/server/conn-config.js +253 -0
- package/dist-server/server/context-files.d.ts +12 -0
- package/dist-server/server/context-files.js +117 -0
- package/dist-server/server/context-map.d.ts +30 -0
- package/dist-server/server/context-map.js +95 -0
- package/dist-server/server/context-source.d.ts +68 -0
- package/dist-server/server/context-source.js +85 -0
- package/dist-server/server/db.d.ts +862 -0
- package/dist-server/server/db.js +1839 -0
- package/dist-server/server/enrichment-runner.d.ts +184 -0
- package/dist-server/server/enrichment-runner.js +213 -0
- package/dist-server/server/enrichment-verify.d.ts +73 -0
- package/dist-server/server/enrichment-verify.js +290 -0
- package/dist-server/server/enrichment.d.ts +232 -0
- package/dist-server/server/enrichment.js +199 -0
- package/dist-server/server/env-detect.d.ts +8 -0
- package/dist-server/server/env-detect.js +17 -0
- package/dist-server/server/env-field-hints.d.ts +46 -0
- package/dist-server/server/env-field-hints.js +48 -0
- package/dist-server/server/fold.d.ts +133 -0
- package/dist-server/server/fold.js +407 -0
- package/dist-server/server/harness-profile-sources.d.ts +3 -0
- package/dist-server/server/harness-profile-sources.js +24 -0
- package/dist-server/server/harness.d.ts +19 -0
- package/dist-server/server/harness.js +439 -0
- package/dist-server/server/impact.d.ts +18 -0
- package/dist-server/server/impact.js +85 -0
- package/dist-server/server/interactive-terminal.d.ts +175 -0
- package/dist-server/server/interactive-terminal.js +336 -0
- package/dist-server/server/native-artifacts.d.ts +1004 -0
- package/dist-server/server/native-artifacts.js +564 -0
- package/dist-server/server/native-dispatch-registry.d.ts +42 -0
- package/dist-server/server/native-dispatch-registry.js +45 -0
- package/dist-server/server/native-producer-fence.d.ts +14 -0
- package/dist-server/server/native-producer-fence.js +48 -0
- package/dist-server/server/native-session-resume.d.ts +6 -0
- package/dist-server/server/native-session-resume.js +103 -0
- package/dist-server/server/native-session-workspace.d.ts +40 -0
- package/dist-server/server/native-session-workspace.js +175 -0
- package/dist-server/server/native-sessions.d.ts +374 -0
- package/dist-server/server/native-sessions.js +1517 -0
- package/dist-server/server/native-wren-runtime.d.ts +20 -0
- package/dist-server/server/native-wren-runtime.js +259 -0
- package/dist-server/server/node-pty-host.d.ts +31 -0
- package/dist-server/server/node-pty-host.js +100 -0
- package/dist-server/server/route-intent.d.ts +40 -0
- package/dist-server/server/route-intent.js +23 -0
- package/dist-server/server/runtime-binding.d.ts +52 -0
- package/dist-server/server/runtime-binding.js +258 -0
- package/dist-server/server/source-catalog.d.ts +84 -0
- package/dist-server/server/source-catalog.js +221 -0
- package/dist-server/server/source-driver.d.ts +35 -0
- package/dist-server/server/source-driver.js +124 -0
- package/dist-server/server/spa.d.ts +2 -0
- package/dist-server/server/spa.js +51 -0
- package/dist-server/server/subscription-model-catalog.d.ts +27 -0
- package/dist-server/server/subscription-model-catalog.js +203 -0
- package/dist-server/server/turn.d.ts +249 -0
- package/dist-server/server/turn.js +1074 -0
- package/dist-server/server/wire-types.d.ts +799 -0
- package/dist-server/server/wire-types.js +1 -0
- package/dist-server/server/wren-home.d.ts +19 -0
- package/dist-server/server/wren-home.js +33 -0
- package/dist-server/server/wren-profiles.d.ts +43 -0
- package/dist-server/server/wren-profiles.js +123 -0
- package/package.json +93 -0
- package/profiles/genbi-default/context/binding.yml +9 -0
- package/profiles/genbi-default/ir.golden.json +506 -0
- package/profiles/genbi-default/profile.yml +16 -0
- package/profiles/genbi-enrich-context/components/apply_enrichment/component.yml +42 -0
- package/profiles/genbi-enrich-context/components/draft_enrichment/component.yml +30 -0
- package/profiles/genbi-enrich-context/components/draft_enrichment/steps/draft.md +34 -0
- package/profiles/genbi-enrich-context/components/inspect_context/component.yml +32 -0
- package/profiles/genbi-enrich-context/components/inspect_context/steps/inspect.md +13 -0
- package/profiles/genbi-enrich-context/context/binding.yml +4 -0
- package/profiles/genbi-enrich-context/ir.golden.json +302 -0
- package/profiles/genbi-enrich-context/profile.yml +15 -0
- package/profiles/genbi-monitor/context/binding.yml +7 -0
- package/profiles/genbi-monitor/ir.golden.json +188 -0
- package/profiles/genbi-monitor/profile.yml +27 -0
- package/profiles/genbi-setup/components/build_context/component.yml +44 -0
- package/profiles/genbi-setup/components/build_context/steps/build.md +14 -0
- package/profiles/genbi-setup/components/connect_source/component.yml +51 -0
- package/profiles/genbi-setup/components/connect_source/steps/connect.md +23 -0
- package/profiles/genbi-setup/context/binding.yml +8 -0
- package/profiles/genbi-setup/ir.golden.json +140 -0
- package/profiles/genbi-setup/profile.yml +18 -0
- package/profiles/genbi-setup/raw/schema.json +5 -0
|
@@ -0,0 +1,601 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* The BFF's process entrypoint. This is the ONLY file
|
|
4
|
+
* in `server/` that wires real production values (env vars, a real `Store`
|
|
5
|
+
* file, the real `route()` from `../harness/index.js`); every other `server/*`
|
|
6
|
+
* module takes its dependencies injected (`TurnDeps`) so tests never import
|
|
7
|
+
* this file.
|
|
8
|
+
*
|
|
9
|
+
* Env vars:
|
|
10
|
+
* PORT HTTP port to listen on (default 4787)
|
|
11
|
+
* WREN_BFF_DB_PATH SQLite file path (default "./wren-harness-bff.sqlite")
|
|
12
|
+
* WREN_HARNESS_WORKSPACE_ROOT REQUIRED. Dir the agentic setup wizard scaffolds NEW wren
|
|
13
|
+
* projects into (`<root>/<name>/`). The BFF always boots
|
|
14
|
+
* "bootstrap-pending": no project is bound yet, Ask-turn routes
|
|
15
|
+
* 409 until the setup wizard binds one via TurnDeps.bindProject
|
|
16
|
+
* (see server/turn.ts's resolveUserProject /
|
|
17
|
+
* effectiveRouteOptions). `/api/setup/*` and
|
|
18
|
+
* `/api/config/runtime` work unbound. An EXISTING project is
|
|
19
|
+
* taken through the wizard's adopt flow (POST /api/setup/adopt),
|
|
20
|
+
* which accepts any path on disk and survives a restart via
|
|
21
|
+
* recoverBootstrapProjectBinding — there is no boot-time
|
|
22
|
+
* binding env var.
|
|
23
|
+
* WREN_HARNESS_SETUP_IR path to the compiled genbi-setup IR (the profile's
|
|
24
|
+
* `connect_source` component) the setup wizard dispatches via
|
|
25
|
+
* dispatched's DispatchedOptions.irPath bypass. Defaults to
|
|
26
|
+
* resolveDefaultSetupIrPath()'s walk for this package's own
|
|
27
|
+
* profiles/genbi-setup/ir.golden.json; unlike WREN_HARNESS_PROFILE
|
|
28
|
+
* this does NOT hard-fail at boot if unresolved — only
|
|
29
|
+
* POST /api/setup/connect(/resume) needs it, and those return a
|
|
30
|
+
* clear 500 at dispatch time instead.
|
|
31
|
+
* WREN_HARNESS_PROFILE profileSource; default resolveDefaultProfileSource()
|
|
32
|
+
* WREN_HARNESS_MODE subscription|api-key|local|gateway (auth mode)
|
|
33
|
+
* WREN_HARNESS_PROVIDER subscription provider: claude|codex
|
|
34
|
+
* WREN_HARNESS_ADAPTER api-key adapter name
|
|
35
|
+
* WREN_HARNESS_API_KEY api-key credential
|
|
36
|
+
* WREN_HARNESS_MODEL model override
|
|
37
|
+
* WREN_HARNESS_ENDPOINT local/gateway endpoint URL
|
|
38
|
+
* WREN_HARNESS_WARBLE_BIN path to the warble CLI binary
|
|
39
|
+
* WREN_HARNESS_WREN_SHIM optional absolute server-owned Wren shim for
|
|
40
|
+
* native Codex runtime permissions; defaults to
|
|
41
|
+
* the fixed local installation when unset
|
|
42
|
+
* WREN_HARNESS_AGENT_SDK_BIN path to the agent-sdk CLI binary (dispatched)
|
|
43
|
+
* WREN_HARNESS_CODEX_LOCAL_BIN path to the warble-codex-local dispatcher CLI
|
|
44
|
+
* WREN_HARNESS_CODEX_BIN optional path to the Codex CLI used by that dispatcher
|
|
45
|
+
* WREN_HARNESS_CODEX_HOME absolute dedicated, externally authenticated CODEX_HOME used
|
|
46
|
+
* by persistent codex:local Ask sessions; must not be the default
|
|
47
|
+
* Codex home and must not contain config.toml
|
|
48
|
+
* WREN_HARNESS_OUT output dir forwarded to route() (dispatched run dir; also in-process's
|
|
49
|
+
* native write_artifact scope root, unless WREN_HARNESS_ARTIFACTS_DIR
|
|
50
|
+
* is set — see harness/route/in-process.ts's resolveArtifactsDir)
|
|
51
|
+
* WREN_HARNESS_ARTIFACTS_DIR in-process only: overrides resolveArtifactsDir's default
|
|
52
|
+
* (an os.tmpdir() path) when WREN_HARNESS_OUT isn't set — read
|
|
53
|
+
* directly by resolveArtifactsDir, not plumbed through CliFlags
|
|
54
|
+
* WREN_HARNESS_DEPLOYMENT personal|hosted (default personal)
|
|
55
|
+
* WREN_HARNESS_TIER_ADAPTER repeatable via commas: "<tier>=<mode>[:<field>=<value>,...]"
|
|
56
|
+
* (see harness/cli-args.ts's parseTierAdapterFlag), e.g.
|
|
57
|
+
* "cheap=local:endpoint=http://localhost:11434/v1,model=llama3.1"
|
|
58
|
+
* WREN_HARNESS_MODELS_CONFIG path forwarded verbatim to warble-agent-sdk chat (dispatched)
|
|
59
|
+
* WREN_HARNESS_CHAT_TIMEOUT_MS dispatched only: overrides spawnChat's default 10-minute hang
|
|
60
|
+
* guard on a "warble-agent-sdk chat" invocation. Raise this for a
|
|
61
|
+
* legitimately slower cold-start Ask turn (e.g. right after
|
|
62
|
+
* onboarding a project, before any compile cache is warm) instead
|
|
63
|
+
* of the turn being killed and reported as a timeout. Must be a
|
|
64
|
+
* positive integer (milliseconds); an invalid value fails fast at
|
|
65
|
+
* boot via the same parseChatTimeoutMs used by harness/cli.ts.
|
|
66
|
+
* WREN_HARNESS_SETUP_MAX_TURNS agentic setup only: caps the agent loop per setup turn,
|
|
67
|
+
* forwarded as "warble-agent-sdk chat --max-turns". Defaults to
|
|
68
|
+
* DEFAULT_SETUP_MAX_TURNS (120) — well above the dispatcher's own
|
|
69
|
+
* 40 — because build_context (generating an MDL) needs many turns
|
|
70
|
+
* and otherwise dies with error_max_turns. Positive integer.
|
|
71
|
+
* WREN_HARNESS_ENRICH_IR a PREBUILT genbi-enrich-context IR — in practice the profile's
|
|
72
|
+
* eval golden, i.e. that profile compiled against this repo's own
|
|
73
|
+
* example project with that project's schema baked into
|
|
74
|
+
* `context_binding.resolved`. It is NOT a per-user artifact and is
|
|
75
|
+
* no longer what a draft or a native session dispatches: both now
|
|
76
|
+
* compile against the bound project (see `resolveDispatchIr` and
|
|
77
|
+
* the enrichment runner's `profileSource`). What it still feeds is
|
|
78
|
+
* the producer contract probe and readiness, which ask whether this
|
|
79
|
+
* Warble can dispatch this profile SHAPE at all and have no user
|
|
80
|
+
* project in view — a fixture is the correct input there.
|
|
81
|
+
* Defaults to
|
|
82
|
+
* resolveDefaultEnrichIrPath()'s walk for this package's own
|
|
83
|
+
* profiles/genbi-enrich-context/ir.golden.json; like
|
|
84
|
+
* WREN_HARNESS_SETUP_IR this does NOT hard-fail at boot if
|
|
85
|
+
* unresolved — GET /api/context/enrichment reports the draft
|
|
86
|
+
* capability unavailable until one is configured, and
|
|
87
|
+
* POST /api/context/enrichment/start 503s. Claude subscription
|
|
88
|
+
* auth only; apply/reconcile/approval remain unwired (see the
|
|
89
|
+
* ticket).
|
|
90
|
+
*
|
|
91
|
+
* These mirror harness/cli.ts's `--project`/`--profile`/`--mode`/... flags
|
|
92
|
+
* one-to-one (env vars instead of flags, since this is a long-running server
|
|
93
|
+
* rather than a one-shot CLI invocation) and reuse the same cli-args.ts
|
|
94
|
+
* helpers harness/cli.ts itself uses for auth/deployment/tier-binding resolution.
|
|
95
|
+
*/
|
|
96
|
+
import { serve } from "@hono/node-server";
|
|
97
|
+
import { createRequire } from "node:module";
|
|
98
|
+
import { existsSync } from "node:fs";
|
|
99
|
+
import path from "node:path";
|
|
100
|
+
import { fileURLToPath } from "node:url";
|
|
101
|
+
import { WebSocketServer } from "ws";
|
|
102
|
+
import { createDefaultLoginProbe, CodexSetupRunner, describeBundle, deriveAdapterSpec, enforceCompliance, InProcessSetupRunner, DispatchedSetupRunner, resolveDefaultEnrichIrPath, resolveDefaultProfileSource, resolveDefaultSetupIrPath, route, resolveArtifactsDir, selectSetupRunnerForAuth, } from "../harness/index.js";
|
|
103
|
+
import { buildTierBindingFromFlags, parseChatTimeoutMs, parseSetupMaxTurns, resolveAuthChoice, resolveDeployment, } from "../harness/cli-args.js";
|
|
104
|
+
import { createApp } from "./app.js";
|
|
105
|
+
import { recoverBootstrapProjectBinding } from "./bootstrap-project-binding.js";
|
|
106
|
+
import { createHarnessProfileSources } from "./harness-profile-sources.js";
|
|
107
|
+
import { compileProfile, compileRawProfile } from "../harness/compile/index.js";
|
|
108
|
+
import { toAuthChoiceFromRuntimeSettings } from "./auth-choice.js";
|
|
109
|
+
import { Store } from "./db.js";
|
|
110
|
+
import { resolveEnrichmentBinding, resolveProjectIdentity } from "./enrichment.js";
|
|
111
|
+
import { createDispatchedEnrichmentDraftRunner } from "./enrichment-runner.js";
|
|
112
|
+
import { dispatchInteractiveArtifacts, getInteractiveTerminalReadiness, InteractiveLaunchError, InteractiveTerminalManager, prepareInteractiveHandoff, unavailableInteractiveReadiness } from "./interactive-terminal.js";
|
|
113
|
+
import { createProbedPtyFactory, ensureDarwinNodePtySpawnHelper } from "./node-pty-host.js";
|
|
114
|
+
import { NativeSessionService } from "./native-sessions.js";
|
|
115
|
+
import { NativeArtifactService } from "./native-artifacts.js";
|
|
116
|
+
import { initializeNativeSessionStateBase, legacyInteractiveWorkspace, validateLegacyInteractiveWorkspace } from "./native-session-workspace.js";
|
|
117
|
+
import { codexModelsForRuntime, compileUnboundProfileTierNames, effectiveTierModel, materializeRuntimeRouteOptions, } from "./runtime-binding.js";
|
|
118
|
+
import { invalidateBundleAgentIdsCache } from "./turn.js";
|
|
119
|
+
import { createSetWrenHomeForSetupMode } from "./wren-home.js";
|
|
120
|
+
import { createSubscriptionModelCatalog } from "./subscription-model-catalog.js";
|
|
121
|
+
function envFlags() {
|
|
122
|
+
const env = process.env;
|
|
123
|
+
const tierAdapters = env["WREN_HARNESS_TIER_ADAPTER"];
|
|
124
|
+
const chatTimeoutMs = parseChatTimeoutMs(env["WREN_HARNESS_CHAT_TIMEOUT_MS"]);
|
|
125
|
+
return {
|
|
126
|
+
...(env["WREN_HARNESS_PROFILE"] !== undefined ? { profile: env["WREN_HARNESS_PROFILE"] } : {}),
|
|
127
|
+
...(env["WREN_HARNESS_MODE"] !== undefined ? { mode: env["WREN_HARNESS_MODE"] } : {}),
|
|
128
|
+
...(env["WREN_HARNESS_PROVIDER"] !== undefined ? { provider: env["WREN_HARNESS_PROVIDER"] } : {}),
|
|
129
|
+
...(env["WREN_HARNESS_ADAPTER"] !== undefined ? { adapter: env["WREN_HARNESS_ADAPTER"] } : {}),
|
|
130
|
+
...(env["WREN_HARNESS_API_KEY"] !== undefined ? { apiKey: env["WREN_HARNESS_API_KEY"] } : {}),
|
|
131
|
+
...(env["WREN_HARNESS_MODEL"] !== undefined ? { model: env["WREN_HARNESS_MODEL"] } : {}),
|
|
132
|
+
...(env["WREN_HARNESS_ENDPOINT"] !== undefined ? { endpoint: env["WREN_HARNESS_ENDPOINT"] } : {}),
|
|
133
|
+
...(env["WREN_HARNESS_WARBLE_BIN"] !== undefined ? { warbleBin: env["WREN_HARNESS_WARBLE_BIN"] } : {}),
|
|
134
|
+
...(env["WREN_HARNESS_AGENT_SDK_BIN"] !== undefined ? { agentSdkBin: env["WREN_HARNESS_AGENT_SDK_BIN"] } : {}),
|
|
135
|
+
...(env["WREN_HARNESS_OUT"] !== undefined ? { out: env["WREN_HARNESS_OUT"] } : {}),
|
|
136
|
+
...(env["WREN_HARNESS_DEPLOYMENT"] !== undefined ? { deployment: env["WREN_HARNESS_DEPLOYMENT"] } : {}),
|
|
137
|
+
...(tierAdapters !== undefined ? { tierAdapters: tierAdapters.split(",").filter((s) => s.length > 0) } : {}),
|
|
138
|
+
...(env["WREN_HARNESS_MODELS_CONFIG"] !== undefined ? { modelsConfig: env["WREN_HARNESS_MODELS_CONFIG"] } : {}),
|
|
139
|
+
...(chatTimeoutMs !== undefined ? { chatTimeoutMs } : {}),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
async function main() {
|
|
143
|
+
const flags = envFlags();
|
|
144
|
+
const port = Number.parseInt(process.env["PORT"] ?? "4787", 10);
|
|
145
|
+
const workspaceRoot = process.env["WREN_HARNESS_WORKSPACE_ROOT"];
|
|
146
|
+
// Loud-fail rather than ignore: a caller still exporting the removed bound-mode variable
|
|
147
|
+
// believes it is booting against that project, and silently starting unbound would hand
|
|
148
|
+
// them a BFF pointed somewhere else entirely.
|
|
149
|
+
if (process.env["WREN_HARNESS_PROJECT"] !== undefined) {
|
|
150
|
+
process.stderr.write("error: WREN_HARNESS_PROJECT is no longer supported — the BFF has a single boot mode. " +
|
|
151
|
+
"Set WREN_HARNESS_WORKSPACE_ROOT and bring an existing project in through the setup " +
|
|
152
|
+
"wizard's adopt flow, which now survives a restart.\n");
|
|
153
|
+
process.exitCode = 1;
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
if (workspaceRoot === undefined || workspaceRoot.trim().length === 0) {
|
|
157
|
+
process.stderr.write("error: WREN_HARNESS_WORKSPACE_ROOT is required\n");
|
|
158
|
+
process.exitCode = 1;
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const moduleDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
162
|
+
const packageRoot = path.basename(path.dirname(moduleDirectory)) === "dist-server"
|
|
163
|
+
? path.resolve(moduleDirectory, "../..")
|
|
164
|
+
: path.resolve(moduleDirectory, "..");
|
|
165
|
+
// Opt-in same-process SPA serving: only wired when a build actually
|
|
166
|
+
// exists next to this package (`vite build`'s `dist/index.html`). A dev
|
|
167
|
+
// boot (no `dist/`) leaves `staticDir` unset, so `server/app.ts` never
|
|
168
|
+
// mounts the fallback and behavior is unchanged from today (UI served
|
|
169
|
+
// separately by vite's dev proxy). See `TurnDeps.staticDir`'s doc comment
|
|
170
|
+
// (server/turn.ts) and `server/spa.ts`.
|
|
171
|
+
const candidateStaticDir = path.join(packageRoot, "dist");
|
|
172
|
+
const staticDir = existsSync(path.join(candidateStaticDir, "index.html")) ? candidateStaticDir : undefined;
|
|
173
|
+
const profileSource = flags.profile ?? resolveDefaultProfileSource();
|
|
174
|
+
// Frozen at boot: GET /api/harness accepts only a purpose and must never
|
|
175
|
+
// inherit a caller-supplied RouteOptions.profileSource.
|
|
176
|
+
const harnessProfileSources = createHarnessProfileSources(profileSource);
|
|
177
|
+
const loginProbe = createDefaultLoginProbe();
|
|
178
|
+
const authChoice = await resolveAuthChoice(flags, loginProbe);
|
|
179
|
+
const deployment = resolveDeployment(flags);
|
|
180
|
+
const { warnings } = enforceCompliance(authChoice, { deployment });
|
|
181
|
+
for (const warning of warnings)
|
|
182
|
+
process.stderr.write(`warning: ${warning}\n`);
|
|
183
|
+
const tierBinding = flags.tierAdapters !== undefined ? buildTierBindingFromFlags(flags.tierAdapters) : undefined;
|
|
184
|
+
const dbPath = process.env["WREN_BFF_DB_PATH"] ?? "./wren-harness-bff.sqlite";
|
|
185
|
+
// BFF state, never a bound project, owns all non-Setup native artifacts.
|
|
186
|
+
// Initialization creates the fixed private namespaces once at startup.
|
|
187
|
+
const nativeMaterializationState = initializeNativeSessionStateBase(dbPath);
|
|
188
|
+
const store = new Store(dbPath);
|
|
189
|
+
const getCodexModels = () => codexModelsForRuntime(store.getRuntimeSettings());
|
|
190
|
+
const getRuntimeTierNames = () => compileUnboundProfileTierNames({
|
|
191
|
+
profileSource,
|
|
192
|
+
...(flags.warbleBin !== undefined ? { warbleBin: flags.warbleBin } : {}),
|
|
193
|
+
});
|
|
194
|
+
// userProject is always a placeholder here: the BFF boots unbound.
|
|
195
|
+
// RouteOptions.userProject is a required string, but every real reader resolves the LIVE
|
|
196
|
+
// value through TurnDeps.getUserProject() (see server/turn.ts's resolveUserProject /
|
|
197
|
+
// effectiveRouteOptions), which takes precedence over this fixed field once wired below.
|
|
198
|
+
// This field is only ever read as a fallback for TurnDeps literals that don't wire
|
|
199
|
+
// getUserProject at all (e.g. existing tests' plain-object TurnDeps).
|
|
200
|
+
const baseRouteOptions = {
|
|
201
|
+
authChoice,
|
|
202
|
+
profileSource,
|
|
203
|
+
userProject: "",
|
|
204
|
+
deployment,
|
|
205
|
+
...(flags.model !== undefined ? { model: flags.model } : {}),
|
|
206
|
+
...(flags.warbleBin !== undefined ? { warbleBin: flags.warbleBin } : {}),
|
|
207
|
+
...(flags.agentSdkBin !== undefined ? { agentSdkBin: flags.agentSdkBin } : {}),
|
|
208
|
+
...(flags.out !== undefined ? { outDir: flags.out } : {}),
|
|
209
|
+
...(tierBinding !== undefined ? { tierBinding } : {}),
|
|
210
|
+
...(flags.modelsConfig !== undefined ? { modelsConfig: flags.modelsConfig } : {}),
|
|
211
|
+
...(flags.chatTimeoutMs !== undefined ? { chatTimeoutMs: flags.chatTimeoutMs } : {}),
|
|
212
|
+
codexModels: getCodexModels,
|
|
213
|
+
...(process.env["WREN_HARNESS_CODEX_HOME"] !== undefined
|
|
214
|
+
? { codexHome: process.env["WREN_HARNESS_CODEX_HOME"] }
|
|
215
|
+
: {}),
|
|
216
|
+
...(process.env["WREN_HARNESS_CODEX_LOCAL_BIN"] !== undefined
|
|
217
|
+
? { codexLocalBin: process.env["WREN_HARNESS_CODEX_LOCAL_BIN"] }
|
|
218
|
+
: {}),
|
|
219
|
+
...(process.env["WREN_HARNESS_CODEX_BIN"] !== undefined
|
|
220
|
+
? { codexBin: process.env["WREN_HARNESS_CODEX_BIN"] }
|
|
221
|
+
: {}),
|
|
222
|
+
};
|
|
223
|
+
// Mutable project binding: the BFF always boots unbound, recovering a previously bound
|
|
224
|
+
// project from durable state when there is one, and is otherwise bound by the setup
|
|
225
|
+
// wizard's connect/adopt flows calling deps.bindProject(dir) — see server/turn.ts.
|
|
226
|
+
let boundProject = recoverBootstrapProjectBinding(store, workspaceRoot);
|
|
227
|
+
// eslint-disable-next-line prefer-const -- assigned once below, referenced by the closures first
|
|
228
|
+
let deps;
|
|
229
|
+
function getUserProject() {
|
|
230
|
+
return boundProject;
|
|
231
|
+
}
|
|
232
|
+
function bindProject(dir) {
|
|
233
|
+
// Canonicalize before exposing the binding. A different symlink spelling
|
|
234
|
+
// therefore cannot make an old enrichment run appear current, and every
|
|
235
|
+
// call (even to the same directory) advances its generation. Binding is
|
|
236
|
+
// a foundation operation (Setup connect, adopt, the context step's
|
|
237
|
+
// healthcheck bind) that must succeed for a project that has never been
|
|
238
|
+
// built, so it resolves identity only — never a revision, which would
|
|
239
|
+
// require `target/mdl.json` to exist. Enrichment call sites resolve the
|
|
240
|
+
// revision lazily, on demand, and treat an unbuilt project as a
|
|
241
|
+
// legitimate refusal there instead.
|
|
242
|
+
const identity = resolveProjectIdentity(dir);
|
|
243
|
+
boundProject = identity.path;
|
|
244
|
+
const { revokedNativeSessionIds } = store.activateEnrichmentBindingAndRevokeBoundNativeSessions(identity);
|
|
245
|
+
nativeSessions.revokeBindingCapabilities(revokedNativeSessionIds);
|
|
246
|
+
invalidateBundleAgentIdsCache(deps);
|
|
247
|
+
}
|
|
248
|
+
function unbindProject() {
|
|
249
|
+
boundProject = undefined;
|
|
250
|
+
invalidateBundleAgentIdsCache(deps);
|
|
251
|
+
}
|
|
252
|
+
// Discovery shares the same configured binaries and Codex identity as the
|
|
253
|
+
// runtime. Its implementation owns subprocess cleanup and payload
|
|
254
|
+
// sanitization; the app route only sees the public wire contract.
|
|
255
|
+
const listSubscriptionModels = createSubscriptionModelCatalog({
|
|
256
|
+
...(flags.agentSdkBin !== undefined ? { agentSdkBin: flags.agentSdkBin } : {}),
|
|
257
|
+
...(process.env["WREN_HARNESS_CODEX_LOCAL_BIN"] !== undefined
|
|
258
|
+
? { codexLocalBin: process.env["WREN_HARNESS_CODEX_LOCAL_BIN"] }
|
|
259
|
+
: {}),
|
|
260
|
+
...(baseRouteOptions.codexHome !== undefined ? { codexHome: baseRouteOptions.codexHome } : {}),
|
|
261
|
+
...(baseRouteOptions.codexBin !== undefined ? { codexBin: baseRouteOptions.codexBin } : {}),
|
|
262
|
+
getUserProject,
|
|
263
|
+
});
|
|
264
|
+
// Baseline WREN_HOME this process actually booted with (possibly undefined — an operator may
|
|
265
|
+
// legitimately not have the var set at all, in which case the wren CLI's own default of
|
|
266
|
+
// `~/.wren` applies). Captured once, before anything below ever mutates
|
|
267
|
+
// `process.env["WREN_HOME"]`, so `setWrenHomeForSetupMode` can restore exactly this rather than
|
|
268
|
+
// guessing or force-deleting a value the operator's own shell environment set. See
|
|
269
|
+
// `TurnDeps.setWrenHomeForSetupMode`'s doc comment (server/turn.ts) for why
|
|
270
|
+
// this is toggled per setup-mode-action rather than once at boot: the wizard's create/adopt
|
|
271
|
+
// choice is a runtime, resettable decision (POST /api/setup/mode, POST /api/setup/reset), not
|
|
272
|
+
// something fixed at process start, so a single boot-time assignment would apply create's
|
|
273
|
+
// workspace-anchored WREN_HOME to adopt turns too whenever adopt is chosen (or re-chosen after
|
|
274
|
+
// a reset) later in the same boot. The actual logic lives in `./wren-home.js` so it's directly
|
|
275
|
+
// unit-testable without going through a mocked `TurnDeps`.
|
|
276
|
+
const originalWrenHome = process.env["WREN_HOME"];
|
|
277
|
+
const setWrenHomeForSetupMode = createSetWrenHomeForSetupMode(workspaceRoot, originalWrenHome);
|
|
278
|
+
// Mutable auth-choice binding — the auth-choice mirror of boundProject above. Starts at the
|
|
279
|
+
// boot-resolved `authChoice` and can be rebound later by PUT /api/config/runtime
|
|
280
|
+
// (server/app.ts) once a candidate AuthChoice has passed the compliance gate there.
|
|
281
|
+
let boundAuthChoice = store.hasExplicitRuntimeSettings()
|
|
282
|
+
? toAuthChoiceFromRuntimeSettings(store.getRuntimeSettings())
|
|
283
|
+
: authChoice;
|
|
284
|
+
function getAuthChoice() {
|
|
285
|
+
return boundAuthChoice;
|
|
286
|
+
}
|
|
287
|
+
function setAuthChoice(choice) {
|
|
288
|
+
boundAuthChoice = choice;
|
|
289
|
+
}
|
|
290
|
+
const setupIrPath = process.env["WREN_HARNESS_SETUP_IR"] ?? resolveDefaultSetupIrPath();
|
|
291
|
+
const setupMaxTurns = parseSetupMaxTurns(process.env["WREN_HARNESS_SETUP_MAX_TURNS"]);
|
|
292
|
+
// Provider-aware setup selection: Claude subscription dispatches through
|
|
293
|
+
// DispatchedSetupRunner, Codex subscription through CodexSetupRunner, and
|
|
294
|
+
// api-key/local/gateway through
|
|
295
|
+
// the in-process vercel loop (InProcessSetupRunner). `agentSdkBin` still only
|
|
296
|
+
// applies to dispatched — it has no subprocess to pass a binary path to — but
|
|
297
|
+
// `setupMaxTurns` now applies to both: in-process enforces it as an in-process
|
|
298
|
+
// tool-loop step budget (`ExecuteAgentContext.maxSteps`) rather than a
|
|
299
|
+
// subprocess CLI flag, and `effectiveMaxTurns` reports it accordingly (see
|
|
300
|
+
// that class's doc comment in harness/setup/runner.ts).
|
|
301
|
+
//
|
|
302
|
+
// Both runners are constructed unconditionally (guarded only by setupIrPath being
|
|
303
|
+
// resolvable) rather than picking just one at boot, so a live auth-choice rebind (via
|
|
304
|
+
// setAuthChoice above) can dispatch a LATER setup turn through the OTHER runner without
|
|
305
|
+
// a process restart — see setupRunnerFor below and resolveSetupRunner in server/turn.ts.
|
|
306
|
+
let setupRunner;
|
|
307
|
+
let setupRunnerFor;
|
|
308
|
+
if (setupIrPath !== undefined) {
|
|
309
|
+
const dispatchedSetupRunner = new DispatchedSetupRunner({
|
|
310
|
+
irPath: setupIrPath,
|
|
311
|
+
getModelsConfig: () => {
|
|
312
|
+
if (!store.hasExplicitRuntimeSettings())
|
|
313
|
+
return flags.modelsConfig;
|
|
314
|
+
const modelsConfig = materializeRuntimeRouteOptions(store.getRuntimeSettings(), {
|
|
315
|
+
mode: "subscription",
|
|
316
|
+
provider: "claude",
|
|
317
|
+
}).modelsConfig;
|
|
318
|
+
if (!modelsConfig)
|
|
319
|
+
throw new Error("Claude setup requires a generated models config");
|
|
320
|
+
return modelsConfig;
|
|
321
|
+
},
|
|
322
|
+
...(flags.warbleBin !== undefined ? { warbleBin: flags.warbleBin } : {}),
|
|
323
|
+
...(flags.agentSdkBin !== undefined ? { agentSdkBin: flags.agentSdkBin } : {}),
|
|
324
|
+
...(flags.out !== undefined ? { outDir: flags.out } : {}),
|
|
325
|
+
...(setupMaxTurns !== undefined ? { maxTurns: setupMaxTurns } : {}),
|
|
326
|
+
});
|
|
327
|
+
const inProcessSetupRunner = new InProcessSetupRunner({
|
|
328
|
+
irPath: setupIrPath,
|
|
329
|
+
getStrongAdapterSpec: (choice) => {
|
|
330
|
+
if (!store.hasExplicitRuntimeSettings()) {
|
|
331
|
+
if (choice.mode === "subscription")
|
|
332
|
+
throw new Error("in-process setup requires non-subscription auth");
|
|
333
|
+
return deriveAdapterSpec(choice, flags.model !== undefined ? { model: flags.model } : {});
|
|
334
|
+
}
|
|
335
|
+
const strong = materializeRuntimeRouteOptions(store.getRuntimeSettings(), choice).tierBinding?.["strong"];
|
|
336
|
+
if (!strong)
|
|
337
|
+
throw new Error("in-process setup requires a configured strong-tier adapter");
|
|
338
|
+
return strong;
|
|
339
|
+
},
|
|
340
|
+
...(flags.warbleBin !== undefined ? { warbleBin: flags.warbleBin } : {}),
|
|
341
|
+
...(flags.out !== undefined ? { outDir: flags.out } : {}),
|
|
342
|
+
...(flags.model !== undefined ? { model: flags.model } : {}),
|
|
343
|
+
...(setupMaxTurns !== undefined ? { maxTurns: setupMaxTurns } : {}),
|
|
344
|
+
});
|
|
345
|
+
const codexSetupRunner = new CodexSetupRunner({
|
|
346
|
+
irPath: setupIrPath,
|
|
347
|
+
getStrongModel: () => store.hasExplicitRuntimeSettings()
|
|
348
|
+
? effectiveTierModel(store.getRuntimeSettings().tierModels.find((binding) => binding.tier === "strong") ?? { tier: "strong" }, store.getRuntimeSettings()) ?? ""
|
|
349
|
+
: flags.model ?? "",
|
|
350
|
+
...(process.env["WREN_HARNESS_CODEX_LOCAL_BIN"] !== undefined
|
|
351
|
+
? { codexLocalBin: process.env["WREN_HARNESS_CODEX_LOCAL_BIN"] }
|
|
352
|
+
: {}),
|
|
353
|
+
...(process.env["WREN_HARNESS_CODEX_BIN"] !== undefined
|
|
354
|
+
? { codexBin: process.env["WREN_HARNESS_CODEX_BIN"] }
|
|
355
|
+
: {}),
|
|
356
|
+
});
|
|
357
|
+
const runnerSet = {
|
|
358
|
+
claudeSubscription: dispatchedSetupRunner,
|
|
359
|
+
codexSubscription: codexSetupRunner,
|
|
360
|
+
nonSubscription: inProcessSetupRunner,
|
|
361
|
+
};
|
|
362
|
+
const runnerFor = (choice) => selectSetupRunnerForAuth(choice, runnerSet);
|
|
363
|
+
setupRunner = runnerFor(authChoice);
|
|
364
|
+
setupRunnerFor = runnerFor;
|
|
365
|
+
}
|
|
366
|
+
else {
|
|
367
|
+
process.stderr.write("warning: no genbi-setup IR found (set WREN_HARNESS_SETUP_IR or restore this package's " +
|
|
368
|
+
"profiles/genbi-setup/ir.golden.json) — POST /api/setup/connect will 500 until one is " +
|
|
369
|
+
"configured\n");
|
|
370
|
+
}
|
|
371
|
+
// Post-bind enrichment draft callback. Guarded only by enrichIrPath being resolvable, mirroring
|
|
372
|
+
// setupRunner above — the runner itself reads the LIVE auth choice per-call (getAuthChoice)
|
|
373
|
+
// rather than freezing the boot-time one, and refuses (EnrichmentContractError, surfaced as a
|
|
374
|
+
// 500 by the route) any call made while the live choice isn't Claude subscription. Apply,
|
|
375
|
+
// reconcile and approval stay unwired: deps.enrichmentApplyRunner / .enrichmentApprovalProvider
|
|
376
|
+
// are intentionally left unset, so GET /api/context/enrichment keeps reporting those three
|
|
377
|
+
// "callback_unavailable" and POST /api/context/enrichment/.../apply keeps 503ing, exactly as
|
|
378
|
+
// before this change — only the draft capability goes live.
|
|
379
|
+
const enrichIrPath = process.env["WREN_HARNESS_ENRICH_IR"] ?? resolveDefaultEnrichIrPath();
|
|
380
|
+
// The profile's committed IR beside profile.yml — its eval golden, carrying
|
|
381
|
+
// the example project's schema. It is a producer-contract and readiness
|
|
382
|
+
// input only: what a session actually dispatches is compiled per binding by
|
|
383
|
+
// `resolveDispatchIr` below.
|
|
384
|
+
const analysisIrCandidate = process.env["WREN_HARNESS_ANALYSIS_IR"] ?? path.join(profileSource, "ir.golden.json");
|
|
385
|
+
const analysisIrPath = existsSync(analysisIrCandidate) ? analysisIrCandidate : undefined;
|
|
386
|
+
let enrichmentRunner;
|
|
387
|
+
if (enrichIrPath !== undefined) {
|
|
388
|
+
enrichmentRunner = createDispatchedEnrichmentDraftRunner({
|
|
389
|
+
irPath: enrichIrPath,
|
|
390
|
+
// Compile against the bound project rather than dispatching the prebuilt
|
|
391
|
+
// IR, which is the profile's eval golden and carries this repo's example
|
|
392
|
+
// project's schema.
|
|
393
|
+
profileSource: harnessProfileSources.context_enrichment,
|
|
394
|
+
getAuthChoice,
|
|
395
|
+
// Mirrors DispatchedSetupRunner's getModelsConfig above: enrichment draft dispatch is Claude
|
|
396
|
+
// subscription only, so this always materializes the Claude models config regardless of
|
|
397
|
+
// which auth choice is currently live — getAuthChoice's own check rejects the call before
|
|
398
|
+
// dispatch if the live choice isn't actually Claude subscription.
|
|
399
|
+
getModelsConfig: () => {
|
|
400
|
+
if (!store.hasExplicitRuntimeSettings())
|
|
401
|
+
return flags.modelsConfig;
|
|
402
|
+
return materializeRuntimeRouteOptions(store.getRuntimeSettings(), { mode: "subscription", provider: "claude" }).modelsConfig;
|
|
403
|
+
},
|
|
404
|
+
...(flags.warbleBin !== undefined ? { warbleBin: flags.warbleBin } : {}),
|
|
405
|
+
...(flags.agentSdkBin !== undefined ? { agentSdkBin: flags.agentSdkBin } : {}),
|
|
406
|
+
...(flags.out !== undefined ? { outDir: flags.out } : {}),
|
|
407
|
+
...(flags.chatTimeoutMs !== undefined ? { chatTimeoutMs: flags.chatTimeoutMs } : {}),
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
else {
|
|
411
|
+
process.stderr.write("warning: no genbi-enrich-context IR found (set WREN_HARNESS_ENRICH_IR or restore this " +
|
|
412
|
+
"package's profiles/genbi-enrich-context/ir.golden.json) — GET /api/context/enrichment " +
|
|
413
|
+
"will report draft unavailable until one is configured\n");
|
|
414
|
+
}
|
|
415
|
+
// Lazy-load the native add-on: a BFF serving ordinary Ask/Context must not
|
|
416
|
+
// fail at boot merely because the optional local terminal host is absent.
|
|
417
|
+
let interactiveTerminal;
|
|
418
|
+
const currentInteractiveBinding = () => {
|
|
419
|
+
const project = getUserProject();
|
|
420
|
+
const stored = store.getEnrichmentBinding();
|
|
421
|
+
if (!project || !stored)
|
|
422
|
+
return undefined;
|
|
423
|
+
try {
|
|
424
|
+
const resolved = resolveEnrichmentBinding(project);
|
|
425
|
+
return resolved.path === stored.path && resolved.identity === stored.identity
|
|
426
|
+
? { ...resolved, generation: stored.generation }
|
|
427
|
+
: undefined;
|
|
428
|
+
}
|
|
429
|
+
catch {
|
|
430
|
+
return undefined;
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
let ptyFactory;
|
|
434
|
+
async function loadPty() {
|
|
435
|
+
ptyFactory ??= (async () => {
|
|
436
|
+
try {
|
|
437
|
+
const require = createRequire(import.meta.url);
|
|
438
|
+
ensureDarwinNodePtySpawnHelper(require.resolve("node-pty"));
|
|
439
|
+
return await createProbedPtyFactory(await import("node-pty"));
|
|
440
|
+
}
|
|
441
|
+
catch {
|
|
442
|
+
throw new InteractiveLaunchError("interactive terminal host cannot spawn local processes on this machine");
|
|
443
|
+
}
|
|
444
|
+
})();
|
|
445
|
+
return ptyFactory;
|
|
446
|
+
}
|
|
447
|
+
async function terminalManager() {
|
|
448
|
+
if (interactiveTerminal)
|
|
449
|
+
return interactiveTerminal;
|
|
450
|
+
interactiveTerminal = new InteractiveTerminalManager(await loadPty());
|
|
451
|
+
return interactiveTerminal;
|
|
452
|
+
}
|
|
453
|
+
async function prepareInteractiveTerminal(target, binding) {
|
|
454
|
+
if (enrichIrPath === undefined)
|
|
455
|
+
throw new InteractiveLaunchError("interactive enrichment artifacts are not configured");
|
|
456
|
+
const artifactRoot = legacyInteractiveWorkspace(nativeMaterializationState, binding.path, target);
|
|
457
|
+
return prepareInteractiveHandoff({
|
|
458
|
+
target,
|
|
459
|
+
binding,
|
|
460
|
+
artifactRoot,
|
|
461
|
+
materializationState: nativeMaterializationState,
|
|
462
|
+
materialize: () => dispatchInteractiveArtifacts({ warbleBin: flags.warbleBin ?? "warble", irPath: enrichIrPath, target, cwd: artifactRoot, boundProject: binding.path, materializationState: nativeMaterializationState }),
|
|
463
|
+
}, { getCurrentBinding: currentInteractiveBinding });
|
|
464
|
+
}
|
|
465
|
+
const nativeMcpUrl = `http://127.0.0.1:${port}/api/native-sessions/mcp`;
|
|
466
|
+
const nativeArtifacts = new NativeArtifactService({
|
|
467
|
+
store,
|
|
468
|
+
artifactsRoot: resolveArtifactsDir(flags.out),
|
|
469
|
+
expectedMcpUrl: nativeMcpUrl,
|
|
470
|
+
mcpUrl: process.env["WREN_HARNESS_NATIVE_MCP_URL"] ?? nativeMcpUrl,
|
|
471
|
+
getBinding: currentInteractiveBinding,
|
|
472
|
+
});
|
|
473
|
+
const nativeSessions = new NativeSessionService({
|
|
474
|
+
store,
|
|
475
|
+
terminalManager,
|
|
476
|
+
getBinding: currentInteractiveBinding,
|
|
477
|
+
workspaceRoot,
|
|
478
|
+
materializationState: nativeMaterializationState,
|
|
479
|
+
irPaths: { analysis: analysisIrPath, setup: setupIrPath, context_enrichment: enrichIrPath },
|
|
480
|
+
// A session dispatches an IR compiled for the project it is bound to. The
|
|
481
|
+
// paths above are the profiles' eval goldens — this profile compiled against
|
|
482
|
+
// warble's own example project, that project's schema baked into
|
|
483
|
+
// `context_binding.resolved` — which is the right artifact for the producer
|
|
484
|
+
// contract probe and the wrong one for a session that has a binding.
|
|
485
|
+
//
|
|
486
|
+
// Setup has no binding by definition, so it compiles the profile's authored
|
|
487
|
+
// context with no user project composed in; its golden resolves nothing
|
|
488
|
+
// either, which is why Setup never showed this symptom.
|
|
489
|
+
resolveDispatchIr: async (purpose, binding) => {
|
|
490
|
+
const profileSource = harnessProfileSources[purpose];
|
|
491
|
+
const compiled = purpose === "setup" || binding === undefined
|
|
492
|
+
? await compileRawProfile({ profileSource, mode: "native", warbleBin: flags.warbleBin ?? "warble" })
|
|
493
|
+
: await compileProfile({ profileSource, userProject: binding.path, mode: "native", warbleBin: flags.warbleBin ?? "warble" });
|
|
494
|
+
return compiled.irPath;
|
|
495
|
+
},
|
|
496
|
+
warbleBin: flags.warbleBin ?? "warble",
|
|
497
|
+
...(process.env["WREN_HARNESS_WREN_SHIM"] !== undefined ? { wrenShim: process.env["WREN_HARNESS_WREN_SHIM"] } : {}),
|
|
498
|
+
terminalHostAvailable: async () => {
|
|
499
|
+
try {
|
|
500
|
+
await loadPty();
|
|
501
|
+
return true;
|
|
502
|
+
}
|
|
503
|
+
catch {
|
|
504
|
+
return false;
|
|
505
|
+
}
|
|
506
|
+
},
|
|
507
|
+
artifactService: nativeArtifacts,
|
|
508
|
+
});
|
|
509
|
+
deps = {
|
|
510
|
+
store,
|
|
511
|
+
nativeSessions,
|
|
512
|
+
nativeArtifacts,
|
|
513
|
+
route,
|
|
514
|
+
baseRouteOptions,
|
|
515
|
+
describeBundle: (options) => {
|
|
516
|
+
return describeBundle({
|
|
517
|
+
...options,
|
|
518
|
+
codexModels: typeof options.codexModels === "function" ? options.codexModels() : options.codexModels ?? getCodexModels(),
|
|
519
|
+
...(process.env["WREN_HARNESS_CODEX_LOCAL_BIN"] !== undefined
|
|
520
|
+
? { codexLocalBin: process.env["WREN_HARNESS_CODEX_LOCAL_BIN"] }
|
|
521
|
+
: {}),
|
|
522
|
+
});
|
|
523
|
+
},
|
|
524
|
+
describeHarnessBundle: (purpose, options) => {
|
|
525
|
+
const profileSource = harnessProfileSources[purpose];
|
|
526
|
+
const common = {
|
|
527
|
+
...options,
|
|
528
|
+
profileSource,
|
|
529
|
+
codexManifestPurpose: purpose,
|
|
530
|
+
codexModels: typeof options.codexModels === "function" ? options.codexModels() : options.codexModels ?? getCodexModels(),
|
|
531
|
+
...(process.env["WREN_HARNESS_CODEX_LOCAL_BIN"] !== undefined
|
|
532
|
+
? { codexLocalBin: process.env["WREN_HARNESS_CODEX_LOCAL_BIN"] }
|
|
533
|
+
: {}),
|
|
534
|
+
};
|
|
535
|
+
return purpose === "setup"
|
|
536
|
+
? describeBundle({ ...common, context: "bootstrap" })
|
|
537
|
+
: describeBundle({ ...common, context: "bound_project", userProject: options.userProject });
|
|
538
|
+
},
|
|
539
|
+
getRuntimeTierNames,
|
|
540
|
+
getUserProject,
|
|
541
|
+
bindProject,
|
|
542
|
+
unbindProject,
|
|
543
|
+
getAuthChoice,
|
|
544
|
+
setAuthChoice,
|
|
545
|
+
loginProbe,
|
|
546
|
+
listSubscriptionModels,
|
|
547
|
+
...(setupRunner !== undefined ? { setupRunner } : {}),
|
|
548
|
+
...(setupRunnerFor !== undefined ? { setupRunnerFor } : {}),
|
|
549
|
+
...(enrichmentRunner !== undefined ? { enrichmentRunner } : {}),
|
|
550
|
+
startInteractiveTerminal: async ({ target, binding }) => {
|
|
551
|
+
const { spec } = await prepareInteractiveTerminal(target, binding);
|
|
552
|
+
const manager = await terminalManager();
|
|
553
|
+
validateLegacyInteractiveWorkspace(nativeMaterializationState, binding.path, spec.cwd);
|
|
554
|
+
return manager.start(spec, undefined, { ...process.env, WREN_PROJECT_HOME: binding.path });
|
|
555
|
+
},
|
|
556
|
+
prepareInteractiveTerminal: async ({ target, binding }) => {
|
|
557
|
+
return (await prepareInteractiveTerminal(target, binding)).handoff;
|
|
558
|
+
},
|
|
559
|
+
getInteractiveTerminal: (id) => interactiveTerminal?.get(id),
|
|
560
|
+
revokeInteractiveTerminals: () => interactiveTerminal?.closeAll(),
|
|
561
|
+
interactiveTerminalReadiness: async () => {
|
|
562
|
+
if (enrichIrPath === undefined)
|
|
563
|
+
return unavailableInteractiveReadiness("interactive enrichment artifacts are not configured");
|
|
564
|
+
return getInteractiveTerminalReadiness({
|
|
565
|
+
getCurrentBinding: currentInteractiveBinding,
|
|
566
|
+
materializationState: nativeMaterializationState,
|
|
567
|
+
terminalHostAvailable: async () => {
|
|
568
|
+
try {
|
|
569
|
+
await loadPty();
|
|
570
|
+
return true;
|
|
571
|
+
}
|
|
572
|
+
catch {
|
|
573
|
+
return false;
|
|
574
|
+
}
|
|
575
|
+
},
|
|
576
|
+
});
|
|
577
|
+
},
|
|
578
|
+
workspaceRoot,
|
|
579
|
+
setWrenHomeForSetupMode,
|
|
580
|
+
...(staticDir !== undefined ? { staticDir } : {}),
|
|
581
|
+
};
|
|
582
|
+
const app = createApp(deps);
|
|
583
|
+
const websocket = new WebSocketServer({ noServer: true });
|
|
584
|
+
// ws' `noServer` runtime shape is the one documented by @hono/node-server;
|
|
585
|
+
// its package types disagree under exactOptionalPropertyTypes.
|
|
586
|
+
const server = serve({ fetch: app.fetch, websocket: { server: websocket }, hostname: "127.0.0.1", port }, (info) => {
|
|
587
|
+
process.stdout.write(`wren-harness BFF listening on http://127.0.0.1:${info.port} (db: ${dbPath})\n`);
|
|
588
|
+
});
|
|
589
|
+
// Without this, a port that is already taken surfaces as an unhandled 'error' event and a raw
|
|
590
|
+
// Node stack trace -- the first thing a new user is likely to hit, and the least readable.
|
|
591
|
+
server.on("error", (error) => {
|
|
592
|
+
process.stderr.write(error.code === "EADDRINUSE"
|
|
593
|
+
? `error: port ${port} is already in use — stop whatever is using it, or set PORT to a free one\n`
|
|
594
|
+
: `error: could not start the server on port ${port}: ${error.message}\n`);
|
|
595
|
+
process.exit(1);
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
main().catch((error) => {
|
|
599
|
+
process.stderr.write(`fatal: ${error instanceof Error ? error.stack ?? error.message : String(error)}\n`);
|
|
600
|
+
process.exitCode = 1;
|
|
601
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Store } from "./db.js";
|
|
2
|
+
/**
|
|
3
|
+
* Recover the in-memory project binding after a BFF restart.
|
|
4
|
+
*
|
|
5
|
+
* `bindProject` already persists an identity-fenced enrichment binding when
|
|
6
|
+
* Setup accepts a project. The process-local `boundProject` pointer used by the
|
|
7
|
+
* remaining Setup routes must be reconstructed from that durable proof, not
|
|
8
|
+
* from the setup form alone: the form is user input and a stale or replaced
|
|
9
|
+
* directory must remain unbound.
|
|
10
|
+
*
|
|
11
|
+
* The wizard's two entry paths leave different durable evidence, so recovery
|
|
12
|
+
* branches on the persisted `setup.mode` rather than applying one set of
|
|
13
|
+
* checks to both:
|
|
14
|
+
*
|
|
15
|
+
* - `"create"` scaffolds INTO the workspace root, and its connect turn writes
|
|
16
|
+
* the `.wren-validated` sentinel only after `wren profile add` genuinely
|
|
17
|
+
* validates the connection. Both facts are load-bearing evidence there, so
|
|
18
|
+
* that path keeps the workspace fence and the sentinel check.
|
|
19
|
+
* - `"adopt"` takes a project the user already has, anywhere on disk. Neither
|
|
20
|
+
* piece of evidence exists for it — containment is false by construction and
|
|
21
|
+
* the sentinel belongs to a flow that never ran. What stands in for them is
|
|
22
|
+
* that `bindProject` is reached only after adopt verification succeeds
|
|
23
|
+
* (`POST /api/setup/adopt`), so a *persisted binding at all* is that flow's
|
|
24
|
+
* equivalent proof.
|
|
25
|
+
*
|
|
26
|
+
* Identity equality is required on both paths and is what actually fences the
|
|
27
|
+
* adopt case: `resolveProjectIdentity` resolves device+inode, so a symlink
|
|
28
|
+
* repointed at another directory — or a different directory moved into the
|
|
29
|
+
* recorded path — no longer matches and stays unbound.
|
|
30
|
+
*/
|
|
31
|
+
export declare function recoverBootstrapProjectBinding(store: Store, workspaceRoot: string): string | undefined;
|