@vantran-se/openchamber-web 1.24.3 → 2.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/README.md +2 -8
- package/bin/lib/commands-connect-url.js +3 -8
- package/bin/lib/commands-tunnel.js +3 -0
- package/dist/assets/{CodeMirrorCommentWidgets-BjJHrzmU.js → CodeMirrorCommentWidgets-ntXIbhV2.js} +1 -1
- package/dist/assets/DiffView-CjxUovbM.js +4 -0
- package/dist/assets/FilesView-DFWD0RQR.js +17 -0
- package/dist/assets/{GitView-CDOJy5Ay.js → GitView-BWa7e5vw.js} +4 -4
- package/dist/assets/{GuestAttachDialog-D3S0Rjic.js → GuestAttachDialog-DdOuyZzp.js} +1 -1
- package/dist/assets/GuestSurfacePane-DDfzid3U.js +1 -0
- package/dist/assets/{JsonTreeView-D0C9nU7K.js → JsonTreeView-3sGwqexh.js} +1 -1
- package/dist/assets/LinearIssuesView-C6dpIdjw.js +1 -0
- package/dist/assets/MarkdownImageGallery-Q7YR_tl4.js +1 -0
- package/dist/assets/MarkdownRendererImpl-DxDV56bL.js +12 -0
- package/dist/assets/OnboardingScreen-rK4Stg__.js +1 -0
- package/dist/assets/{PatchDiff-DnEhjXcC.js → PatchDiff-DS-IMU1j.js} +1 -1
- package/dist/assets/{PierreDiffViewer-DyxZVVVR.js → PierreDiffViewer-DW23KWKZ.js} +1 -1
- package/dist/assets/PlanView-BCk_VobP.js +5 -0
- package/dist/assets/PluginPane-BPP1dbck.js +3 -0
- package/dist/assets/SettingsView-kMmrpn43.js +29 -0
- package/dist/assets/SettingsWindow-BpvD6IYQ.js +1 -0
- package/dist/assets/SubmoduleDiffSummary-BLttSS1X.js +1 -0
- package/dist/assets/ToolOutputDialog-CtqDqbyr.js +15 -0
- package/dist/assets/ToolPartDiffPreview-5FcKdyRy.js +8 -0
- package/dist/assets/{VirtualizedFileDiff-0pyr_mHY.js → VirtualizedFileDiff-C763hNCR.js} +4 -4
- package/dist/assets/{WalkthroughView-yNj4nOP5.js → WalkthroughView-lap5btCF.js} +2 -2
- package/dist/assets/appThemeRegistry-u4zw1l0B.js +1 -0
- package/dist/assets/{commitAndPush-DvJKa3Ks.js → commitAndPush-CwGJfJG-.js} +1 -1
- package/dist/assets/{computeVirtualFileMetrics-DxTBMuu-.js → computeVirtualFileMetrics-DW5jjX4n.js} +1 -1
- package/dist/assets/de-CndEg67T.js +10 -0
- package/dist/assets/es-DglvPlaC.js +16 -0
- package/dist/assets/fr-DejPCpbS.js +14 -0
- package/dist/assets/index-C2r64QfD.js +1 -0
- package/dist/assets/index-CLcM8HKx.css +1 -0
- package/dist/assets/index-CvLgnuNI.js +41 -0
- package/dist/assets/{isDiffPlainText-D7FkBMP6.js → isDiffPlainText-D47a6hMP.js} +1 -1
- package/dist/assets/{isFilePlainText-CL2vsrIZ.js → isFilePlainText-B-mGgeKl.js} +1 -1
- package/dist/assets/ja-CVpQawCd.js +16 -0
- package/dist/assets/ko-Bw446h9j.js +16 -0
- package/dist/assets/main-Bz4WWXbr.js +2 -0
- package/dist/assets/main-oreqDsOV.js +896 -0
- package/dist/assets/{markdownCore-Dh7WEsdM.js → markdownCore-D2e5sp9y.js} +4 -4
- package/dist/assets/miniChat-D1v2gsBI.js +2 -0
- package/dist/assets/mobile-DBj3lCf2.js +2 -0
- package/dist/assets/{patchFileDiff-Bsba8KFp.js → patchFileDiff-BpeUOV91.js} +3 -3
- package/dist/assets/pl-BYNlPdhm.js +16 -0
- package/dist/assets/pt-BR-CL-Yfv9H.js +16 -0
- package/dist/assets/renderElectronMiniChatApp-DVtfEB2z.js +2 -0
- package/dist/assets/renderMobileApp-DFp9-xbG.js +25 -0
- package/dist/assets/runtimeEndpointReset-CsIpUep1.js +27 -0
- package/dist/assets/scheduledTasksApi-B5OHVpPM.js +1 -0
- package/dist/assets/{shikiHighlight-8_3csmr6.js → shikiHighlight-DNm2Z_Jc.js} +1 -1
- package/dist/assets/{textMateThemeFromAppTheme-CHo2fCgW.js → textMateThemeFromAppTheme-DGF2kG7g.js} +1 -1
- package/dist/assets/tr-DzmbxLy-.js +7 -0
- package/dist/assets/uk-C7vQnwro.js +16 -0
- package/dist/assets/useAppFontEffects-Ci3FEaPf.js +7337 -0
- package/dist/assets/{useInlineCommentController-DkQ_gqpn.js → useInlineCommentController-CuUuakrt.js} +2 -2
- package/dist/assets/useRootScrollLock-CQIsyERq.js +2 -0
- package/dist/assets/{vendor-codemirror-language-data-CdzUmUyZ.js → vendor-codemirror-language-data-CQa7_1cr.js} +2 -2
- package/dist/assets/vendor-cron-parser-CjvYVigk.js +2 -0
- package/dist/assets/vendor-opencode-client-_BAHQo4R.js +12 -0
- package/dist/assets/{vendor-remixicon-react-BDmGmFCA.js → vendor-remixicon-react-D6dZ0UwF.js} +1 -1
- package/dist/assets/{vendor-zod-CgAdXWZO.js → vendor-zod-Gea9uBlY.js} +1 -1
- package/dist/assets/{web-components-B8IcVQAA.js → web-components-B6tKQyJN.js} +2 -2
- package/dist/assets/zh-CN-Cl0srS-z.js +16 -0
- package/dist/assets/zh-TW-DtII5bDb.js +16 -0
- package/dist/index.html +13 -7
- package/dist/mini-chat.html +5 -5
- package/dist/mobile.html +5 -5
- package/package.json +6 -5
- package/server/index.d.ts +1 -0
- package/server/index.js +246 -91
- package/server/lib/agent-tool/DOCUMENTATION.md +32 -15
- package/server/lib/agent-tool/runtime.js +135 -54
- package/server/lib/agent-tool/runtime.test.js +155 -87
- package/server/lib/browser-control/DOCUMENTATION.md +37 -2
- package/server/lib/browser-control/provider.js +217 -0
- package/server/lib/browser-control/provider.test.js +251 -0
- package/server/lib/context-obligatory/runtime.js +61 -50
- package/server/lib/context-obligatory/runtime.test.js +62 -162
- package/server/lib/event-stream/DOCUMENTATION.md +12 -1
- package/server/lib/event-stream/delta-coalescer.js +0 -0
- package/server/lib/event-stream/delta-coalescer.test.js +68 -57
- package/server/lib/event-stream/directory-ws-bridge.js +1 -1
- package/server/lib/event-stream/global-hub.js +10 -1
- package/server/lib/event-stream/global-hub.test.js +27 -26
- package/server/lib/event-stream/index.js +6 -0
- package/server/lib/event-stream/protocol.js +26 -19
- package/server/lib/event-stream/protocol.test.js +18 -7
- package/server/lib/event-stream/rebind.test.js +5 -5
- package/server/lib/event-stream/runtime.test.js +17 -15
- package/server/lib/event-stream/translate-v2.js +313 -0
- package/server/lib/event-stream/translate-v2.test.js +209 -0
- package/server/lib/event-stream/upstream-reader.test.js +16 -16
- package/server/lib/fs/DOCUMENTATION.md +4 -2
- package/server/lib/fs/byte-range.js +35 -0
- package/server/lib/fs/byte-range.test.js +31 -0
- package/server/lib/fs/routes.js +47 -42
- package/server/lib/fs/routes.test.js +123 -18
- package/server/lib/git/service.js +12 -0
- package/server/lib/github/DOCUMENTATION.md +1 -1
- package/server/lib/github/pr-status.js +22 -2
- package/server/lib/github/pr-status.test.js +27 -14
- package/server/lib/guests/DOCUMENTATION.md +13 -0
- package/server/lib/guests/catalog.js +12 -0
- package/server/lib/guests/catalog.test.js +33 -0
- package/server/lib/guests/routes.js +10 -1
- package/server/lib/guests/service.js +192 -14
- package/server/lib/guests/service.test.js +201 -1
- package/server/lib/guests/surface.js +630 -0
- package/server/lib/guests/surface.test.js +486 -0
- package/server/lib/markdown-image-grants/routes.js +18 -5
- package/server/lib/markdown-image-grants/routes.test.js +9 -6
- package/server/lib/message-queue/DOCUMENTATION.md +33 -22
- package/server/lib/message-queue/runtime.js +155 -100
- package/server/lib/message-queue/runtime.test.js +168 -82
- package/server/lib/notifications/DOCUMENTATION.md +28 -0
- package/server/lib/notifications/control-stream.http.test.js +68 -0
- package/server/lib/notifications/emitter-runtime.js +15 -0
- package/server/lib/notifications/emitter-runtime.test.js +32 -0
- package/server/lib/notifications/push-runtime.js +5 -1
- package/server/lib/notifications/push-runtime.test.js +2 -2
- package/server/lib/notifications/routes.js +1 -1
- package/server/lib/notifications/runtime.js +115 -26
- package/server/lib/notifications/template-runtime.js +13 -15
- package/server/lib/notifications/template-runtime.test.js +16 -9
- package/server/lib/notifications/trigger-runtime.test.js +187 -0
- package/server/lib/openchamber-control/DOCUMENTATION.md +10 -0
- package/server/lib/openchamber-control/actions.js +3 -2
- package/server/lib/openchamber-control/error.js +1 -1
- package/server/lib/openchamber-control/file-open.js +59 -0
- package/server/lib/openchamber-control/file-open.test.js +57 -0
- package/server/lib/openchamber-control/routes.test.js +12 -0
- package/server/lib/openchamber-control/service.js +102 -49
- package/server/lib/openchamber-control/service.test.js +84 -44
- package/server/lib/openchamber-sessions/archive-store.js +224 -0
- package/server/lib/openchamber-sessions/archive-store.test.js +187 -0
- package/server/lib/openchamber-sessions/opencode-client.js +22 -0
- package/server/lib/openchamber-sessions/routes.js +398 -328
- package/server/lib/openchamber-sessions/routes.test.js +777 -740
- package/server/lib/openchamber-sessions/session-metadata-store.js +285 -0
- package/server/lib/openchamber-sessions/session-metadata-store.test.js +259 -0
- package/server/lib/opencode/DOCUMENTATION.md +449 -29
- package/server/lib/opencode/agents.js +233 -327
- package/server/lib/opencode/auth.js +41 -44
- package/server/lib/opencode/auth.test.js +92 -0
- package/server/lib/opencode/cli-upgrade.d.ts +4 -0
- package/server/lib/opencode/cli-upgrade.js +18 -0
- package/server/lib/opencode/cli-upgrade.test.js +36 -0
- package/server/lib/opencode/commands.js +164 -145
- package/server/lib/opencode/compatibility.d.ts +16 -0
- package/server/lib/opencode/compatibility.js +88 -0
- package/server/lib/opencode/compatibility.test.js +62 -0
- package/server/lib/opencode/config-entity-routes.js +58 -29
- package/server/lib/opencode/config-entity-v2.test.js +290 -0
- package/server/lib/opencode/config-mutation-response.js +17 -7
- package/server/lib/opencode/config-mutation-response.test.js +4 -7
- package/server/lib/opencode/config-paths.test.js +2 -2
- package/server/lib/opencode/config-v2.d.ts +183 -0
- package/server/lib/opencode/config-v2.js +801 -0
- package/server/lib/opencode/config-v2.test.js +466 -0
- package/server/lib/opencode/credential-db.js +144 -0
- package/server/lib/opencode/credential-db.test.js +104 -0
- package/server/lib/opencode/env-runtime.js +48 -9
- package/server/lib/opencode/env-runtime.test.js +45 -0
- package/server/lib/opencode/feature-routes-runtime.js +15 -5
- package/server/lib/opencode/lifecycle-process.test.js +7 -4
- package/server/lib/opencode/lifecycle.js +85 -30
- package/server/lib/opencode/lifecycle.test.js +80 -8
- package/server/lib/opencode/login-shell-env.js +27 -0
- package/server/lib/opencode/managed-config-file.js +119 -0
- package/server/lib/opencode/managed-config-file.test.js +152 -0
- package/server/lib/opencode/managed-plugin-config.js +33 -13
- package/server/lib/opencode/mcp.js +43 -149
- package/server/lib/opencode/network-runtime.js +6 -7
- package/server/lib/opencode/openchamber-routes.test.js +2 -5
- package/server/lib/opencode/plugin-routes.js +3 -3
- package/server/lib/opencode/plugin-routes.test.js +6 -24
- package/server/lib/opencode/plugins.js +91 -40
- package/server/lib/opencode/plugins.test.js +28 -15
- package/server/lib/opencode/providers.js +63 -83
- package/server/lib/opencode/providers.test.js +128 -59
- package/server/lib/opencode/proxy-agent-wiring.test.js +4 -2
- package/server/lib/opencode/proxy.js +179 -129
- package/server/lib/opencode/pwa-manifest-routes.js +4 -2
- package/server/lib/opencode/pwa-manifest-routes.test.js +3 -2
- package/server/lib/opencode/response-envelope.js +14 -0
- package/server/lib/opencode/response-envelope.test.js +23 -0
- package/server/lib/opencode/routes-upgrade.test.js +73 -116
- package/server/lib/opencode/routes.js +121 -428
- package/server/lib/opencode/server-utils-runtime.js +10 -4
- package/server/lib/opencode/session-activity.js +78 -0
- package/server/lib/opencode/session-runtime.js +21 -10
- package/server/lib/opencode/session-runtime.test.js +9 -9
- package/server/lib/opencode/settings-helpers.js +9 -9
- package/server/lib/opencode/settings-helpers.test.js +7 -12
- package/server/lib/opencode/settings-registry.json +6 -8
- package/server/lib/opencode/settings-runtime.js +18 -1
- package/server/lib/opencode/settings-runtime.test.js +31 -2
- package/server/lib/opencode/shared.js +54 -22
- package/server/lib/opencode/shared.test.js +8 -4
- package/server/lib/opencode/shutdown-runtime.js +54 -7
- package/server/lib/opencode/shutdown-runtime.test.js +224 -30
- package/server/lib/opencode/skill-routes.js +23 -24
- package/server/lib/opencode/skills.js +7 -5
- package/server/lib/opencode/tunnel-wiring-runtime.js +2 -1
- package/server/lib/opencode/upgrade-capability.test.js +1 -1
- package/server/lib/opencode/v1-migration-topup.js +288 -0
- package/server/lib/opencode/v1-migration-topup.test.js +266 -0
- package/server/lib/opencode/v2-install.d.ts +8 -0
- package/server/lib/opencode/v2-install.js +158 -0
- package/server/lib/opencode/v2-install.test.js +124 -0
- package/server/lib/opencode/watcher.js +10 -3
- package/server/lib/opencode/watcher.test.js +17 -21
- package/server/lib/opencode/websearch-config.js +66 -0
- package/server/lib/package-manager.js +55 -68
- package/server/lib/package-manager.test.js +241 -75
- package/server/lib/permission-auto-accept/runtime.js +38 -22
- package/server/lib/permission-auto-accept/runtime.test.js +39 -17
- package/server/lib/quota/DOCUMENTATION.md +4 -0
- package/server/lib/quota/providers/xai.js +4 -4
- package/server/lib/relay/tunnel-host.js +4 -1
- package/server/lib/relay/tunnel-host.test.js +7 -0
- package/server/lib/routing/DOCUMENTATION.md +52 -21
- package/server/lib/routing/defaults.js +23 -6
- package/server/lib/routing/jev.js +17 -7
- package/server/lib/routing/routes.http.test.js +68 -33
- package/server/lib/routing/routes.js +65 -22
- package/server/lib/routing/runtime.js +145 -48
- package/server/lib/routing/runtime.test.js +79 -55
- package/server/lib/scheduled-tasks/DOCUMENTATION.md +4 -0
- package/server/lib/scheduled-tasks/issue-2710-double-execution.test.js +51 -46
- package/server/lib/scheduled-tasks/runtime.js +81 -74
- package/server/lib/scheduled-tasks/runtime.test.js +53 -1
- package/server/lib/session-assist/DOCUMENTATION.md +8 -3
- package/server/lib/session-assist/context.js +122 -46
- package/server/lib/session-assist/context.test.js +117 -49
- package/server/lib/session-assist/runtime.js +52 -22
- package/server/lib/session-assist/runtime.test.js +145 -173
- package/server/lib/session-goal/DOCUMENTATION.md +23 -15
- package/server/lib/session-goal/create.js +9 -12
- package/server/lib/session-goal/create.test.js +27 -28
- package/server/lib/session-goal/runtime.js +164 -77
- package/server/lib/session-goal/runtime.test.js +271 -519
- package/server/lib/session-knowledge/DOCUMENTATION.md +5 -0
- package/server/lib/session-knowledge/runtime.js +59 -41
- package/server/lib/session-knowledge/runtime.test.js +90 -20
- package/server/lib/small-model/DOCUMENTATION.md +154 -208
- package/server/lib/small-model/client.js +113 -0
- package/server/lib/small-model/index.js +296 -158
- package/server/lib/small-model/index.test.js +423 -256
- package/server/lib/small-model/routes.js +1 -1
- package/server/lib/spaces/DOCUMENTATION.md +807 -0
- package/server/lib/spaces/code-in-bait.js +185 -0
- package/server/lib/spaces/code-in.js +641 -0
- package/server/lib/spaces/code-in.test.js +1048 -0
- package/server/lib/spaces/errors.js +8 -0
- package/server/lib/spaces/exec-http.js +94 -0
- package/server/lib/spaces/gatekeeper-channel.js +202 -0
- package/server/lib/spaces/gatekeeper-channel.test.js +215 -0
- package/server/lib/spaces/gatekeeper-program.cjs +914 -0
- package/server/lib/spaces/gatekeeper-program.test.js +1391 -0
- package/server/lib/spaces/hardening.js +457 -0
- package/server/lib/spaces/hardening.test.js +368 -0
- package/server/lib/spaces/host-git.js +127 -0
- package/server/lib/spaces/host-git.test.js +137 -0
- package/server/lib/spaces/labels.js +234 -0
- package/server/lib/spaces/labels.test.js +219 -0
- package/server/lib/spaces/layout.js +184 -0
- package/server/lib/spaces/layout.test.js +30 -0
- package/server/lib/spaces/manager.js +82 -0
- package/server/lib/spaces/manager.test.js +114 -0
- package/server/lib/spaces/places/code-in.docker.live.test.js +216 -0
- package/server/lib/spaces/places/contract-suite.js +156 -0
- package/server/lib/spaces/places/contract.docker.live.test.js +7 -0
- package/server/lib/spaces/places/contract.test.js +6 -0
- package/server/lib/spaces/places/docker-engine.js +126 -0
- package/server/lib/spaces/places/docker-live-support.js +202 -0
- package/server/lib/spaces/places/docker-tools.js +186 -0
- package/server/lib/spaces/places/docker.js +587 -0
- package/server/lib/spaces/places/docker.test.js +1610 -0
- package/server/lib/spaces/places/escape-suite.js +1018 -0
- package/server/lib/spaces/places/escape.docker.live.test.js +7 -0
- package/server/lib/spaces/places/fake-docker.js +375 -0
- package/server/lib/spaces/places/memory-place.js +103 -0
- package/server/lib/spaces/places/packed.docker.live.test.js +77 -0
- package/server/lib/spaces/places/registry.js +43 -0
- package/server/lib/spaces/places/registry.test.js +50 -0
- package/server/lib/spaces/places/server.docker.live.test.js +225 -0
- package/server/lib/spaces/run-command.js +261 -0
- package/server/lib/spaces/run-command.test.js +396 -0
- package/server/lib/spaces/space-server.js +190 -0
- package/server/lib/spaces/space-server.test.js +335 -0
- package/server/lib/spaces/tools-filler.js +82 -0
- package/server/lib/spaces/tools-filler.test.js +148 -0
- package/server/lib/spaces/tools-pack.js +49 -0
- package/server/lib/spaces/tools-pack.test.js +57 -0
- package/server/lib/spaces/tools.js +138 -0
- package/server/lib/spaces/tools.test.js +153 -0
- package/server/lib/tts/openai-sdk.js +11 -0
- package/server/lib/tts/service.js +7 -5
- package/server/lib/tts/stt.js +3 -2
- package/server/lib/tunnels/DOCUMENTATION.md +2 -0
- package/server/lib/tunnels/routes.js +7 -0
- package/server/lib/tunnels/routes.test.js +55 -0
- package/server/lib/ui-auth/ui-auth.js +38 -2
- package/server/lib/ui-auth/ui-auth.test.js +42 -0
- package/server/lib/ui-auth/ui-passkeys.js +12 -10
- package/server/lib/walkthrough/DOCUMENTATION.md +10 -1
- package/server/lib/walkthrough/pull-request.js +61 -8
- package/server/lib/walkthrough/pull-request.test.js +54 -1
- package/server/lib/walkthrough/routes.js +20 -0
- package/server/lib/walkthrough/routes.test.js +19 -0
- package/server/opencode-proxy.test.js +190 -166
- package/server/opencode-worktree-readiness.test.js +5 -4
- package/dist/assets/DiffView-COVQhWGd.js +0 -4
- package/dist/assets/FilesView-B1V5JxFX.js +0 -13
- package/dist/assets/LinearIssuesView-D7FKg1XJ.js +0 -1
- package/dist/assets/MarkdownImageGallery-BrFjAVEf.js +0 -1
- package/dist/assets/MarkdownRendererImpl-DWepTafh.js +0 -12
- package/dist/assets/OnboardingScreen-Dyxxiqdt.js +0 -1
- package/dist/assets/PlanView-LW2z_EAP.js +0 -5
- package/dist/assets/PluginPane-B4tKno2h.js +0 -3
- package/dist/assets/SettingsView-DEaMulN9.js +0 -29
- package/dist/assets/SettingsWindow-FLDJXOgQ.js +0 -1
- package/dist/assets/SubmoduleDiffSummary-CvVwD-Vi.js +0 -1
- package/dist/assets/ToolOutputDialog-BiP0x2Rw.js +0 -15
- package/dist/assets/ToolPartDiffPreview-CkR4msKP.js +0 -8
- package/dist/assets/appThemeRegistry-BuUKhbYQ.js +0 -1
- package/dist/assets/de-DgxgFyGG.js +0 -10
- package/dist/assets/es-BFsZj5F_.js +0 -16
- package/dist/assets/fr-jdZ5KKQi.js +0 -14
- package/dist/assets/index-CKHjPKbq.js +0 -1
- package/dist/assets/index-Cbci4i1W.js +0 -40
- package/dist/assets/index-DqaMWNrC.css +0 -1
- package/dist/assets/ja-C5kKrhO4.js +0 -16
- package/dist/assets/ko-DXDTT_0O.js +0 -16
- package/dist/assets/main-CZALtVek.js +0 -896
- package/dist/assets/main-CgXMBNvG.js +0 -2
- package/dist/assets/miniChat-CwTmDixi.js +0 -2
- package/dist/assets/mobile-CLf2vCtO.js +0 -2
- package/dist/assets/pl-D-7x4Y8Q.js +0 -16
- package/dist/assets/pt-BR-CSBZrHa8.js +0 -16
- package/dist/assets/renderElectronMiniChatApp-C5gAFBk0.js +0 -2
- package/dist/assets/renderMobileApp-V4hYd32H.js +0 -25
- package/dist/assets/runtimeEndpointReset-DhlHCn-2.js +0 -25
- package/dist/assets/scheduledTasksApi-DzjbLd8P.js +0 -1
- package/dist/assets/tr-Dr-oJ22L.js +0 -7
- package/dist/assets/uk-9Nx83UcI.js +0 -16
- package/dist/assets/useAppFontEffects-khZXpFL_.js +0 -7333
- package/dist/assets/useRootScrollLock-DdvHf9Pt.js +0 -2
- package/dist/assets/vendor-cron-parser-CUcP7OfX.js +0 -2
- package/dist/assets/vendor-opencode-sdk-BJ73u-bH.js +0 -7
- package/dist/assets/zh-CN-Bxy_Hj3r.js +0 -16
- package/dist/assets/zh-TW-0BB4gpHM.js +0 -16
- package/server/lib/opencode/mcp-oauth-callback.test.js +0 -111
- package/server/lib/routing/feature-flag.js +0 -11
- package/server/lib/small-model/call.js +0 -851
- package/server/lib/small-model/call.test.js +0 -1292
- package/server/lib/small-model/catalog.js +0 -13
- package/server/lib/small-model/resolve.js +0 -131
- package/server/lib/small-model/resolve.test.js +0 -197
- package/server/lib/small-model/runtime-providers.js +0 -164
- package/server/lib/small-model/runtime-providers.test.js +0 -118
- package/server/lib/system-prompt/DOCUMENTATION.md +0 -36
- package/server/lib/system-prompt/runtime.js +0 -49
- package/server/lib/system-prompt/runtime.test.js +0 -76
- package/server/lib/walkthrough/reproduce-2607.test.js +0 -148
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import fs from 'node:fs/promises';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
import { requireOpenCodeV2 } from './compatibility.js';
|
|
8
|
+
|
|
9
|
+
const INSTALL_TIMEOUT_MS = 5 * 60_000;
|
|
10
|
+
const NPM_REGISTRY = 'https://registry.npmjs.org';
|
|
11
|
+
|
|
12
|
+
const releaseSchema = z.object({ version: z.string().regex(/^2\.\d+\.\d+$/) });
|
|
13
|
+
const packageDistSchema = z.object({
|
|
14
|
+
dist: z.object({
|
|
15
|
+
tarball: z.string().url(),
|
|
16
|
+
integrity: z.string().regex(/^sha512-[A-Za-z0-9+/]+={0,2}$/),
|
|
17
|
+
}),
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// OpenCode's installer is a bash script, so Windows installs the npm platform
|
|
21
|
+
// package that script downloads. x64-baseline serves arm64 too: the native arm64
|
|
22
|
+
// build fails to load (https://github.com/anomalyco/opencode/issues/19130), the
|
|
23
|
+
// same workaround the desktop bundle uses in packages/electron/scripts/prepare-opencode-cli.mjs.
|
|
24
|
+
const WINDOWS_PACKAGE = '@opencode/cli-windows-x64-baseline';
|
|
25
|
+
|
|
26
|
+
export const supportsOpenCodeV2Install = (platform = process.platform) =>
|
|
27
|
+
(platform === 'darwin' || platform === 'linux' || platform === 'win32') && (process.arch === 'x64' || process.arch === 'arm64');
|
|
28
|
+
|
|
29
|
+
const runInstaller = (script, version, env) => new Promise((resolve, reject) => {
|
|
30
|
+
const child = spawn('/bin/bash', [script, '--version', version, '--no-modify-path'], {
|
|
31
|
+
env, cwd: os.tmpdir(), detached: true, stdio: 'ignore',
|
|
32
|
+
});
|
|
33
|
+
const terminate = () => {
|
|
34
|
+
if (!child.pid) return;
|
|
35
|
+
try { process.kill(-child.pid, 'SIGKILL'); } catch { /* Already exited. */ }
|
|
36
|
+
};
|
|
37
|
+
const timer = setTimeout(terminate, INSTALL_TIMEOUT_MS);
|
|
38
|
+
child.once('error', () => {
|
|
39
|
+
clearTimeout(timer);
|
|
40
|
+
reject(new Error('Could not start the OpenCode installer.'));
|
|
41
|
+
});
|
|
42
|
+
child.once('close', (code) => {
|
|
43
|
+
clearTimeout(timer);
|
|
44
|
+
terminate();
|
|
45
|
+
// Installer output may contain environment or registry secrets.
|
|
46
|
+
if (code === 0) resolve();
|
|
47
|
+
else reject(new Error('OpenCode installation failed. Try the official installation guide.'));
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const extractArchive = (tarCommand, archive, destination) => new Promise((resolve, reject) => {
|
|
52
|
+
const child = spawn(tarCommand, ['-xzf', archive, '-C', destination], {
|
|
53
|
+
stdio: 'ignore', windowsHide: true, timeout: INSTALL_TIMEOUT_MS,
|
|
54
|
+
});
|
|
55
|
+
child.once('error', () => reject(new Error('Could not unpack the OpenCode package.')));
|
|
56
|
+
child.once('close', (code) => {
|
|
57
|
+
if (code === 0) resolve();
|
|
58
|
+
else reject(new Error('Could not unpack the OpenCode package.'));
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
/** Downloads and runs the official installer script. Returns the step that replaces the binary. */
|
|
63
|
+
const prepareInstallerScript = async ({ version, workDirectory, env, fetchImpl }) => {
|
|
64
|
+
const response = await fetchImpl('https://opencode.ai/v2/install', { signal: AbortSignal.timeout(15_000) });
|
|
65
|
+
if (!response.ok) throw new Error('Could not download the OpenCode installer.');
|
|
66
|
+
const script = path.join(workDirectory, 'install.sh');
|
|
67
|
+
await fs.writeFile(script, await response.text(), { mode: 0o600 });
|
|
68
|
+
return () => runInstaller(script, version, env);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Downloads the Windows platform package, checks it against the integrity npm
|
|
73
|
+
* publishes for it, and unpacks it. Returns the step that replaces the binary.
|
|
74
|
+
*/
|
|
75
|
+
const prepareWindowsBinary = async ({ version, directory, workDirectory, fetchImpl, tarCommand }) => {
|
|
76
|
+
const metadataResponse = await fetchImpl(`${NPM_REGISTRY}/${WINDOWS_PACKAGE.replace('/', '%2F')}/${version}`, { signal: AbortSignal.timeout(15_000) });
|
|
77
|
+
if (!metadataResponse.ok) throw new Error('Could not resolve the OpenCode v2 package.');
|
|
78
|
+
const { dist } = packageDistSchema.parse(await metadataResponse.json());
|
|
79
|
+
if (new URL(dist.tarball).origin !== NPM_REGISTRY) throw new Error('The OpenCode v2 package is not served by the npm registry.');
|
|
80
|
+
const archiveResponse = await fetchImpl(dist.tarball, { signal: AbortSignal.timeout(INSTALL_TIMEOUT_MS) });
|
|
81
|
+
if (!archiveResponse.ok) throw new Error('Could not download the OpenCode v2 package.');
|
|
82
|
+
const archive = Buffer.from(await archiveResponse.arrayBuffer());
|
|
83
|
+
if (`sha512-${createHash('sha512').update(archive).digest('base64')}` !== dist.integrity) {
|
|
84
|
+
throw new Error('The downloaded OpenCode v2 package failed its integrity check.');
|
|
85
|
+
}
|
|
86
|
+
const archivePath = path.join(workDirectory, 'opencode.tgz');
|
|
87
|
+
const extractDirectory = path.join(workDirectory, 'extract');
|
|
88
|
+
await fs.writeFile(archivePath, archive);
|
|
89
|
+
await fs.mkdir(extractDirectory);
|
|
90
|
+
await extractArchive(tarCommand, archivePath, extractDirectory);
|
|
91
|
+
const extracted = path.join(extractDirectory, 'package', 'bin', 'opencode.exe');
|
|
92
|
+
await fs.access(extracted).catch(() => { throw new Error('The OpenCode v2 package does not contain opencode.exe.'); });
|
|
93
|
+
return () => fs.copyFile(extracted, path.join(directory, 'opencode.exe'));
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
// Only host-owned callers supply these options. No request body controls a
|
|
97
|
+
// command, URL, version, destination, or environment.
|
|
98
|
+
export const installOpenCodeV2 = async ({
|
|
99
|
+
homeDirectory = os.homedir(),
|
|
100
|
+
env = process.env,
|
|
101
|
+
fetchImpl = fetch,
|
|
102
|
+
platform = process.platform,
|
|
103
|
+
// Windows' own bsdtar. Git Bash's GNU tar on PATH reads `C:\...` as a remote host.
|
|
104
|
+
tarCommand = path.join(process.env.SystemRoot || 'C:\\Windows', 'System32', 'tar.exe'),
|
|
105
|
+
} = {}) => {
|
|
106
|
+
if (!supportsOpenCodeV2Install(platform)) throw new Error('Automatic OpenCode v2 installation is unavailable on this platform.');
|
|
107
|
+
const windows = platform === 'win32';
|
|
108
|
+
const binaryNames = windows ? ['opencode.exe'] : ['opencode', 'opencode2'];
|
|
109
|
+
const directory = path.join(homeDirectory, '.opencode', 'bin');
|
|
110
|
+
await fs.mkdir(directory, { recursive: true });
|
|
111
|
+
const lock = path.join(directory, '.openchamber-install');
|
|
112
|
+
// Also serializes separate OpenChamber/VS Code processes sharing this home.
|
|
113
|
+
await fs.mkdir(lock).catch(() => { throw new Error('Another OpenCode installation is already in progress.'); });
|
|
114
|
+
const snapshots = [];
|
|
115
|
+
let started = false;
|
|
116
|
+
let cleanup = true;
|
|
117
|
+
try {
|
|
118
|
+
const releaseResponse = await fetchImpl(`${NPM_REGISTRY}/@opencode%2Fcli/latest`, { signal: AbortSignal.timeout(15_000) });
|
|
119
|
+
if (!releaseResponse.ok) throw new Error('Could not resolve the OpenCode v2 release.');
|
|
120
|
+
const { version } = releaseSchema.parse(await releaseResponse.json());
|
|
121
|
+
const replaceBinary = windows
|
|
122
|
+
? await prepareWindowsBinary({ version, directory, workDirectory: lock, fetchImpl, tarCommand })
|
|
123
|
+
: await prepareInstallerScript({ version, workDirectory: lock, env, fetchImpl });
|
|
124
|
+
for (const name of binaryNames) {
|
|
125
|
+
const target = path.join(directory, name);
|
|
126
|
+
const backup = path.join(lock, name);
|
|
127
|
+
const exists = await fs.lstat(target).then(() => true, (error) => {
|
|
128
|
+
if (error.code === 'ENOENT') return false;
|
|
129
|
+
throw error;
|
|
130
|
+
});
|
|
131
|
+
if (exists) await fs.cp(target, backup, { dereference: false, verbatimSymlinks: true });
|
|
132
|
+
snapshots.push({ target, backup, exists });
|
|
133
|
+
}
|
|
134
|
+
started = true;
|
|
135
|
+
await replaceBinary();
|
|
136
|
+
const binary = path.join(directory, binaryNames[0]);
|
|
137
|
+
const installedVersion = await requireOpenCodeV2({ binary, args: [] }, { env });
|
|
138
|
+
if (installedVersion !== version) throw new Error('The installed OpenCode version does not match the requested release.');
|
|
139
|
+
return binary;
|
|
140
|
+
} catch (error) {
|
|
141
|
+
if (started) {
|
|
142
|
+
try {
|
|
143
|
+
for (const { target, backup, exists } of snapshots) {
|
|
144
|
+
await fs.rm(target, { force: true });
|
|
145
|
+
if (exists) await fs.rename(backup, target);
|
|
146
|
+
}
|
|
147
|
+
} catch {
|
|
148
|
+
// Keep any remaining backups for manual recovery; never delete them
|
|
149
|
+
// after a failed rollback or allow another installer to overwrite them.
|
|
150
|
+
cleanup = false;
|
|
151
|
+
throw new Error('Could not restore the previous OpenCode installation. The backup remains in .opencode/bin/.openchamber-install.');
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
throw error;
|
|
155
|
+
} finally {
|
|
156
|
+
if (cleanup) await fs.rm(lock, { recursive: true, force: true });
|
|
157
|
+
}
|
|
158
|
+
};
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
2
|
+
import { execFileSync } from 'node:child_process';
|
|
3
|
+
import { createHash } from 'node:crypto';
|
|
4
|
+
import fs from 'node:fs/promises';
|
|
5
|
+
import os from 'node:os';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import { installOpenCodeV2 } from './v2-install.js';
|
|
8
|
+
import { readOpenCodeCliVersion } from './compatibility.js';
|
|
9
|
+
|
|
10
|
+
let homeDirectory;
|
|
11
|
+
let binary;
|
|
12
|
+
const quote = (value) => "'" + value.replaceAll("'", "'\\''") + "'";
|
|
13
|
+
const cli = (version) => '#!/bin/sh\nprintf "%s\\n" "opencode v' + version + '"\n';
|
|
14
|
+
const run = (script, version = '2.0.15') => installOpenCodeV2({
|
|
15
|
+
homeDirectory,
|
|
16
|
+
fetchImpl: async (url) => url.includes('registry.npmjs.org')
|
|
17
|
+
? Response.json({ version })
|
|
18
|
+
: new Response(script),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
beforeEach(async () => {
|
|
22
|
+
homeDirectory = await fs.mkdtemp(path.join(os.tmpdir(), 'oc-install-test-'));
|
|
23
|
+
binary = path.join(homeDirectory, '.opencode/bin/opencode');
|
|
24
|
+
await fs.mkdir(path.dirname(binary), { recursive: true });
|
|
25
|
+
await fs.writeFile(binary, cli('1.18.30'), { mode: 0o755 });
|
|
26
|
+
});
|
|
27
|
+
afterEach(async () => { await fs.rm(homeDirectory, { recursive: true, force: true }); });
|
|
28
|
+
|
|
29
|
+
describe('OpenCode v2 installation', () => {
|
|
30
|
+
it('passes the validated release and verifies the actual executable', async () => {
|
|
31
|
+
const script = `#!/bin/bash
|
|
32
|
+
set -eu
|
|
33
|
+
test "$1" = "--version"
|
|
34
|
+
test "$2" = "2.0.15"
|
|
35
|
+
test "$3" = "--no-modify-path"
|
|
36
|
+
printf %s ${quote(cli('2.0.15'))} > ${quote(binary)}
|
|
37
|
+
chmod 755 ${quote(binary)}
|
|
38
|
+
`;
|
|
39
|
+
expect(await run(script)).toBe(binary);
|
|
40
|
+
expect(await readOpenCodeCliVersion({ binary, args: [] })).toBe('2.0.15');
|
|
41
|
+
await expect(fs.stat(path.join(path.dirname(binary), '.openchamber-install'))).rejects.toMatchObject({ code: 'ENOENT' });
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('restores the binary and shim when an installer exits zero but leaves v1', async () => {
|
|
45
|
+
const shim = path.join(path.dirname(binary), 'opencode2');
|
|
46
|
+
await fs.symlink('opencode', shim);
|
|
47
|
+
const script = `printf %s broken > ${quote(shim)}\nexit 0\n`;
|
|
48
|
+
await expect(run(script)).rejects.toThrow();
|
|
49
|
+
expect(await readOpenCodeCliVersion({ binary, args: [] })).toBe('1.18.30');
|
|
50
|
+
expect(await fs.readlink(shim)).toBe('opencode');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('restores v1 after a partial installer failure, and permits a retry', async () => {
|
|
54
|
+
const script = `printf %s broken > ${quote(binary)}\nexit 1\n`;
|
|
55
|
+
await expect(run(script)).rejects.toThrow('installation failed');
|
|
56
|
+
expect(await readOpenCodeCliVersion({ binary, args: [] })).toBe('1.18.30');
|
|
57
|
+
await expect(run(script)).rejects.toThrow('installation failed');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('rejects a non-v2 registry release before running the installer', async () => {
|
|
61
|
+
await expect(run(`rm ${quote(binary)}`, '3.0.0')).rejects.toThrow();
|
|
62
|
+
expect(await readOpenCodeCliVersion({ binary, args: [] })).toBe('1.18.30');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
describe('on Windows', () => {
|
|
66
|
+
const TARBALL = 'https://registry.npmjs.org/@opencode/cli-windows-x64-baseline/-/cli-windows-x64-baseline-2.0.15.tgz';
|
|
67
|
+
let exe;
|
|
68
|
+
const integrityOf = (bytes) => `sha512-${createHash('sha512').update(bytes).digest('base64')}`;
|
|
69
|
+
// The platform package keeps its binary at package/bin/opencode.exe. Here it is a
|
|
70
|
+
// shell script, so the version check runs it on the Linux/macOS test host.
|
|
71
|
+
const pack = async (version) => {
|
|
72
|
+
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'oc-package-'));
|
|
73
|
+
await fs.mkdir(path.join(root, 'package/bin'), { recursive: true });
|
|
74
|
+
await fs.writeFile(path.join(root, 'package/bin/opencode.exe'), cli(version), { mode: 0o755 });
|
|
75
|
+
execFileSync('tar', ['-czf', path.join(root, 'package.tgz'), '-C', root, 'package']);
|
|
76
|
+
const bytes = await fs.readFile(path.join(root, 'package.tgz'));
|
|
77
|
+
await fs.rm(root, { recursive: true, force: true });
|
|
78
|
+
return bytes;
|
|
79
|
+
};
|
|
80
|
+
const runWindows = (archive, integrity = integrityOf(archive)) => installOpenCodeV2({
|
|
81
|
+
homeDirectory,
|
|
82
|
+
platform: 'win32',
|
|
83
|
+
tarCommand: 'tar',
|
|
84
|
+
fetchImpl: async (url) => {
|
|
85
|
+
if (url.endsWith('/@opencode%2Fcli/latest')) return Response.json({ version: '2.0.15' });
|
|
86
|
+
if (url.endsWith('/@opencode%2Fcli-windows-x64-baseline/2.0.15')) return Response.json({ dist: { tarball: TARBALL, integrity } });
|
|
87
|
+
if (url === TARBALL) return new Response(archive);
|
|
88
|
+
return new Response(null, { status: 404 });
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
beforeEach(async () => {
|
|
93
|
+
exe = path.join(homeDirectory, '.opencode/bin/opencode.exe');
|
|
94
|
+
await fs.writeFile(exe, cli('1.18.30'), { mode: 0o755 });
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('installs the verified platform package without the bash installer', async () => {
|
|
98
|
+
expect(await runWindows(await pack('2.0.15'))).toBe(exe);
|
|
99
|
+
expect(await readOpenCodeCliVersion({ binary: exe, args: [] })).toBe('2.0.15');
|
|
100
|
+
expect(await readOpenCodeCliVersion({ binary, args: [] })).toBe('1.18.30');
|
|
101
|
+
await expect(fs.stat(path.join(path.dirname(exe), '.openchamber-install'))).rejects.toMatchObject({ code: 'ENOENT' });
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('rejects a package that does not match its published integrity before touching the binary', async () => {
|
|
105
|
+
const archive = await pack('2.0.15');
|
|
106
|
+
await expect(runWindows(archive, integrityOf(Buffer.from('other')))).rejects.toThrow('integrity');
|
|
107
|
+
expect(await readOpenCodeCliVersion({ binary: exe, args: [] })).toBe('1.18.30');
|
|
108
|
+
await expect(fs.stat(path.join(path.dirname(exe), '.openchamber-install'))).rejects.toMatchObject({ code: 'ENOENT' });
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('restores the previous binary when the package holds another release', async () => {
|
|
112
|
+
await expect(runWindows(await pack('2.0.14'))).rejects.toThrow();
|
|
113
|
+
expect(await readOpenCodeCliVersion({ binary: exe, args: [] })).toBe('1.18.30');
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('does not overwrite an installation owned by another process', async () => {
|
|
118
|
+
const lock = path.join(path.dirname(binary), '.openchamber-install');
|
|
119
|
+
await fs.mkdir(lock);
|
|
120
|
+
await expect(run('exit 0')).rejects.toThrow('already in progress');
|
|
121
|
+
expect((await fs.stat(lock)).isDirectory()).toBe(true);
|
|
122
|
+
expect(await readOpenCodeCliVersion({ binary, args: [] })).toBe('1.18.30');
|
|
123
|
+
});
|
|
124
|
+
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createUpstreamSseReader } from '../event-stream/upstream-reader.js';
|
|
2
|
+
import { translateWireEvent } from '../event-stream/translate-v2.js';
|
|
2
3
|
|
|
3
4
|
export const createOpenCodeWatcherRuntime = (deps) => {
|
|
4
5
|
const {
|
|
@@ -17,6 +18,12 @@ export const createOpenCodeWatcherRuntime = (deps) => {
|
|
|
17
18
|
let unsubscribeEvent = null;
|
|
18
19
|
let unsubscribeStatus = null;
|
|
19
20
|
|
|
21
|
+
// `onPayload` consumers speak the server's own event vocabulary, so the v2
|
|
22
|
+
// wire payload is translated here rather than in each consumer.
|
|
23
|
+
const emitTranslated = (payload) => {
|
|
24
|
+
for (const translated of translateWireEvent(payload)) onPayload(translated);
|
|
25
|
+
};
|
|
26
|
+
|
|
20
27
|
const unwrapGlobalEventPayload = (eventData) => {
|
|
21
28
|
if (!eventData || typeof eventData !== 'object') {
|
|
22
29
|
return null;
|
|
@@ -45,7 +52,7 @@ export const createOpenCodeWatcherRuntime = (deps) => {
|
|
|
45
52
|
if (!payload || typeof payload !== 'object') {
|
|
46
53
|
return;
|
|
47
54
|
}
|
|
48
|
-
|
|
55
|
+
emitTranslated(payload);
|
|
49
56
|
});
|
|
50
57
|
unsubscribeStatus = globalEventHub.subscribeStatus((status) => {
|
|
51
58
|
if (signal.aborted) {
|
|
@@ -65,7 +72,7 @@ export const createOpenCodeWatcherRuntime = (deps) => {
|
|
|
65
72
|
|
|
66
73
|
reader = createUpstreamSseReader({
|
|
67
74
|
signal,
|
|
68
|
-
buildUrl: () => buildOpenCodeUrl('/
|
|
75
|
+
buildUrl: () => buildOpenCodeUrl('/api/event', ''),
|
|
69
76
|
getHeaders: getOpenCodeAuthHeaders,
|
|
70
77
|
fetchImpl,
|
|
71
78
|
stallTimeoutMs: upstreamStallTimeoutMs,
|
|
@@ -78,7 +85,7 @@ export const createOpenCodeWatcherRuntime = (deps) => {
|
|
|
78
85
|
if (!payload || typeof payload !== 'object') {
|
|
79
86
|
return;
|
|
80
87
|
}
|
|
81
|
-
|
|
88
|
+
emitTranslated(payload);
|
|
82
89
|
},
|
|
83
90
|
onError(error) {
|
|
84
91
|
if (signal.aborted) {
|
|
@@ -63,7 +63,7 @@ describe('createOpenCodeWatcherRuntime', () => {
|
|
|
63
63
|
return createSseResponse({
|
|
64
64
|
signal: options.signal,
|
|
65
65
|
blocks: [
|
|
66
|
-
'
|
|
66
|
+
'data: {"directory":"/tmp/project","payload":{"id":"evt-1","type":"session.renamed","location":{"directory":"/tmp/project"},"data":{"sessionID":"ses_1","title":"New"}}}\n\n',
|
|
67
67
|
],
|
|
68
68
|
});
|
|
69
69
|
},
|
|
@@ -74,7 +74,7 @@ describe('createOpenCodeWatcherRuntime', () => {
|
|
|
74
74
|
|
|
75
75
|
expect(fetchCalls).toEqual([
|
|
76
76
|
{
|
|
77
|
-
url: 'http://127.0.0.1:4096/
|
|
77
|
+
url: 'http://127.0.0.1:4096/api/event',
|
|
78
78
|
headers: {
|
|
79
79
|
Accept: 'text/event-stream',
|
|
80
80
|
'Cache-Control': 'no-cache',
|
|
@@ -83,14 +83,13 @@ describe('createOpenCodeWatcherRuntime', () => {
|
|
|
83
83
|
},
|
|
84
84
|
},
|
|
85
85
|
]);
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
]);
|
|
86
|
+
// The watcher hands its consumers the server's own vocabulary, translated
|
|
87
|
+
// from the v2 wire event.
|
|
88
|
+
expect(payloads).toHaveLength(1);
|
|
89
|
+
expect(payloads[0]).toMatchObject({
|
|
90
|
+
type: 'session.updated',
|
|
91
|
+
properties: { sessionID: 'ses_1', info: { id: 'ses_1', title: 'New' } },
|
|
92
|
+
});
|
|
94
93
|
});
|
|
95
94
|
|
|
96
95
|
it('resumes watcher reconnects with Last-Event-ID after a stalled upstream stream', async () => {
|
|
@@ -118,7 +117,7 @@ describe('createOpenCodeWatcherRuntime', () => {
|
|
|
118
117
|
signal: options.signal,
|
|
119
118
|
holdOpen: true,
|
|
120
119
|
blocks: [
|
|
121
|
-
'id:
|
|
120
|
+
'data: {"id":"evt-1","type":"server.connected","data":{}}\n\n',
|
|
122
121
|
],
|
|
123
122
|
});
|
|
124
123
|
}
|
|
@@ -126,7 +125,7 @@ describe('createOpenCodeWatcherRuntime', () => {
|
|
|
126
125
|
return createSseResponse({
|
|
127
126
|
signal: options.signal,
|
|
128
127
|
blocks: [
|
|
129
|
-
'id:
|
|
128
|
+
'data: {"id":"evt-2","type":"session.renamed","location":{"directory":"/tmp/project"},"data":{"sessionID":"ses_1","title":"New"}}\n\n',
|
|
130
129
|
],
|
|
131
130
|
});
|
|
132
131
|
},
|
|
@@ -157,7 +156,7 @@ describe('createOpenCodeWatcherRuntime', () => {
|
|
|
157
156
|
signal: options.signal,
|
|
158
157
|
holdOpen: true,
|
|
159
158
|
blocks: [
|
|
160
|
-
'
|
|
159
|
+
'data: {"payload":{"id":"evt-1","type":"session.renamed","location":{"directory":"/tmp/project"},"data":{"sessionID":"ses_1","title":"New"}}}\n\n',
|
|
161
160
|
],
|
|
162
161
|
});
|
|
163
162
|
},
|
|
@@ -183,14 +182,11 @@ describe('createOpenCodeWatcherRuntime', () => {
|
|
|
183
182
|
|
|
184
183
|
expect(hubFetchCalls).toBe(1);
|
|
185
184
|
expect(watcherFetchCalls).toBe(0);
|
|
186
|
-
expect(payloads).
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
},
|
|
192
|
-
},
|
|
193
|
-
]);
|
|
185
|
+
expect(payloads).toHaveLength(1);
|
|
186
|
+
expect(payloads[0]).toMatchObject({
|
|
187
|
+
type: 'session.updated',
|
|
188
|
+
properties: { sessionID: 'ses_1', info: { id: 'ses_1', title: 'New' } },
|
|
189
|
+
});
|
|
194
190
|
});
|
|
195
191
|
|
|
196
192
|
it('does not stop a shared global event hub when the watcher stops', async () => {
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import {
|
|
4
|
+
AGENT_SCOPE,
|
|
5
|
+
findWorktreeRoot,
|
|
6
|
+
getAncestors,
|
|
7
|
+
getJsonWriteTarget,
|
|
8
|
+
readConfigFile,
|
|
9
|
+
readConfigLayers,
|
|
10
|
+
writeConfig,
|
|
11
|
+
} from './shared.js';
|
|
12
|
+
import { findWebSearchProjectOverride, writeWebSearchSelection } from './config-v2.js';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Writes the `websearch` choice into the config OpenCode reads last among the
|
|
16
|
+
* files OpenChamber owns a write to: `OPENCODE_CONFIG` when the user set one,
|
|
17
|
+
* else the user's global `opencode.json`. OpenCode watches both, so the choice
|
|
18
|
+
* applies without a restart. `selection` comes from `parseWebSearchSelection`.
|
|
19
|
+
* The file is left untouched when the choice is already there.
|
|
20
|
+
*/
|
|
21
|
+
export function setWebSearchSelection(selection) {
|
|
22
|
+
const layers = readConfigLayers(null);
|
|
23
|
+
const target = getJsonWriteTarget(layers, AGENT_SCOPE.USER);
|
|
24
|
+
const changed = writeWebSearchSelection(target.config, selection);
|
|
25
|
+
if (changed) writeConfig(target.config, target.path);
|
|
26
|
+
return { path: target.path, changed };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Where the effective `websearch` value for `directory` comes from, as far as
|
|
31
|
+
* a Settings write is concerned: `projectPath` is the project config that
|
|
32
|
+
* overrides whatever Settings writes, or `null` when a Settings write applies.
|
|
33
|
+
*/
|
|
34
|
+
export function getWebSearchSource(directory) {
|
|
35
|
+
return { projectPath: findWebSearchProjectOverride(readConfigLayers(directory), readProjectConfigFiles(directory)) };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const PROJECT_CONFIG_NAMES = [
|
|
39
|
+
path.join('.opencode', 'opencode.jsonc'),
|
|
40
|
+
path.join('.opencode', 'opencode.json'),
|
|
41
|
+
'opencode.jsonc',
|
|
42
|
+
'opencode.json',
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Every existing project config file OpenCode merges for `directory`, deepest
|
|
47
|
+
* first, from the directory up to its worktree root. An unreadable file is
|
|
48
|
+
* skipped: it can't be told apart from one without the key.
|
|
49
|
+
*/
|
|
50
|
+
function readProjectConfigFiles(directory) {
|
|
51
|
+
if (!directory) return [];
|
|
52
|
+
const root = findWorktreeRoot(directory) || path.resolve(directory);
|
|
53
|
+
const files = [];
|
|
54
|
+
for (const base of getAncestors(directory, root)) {
|
|
55
|
+
for (const name of PROJECT_CONFIG_NAMES) {
|
|
56
|
+
const filePath = path.join(base, name);
|
|
57
|
+
if (!fs.existsSync(filePath)) continue;
|
|
58
|
+
try {
|
|
59
|
+
files.push({ path: filePath, config: readConfigFile(filePath) });
|
|
60
|
+
} catch {
|
|
61
|
+
// Skipped; see above.
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return files;
|
|
66
|
+
}
|