@vantran-se/openchamber-web 1.24.4 → 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-B4a0JkUz.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-Byu2DY7s.js → GitView-BWa7e5vw.js} +4 -4
- package/dist/assets/{GuestAttachDialog-B808E0-d.js → GuestAttachDialog-DdOuyZzp.js} +1 -1
- package/dist/assets/GuestSurfacePane-DDfzid3U.js +1 -0
- package/dist/assets/{JsonTreeView-VW_7czo3.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-DCjGhlYV.js → PatchDiff-DS-IMU1j.js} +1 -1
- package/dist/assets/{PierreDiffViewer-CEDZNkU2.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-NU0FHFhT.js → VirtualizedFileDiff-C763hNCR.js} +4 -4
- package/dist/assets/{WalkthroughView-BZyjhqHv.js → WalkthroughView-lap5btCF.js} +2 -2
- package/dist/assets/appThemeRegistry-u4zw1l0B.js +1 -0
- package/dist/assets/{commitAndPush-BDftrjr6.js → commitAndPush-CwGJfJG-.js} +1 -1
- package/dist/assets/{computeVirtualFileMetrics-DlWlEM3x.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-CO4e5J_S.js → isDiffPlainText-D47a6hMP.js} +1 -1
- package/dist/assets/{isFilePlainText-ue6kCou7.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-CK48LG0z.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-D7QVlovD.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-BPu3JiQs.js → shikiHighlight-DNm2Z_Jc.js} +1 -1
- package/dist/assets/{textMateThemeFromAppTheme-DWqRPo9A.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-CgS0fnw_.js → useInlineCommentController-CuUuakrt.js} +2 -2
- package/dist/assets/useRootScrollLock-CQIsyERq.js +2 -0
- package/dist/assets/{vendor-codemirror-language-data-Bx2EHc1O.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-DxX8bZ1o.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-B2CD9wC8.js +0 -4
- package/dist/assets/FilesView-C4vMFclc.js +0 -13
- package/dist/assets/LinearIssuesView-EzGNCGrd.js +0 -1
- package/dist/assets/MarkdownImageGallery-B2YUDFaB.js +0 -1
- package/dist/assets/MarkdownRendererImpl-B4ikIzR3.js +0 -12
- package/dist/assets/OnboardingScreen-CQHBSTEi.js +0 -1
- package/dist/assets/PlanView-D3x-pTkZ.js +0 -5
- package/dist/assets/PluginPane-DEcgDQ9h.js +0 -3
- package/dist/assets/SettingsView-CSinuH-1.js +0 -29
- package/dist/assets/SettingsWindow-B5CHoJFZ.js +0 -1
- package/dist/assets/SubmoduleDiffSummary-oxeACCWs.js +0 -1
- package/dist/assets/ToolOutputDialog-tLNWmfcW.js +0 -15
- package/dist/assets/ToolPartDiffPreview-CflzmK-x.js +0 -8
- package/dist/assets/appThemeRegistry-DLicw4dH.js +0 -1
- package/dist/assets/de-Bro6_zyl.js +0 -10
- package/dist/assets/es-Bb9XQZvd.js +0 -16
- package/dist/assets/fr-Dgfzn-IL.js +0 -14
- package/dist/assets/index-CtQNmqUL.js +0 -1
- package/dist/assets/index-DqaMWNrC.css +0 -1
- package/dist/assets/index-Dqc6USl3.js +0 -40
- package/dist/assets/ja-BYsakGFh.js +0 -16
- package/dist/assets/ko-BSaQz9aN.js +0 -16
- package/dist/assets/main-B62_pjL5.js +0 -2
- package/dist/assets/main-BEdcOI2P.js +0 -896
- package/dist/assets/miniChat-LT8Rqofr.js +0 -2
- package/dist/assets/mobile-CqarEynh.js +0 -2
- package/dist/assets/pl-OIviPdoj.js +0 -16
- package/dist/assets/pt-BR-Cltg-E6s.js +0 -16
- package/dist/assets/renderElectronMiniChatApp-COyLTtZx.js +0 -2
- package/dist/assets/renderMobileApp-BC1MHg6g.js +0 -25
- package/dist/assets/runtimeEndpointReset-DdrazOVH.js +0 -25
- package/dist/assets/scheduledTasksApi-B2SWMGEU.js +0 -1
- package/dist/assets/tr-CQI-KPre.js +0 -7
- package/dist/assets/uk-CqW7R4DA.js +0 -16
- package/dist/assets/useAppFontEffects-ne84JBVL.js +0 -7333
- package/dist/assets/useRootScrollLock-Cpf1qoGp.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-CVZG_o_J.js +0 -16
- package/dist/assets/zh-TW-D5ZxzCZp.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
|
@@ -79,12 +79,14 @@ describe('OpenCode API proxy agent wiring', () => {
|
|
|
79
79
|
}
|
|
80
80
|
});
|
|
81
81
|
|
|
82
|
-
it('
|
|
82
|
+
it('resolves an agent for the API proxy', () => {
|
|
83
|
+
// v1 also built a long-deadline proxy for the interactive provider/MCP
|
|
84
|
+
// OAuth callbacks; v2 has neither route, so there is one proxy left.
|
|
83
85
|
registerOpenCodeProxy(createStubApp(), createStubDeps(managedState()));
|
|
84
86
|
|
|
85
87
|
const agents = agentsFromCalls();
|
|
86
88
|
|
|
87
|
-
expect(agents.length).toBeGreaterThan(
|
|
89
|
+
expect(agents.length).toBeGreaterThan(0);
|
|
88
90
|
expect(agents.every(Boolean)).toBe(true);
|
|
89
91
|
expect(new Set(agents).size).toBe(1);
|
|
90
92
|
});
|
|
@@ -210,24 +210,30 @@ export const createSseBoundaryTracker = () => {
|
|
|
210
210
|
};
|
|
211
211
|
};
|
|
212
212
|
|
|
213
|
+
/**
|
|
214
|
+
* Fields a session list is allowed to carry to the browser.
|
|
215
|
+
*
|
|
216
|
+
* The list is an allowlist, not a blocklist: OpenCode keeps adding to
|
|
217
|
+
* `SessionInfo`, and a session list is fetched constantly, so anything heavy
|
|
218
|
+
* that appears later must not silently start crossing the wire. `revert.files`
|
|
219
|
+
* and `revert.snapshot` are the expensive parts and are dropped below;
|
|
220
|
+
* `permissions` is a per-session ruleset the list view never reads.
|
|
221
|
+
*/
|
|
213
222
|
const SESSION_LIST_ALLOWED_FIELDS = [
|
|
214
223
|
'id',
|
|
215
|
-
'slug',
|
|
216
|
-
'projectID',
|
|
217
|
-
'workspaceID',
|
|
218
|
-
'directory',
|
|
219
|
-
'path',
|
|
220
224
|
'parentID',
|
|
225
|
+
'projectID',
|
|
226
|
+
'location',
|
|
227
|
+
'subpath',
|
|
221
228
|
'title',
|
|
222
229
|
'agent',
|
|
223
230
|
'model',
|
|
224
|
-
'version',
|
|
225
|
-
'time',
|
|
226
231
|
'cost',
|
|
227
232
|
'tokens',
|
|
228
|
-
'
|
|
233
|
+
'outcome',
|
|
234
|
+
'time',
|
|
229
235
|
'metadata',
|
|
230
|
-
'
|
|
236
|
+
'fork',
|
|
231
237
|
];
|
|
232
238
|
|
|
233
239
|
const sanitizeSessionListItem = (session) => {
|
|
@@ -242,13 +248,8 @@ const sanitizeSessionListItem = (session) => {
|
|
|
242
248
|
}
|
|
243
249
|
}
|
|
244
250
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
const summaryWithoutDiffs = { ...summary };
|
|
248
|
-
delete summaryWithoutDiffs.diffs;
|
|
249
|
-
sanitized.summary = summaryWithoutDiffs;
|
|
250
|
-
}
|
|
251
|
-
|
|
251
|
+
// Only the revert marker: the staged file list and its snapshot are what make
|
|
252
|
+
// a reverted session's record large.
|
|
252
253
|
const revert = session.revert;
|
|
253
254
|
if (revert && typeof revert === 'object' && !Array.isArray(revert)) {
|
|
254
255
|
const revertMarker = {};
|
|
@@ -266,18 +267,28 @@ const sanitizeSessionListItem = (session) => {
|
|
|
266
267
|
return sanitized;
|
|
267
268
|
};
|
|
268
269
|
|
|
270
|
+
/**
|
|
271
|
+
* Preserve the V2 pagination envelope while sanitizing session records.
|
|
272
|
+
*/
|
|
269
273
|
const sanitizeSessionListPayload = (payload) => {
|
|
270
|
-
if (
|
|
271
|
-
return payload;
|
|
274
|
+
if (Array.isArray(payload)) {
|
|
275
|
+
return payload.map((session) => sanitizeSessionListItem(session));
|
|
272
276
|
}
|
|
273
|
-
|
|
277
|
+
if (payload && typeof payload === 'object' && Array.isArray(payload.data)) {
|
|
278
|
+
return { ...payload, data: payload.data.map((session) => sanitizeSessionListItem(session)) };
|
|
279
|
+
}
|
|
280
|
+
return payload;
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
const sessionListRecords = (payload) => {
|
|
284
|
+
if (Array.isArray(payload)) return payload;
|
|
285
|
+
if (payload && typeof payload === 'object' && Array.isArray(payload.data)) return payload.data;
|
|
286
|
+
return null;
|
|
274
287
|
};
|
|
275
288
|
|
|
276
289
|
export const registerOpenCodeProxy = (app, deps) => {
|
|
277
290
|
const {
|
|
278
291
|
fs,
|
|
279
|
-
os,
|
|
280
|
-
path,
|
|
281
292
|
OPEN_CODE_READY_GRACE_MS,
|
|
282
293
|
LONG_REQUEST_TIMEOUT_MS,
|
|
283
294
|
getRuntime,
|
|
@@ -289,8 +300,91 @@ export const registerOpenCodeProxy = (app, deps) => {
|
|
|
289
300
|
getSseUpstreamStallTimeoutMs = () => SSE_UPSTREAM_STALL_TIMEOUT_MS,
|
|
290
301
|
readWorktreeBootstrapStatus = getWorktreeBootstrapStatus,
|
|
291
302
|
WORKTREE_READY_TIMEOUT_MS = 5 * 60 * 1000,
|
|
303
|
+
// OpenCode 2.x has no archive route, so archive state is OpenChamber's own
|
|
304
|
+
// and the proxy folds it onto the sessions it serves (`time.archived`).
|
|
305
|
+
// Session metadata lives on OpenCode's record; the proxy lays over only
|
|
306
|
+
// the entries an older OpenChamber left in the legacy file until they are
|
|
307
|
+
// migrated.
|
|
308
|
+
getArchivedSessions = null,
|
|
309
|
+
getStoredSessionMetadata = null,
|
|
292
310
|
} = deps;
|
|
293
311
|
|
|
312
|
+
/**
|
|
313
|
+
* `{ [sessionID]: archivedAt }` for the current instance, or `null` when the
|
|
314
|
+
* store cannot answer. `null` means "unknown", and an unknown answer leaves
|
|
315
|
+
* the upstream record untouched — never rewrites a session as un-archived.
|
|
316
|
+
*/
|
|
317
|
+
const readArchivedSessions = async () => {
|
|
318
|
+
if (typeof getArchivedSessions !== 'function') return null;
|
|
319
|
+
try {
|
|
320
|
+
const archived = await getArchivedSessions();
|
|
321
|
+
return archived && typeof archived === 'object' ? archived : null;
|
|
322
|
+
} catch (error) {
|
|
323
|
+
console.warn('[proxy] archive state unavailable:', error?.message ?? error);
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* `{ [sessionID]: metadata }` still waiting to be migrated to OpenCode, or
|
|
330
|
+
* `null` when the store cannot answer. `null` means "unknown", and an
|
|
331
|
+
* unknown answer leaves the upstream record untouched.
|
|
332
|
+
*/
|
|
333
|
+
const readStoredSessionMetadata = async () => {
|
|
334
|
+
if (typeof getStoredSessionMetadata !== 'function') return null;
|
|
335
|
+
try {
|
|
336
|
+
const stored = await getStoredSessionMetadata();
|
|
337
|
+
// Nothing left to migrate is the normal state: skip the rewrite entirely.
|
|
338
|
+
return stored && typeof stored === 'object' && Object.keys(stored).length > 0 ? stored : null;
|
|
339
|
+
} catch (error) {
|
|
340
|
+
console.warn('[proxy] session metadata unavailable:', error?.message ?? error);
|
|
341
|
+
return null;
|
|
342
|
+
}
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
// A number archives, `null` is an explicit unarchive (drops the stamp OpenCode
|
|
346
|
+
// still carries for a session migrated from v1), and a session the file does
|
|
347
|
+
// not mention keeps whatever OpenCode says.
|
|
348
|
+
const withArchivedAt = (session, archived) => {
|
|
349
|
+
if (!session || typeof session !== 'object' || typeof session.id !== 'string') return session;
|
|
350
|
+
if (!Object.prototype.hasOwnProperty.call(archived, session.id)) return session;
|
|
351
|
+
const archivedAt = archived[session.id];
|
|
352
|
+
const time = session.time && typeof session.time === 'object' ? session.time : {};
|
|
353
|
+
if (typeof archivedAt === 'number') {
|
|
354
|
+
return { ...session, time: { ...time, archived: archivedAt } };
|
|
355
|
+
}
|
|
356
|
+
if (!('archived' in time)) return session;
|
|
357
|
+
const { archived: _dropped, ...rest } = time;
|
|
358
|
+
return { ...session, time: rest };
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* A legacy entry is the newest metadata its session has, including {}, so it
|
|
363
|
+
* replaces the upstream record until migration pushes it there.
|
|
364
|
+
*/
|
|
365
|
+
const withStoredMetadata = (session, stored) => {
|
|
366
|
+
if (!session || typeof session !== 'object' || typeof session.id !== 'string') return session;
|
|
367
|
+
const ours = stored[session.id];
|
|
368
|
+
if (!ours || typeof ours !== 'object' || Array.isArray(ours)) return session;
|
|
369
|
+
return { ...session, metadata: ours };
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
const overlaySession = (session, archived, stored) => {
|
|
373
|
+
let result = session;
|
|
374
|
+
if (archived) result = withArchivedAt(result, archived);
|
|
375
|
+
if (stored) result = withStoredMetadata(result, stored);
|
|
376
|
+
return result;
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
const overlayOwnedStateOnList = async (payload) => {
|
|
380
|
+
const records = sessionListRecords(payload);
|
|
381
|
+
if (!records) return payload;
|
|
382
|
+
const [archived, stored] = await Promise.all([readArchivedSessions(), readStoredSessionMetadata()]);
|
|
383
|
+
if (!archived && !stored) return payload;
|
|
384
|
+
const overlaid = records.map((session) => overlaySession(session, archived, stored));
|
|
385
|
+
return Array.isArray(payload) ? overlaid : { ...payload, data: overlaid };
|
|
386
|
+
};
|
|
387
|
+
|
|
294
388
|
if (app.get('opencodeProxyConfigured')) {
|
|
295
389
|
return;
|
|
296
390
|
}
|
|
@@ -412,15 +506,9 @@ export const registerOpenCodeProxy = (app, deps) => {
|
|
|
412
506
|
const PROXY_REQUEST_TIMEOUT_MS = normalizeProxyTimeout(LONG_REQUEST_TIMEOUT_MS);
|
|
413
507
|
const PROXY_TIMEOUT_MARKER = Symbol('openchamberProxyTimedOut');
|
|
414
508
|
|
|
415
|
-
//
|
|
416
|
-
//
|
|
417
|
-
//
|
|
418
|
-
// upstream expiry we know of — GitHub device codes last ~15 minutes.
|
|
419
|
-
const INTERACTIVE_OAUTH_TIMEOUT_MS = 15 * 60 * 1000;
|
|
420
|
-
const INTERACTIVE_OAUTH_PATH = /^\/provider\/[^/]+\/oauth\/callback\/?$/;
|
|
421
|
-
|
|
422
|
-
const isInteractiveOAuthCallback = (req) =>
|
|
423
|
-
req.method === 'POST' && INTERACTIVE_OAUTH_PATH.test(req.path);
|
|
509
|
+
// OpenCode 2.x runs provider connection through `/api/integration/*`, whose
|
|
510
|
+
// OAuth steps return immediately and are polled, so no route needs a deadline
|
|
511
|
+
// longer than the ordinary one any more.
|
|
424
512
|
|
|
425
513
|
const isProxyTimeoutError = (error) => {
|
|
426
514
|
const code = typeof error?.code === 'string' ? error.code : '';
|
|
@@ -440,10 +528,6 @@ export const registerOpenCodeProxy = (app, deps) => {
|
|
|
440
528
|
};
|
|
441
529
|
|
|
442
530
|
const applyProxyResponseDeadline = (req, res, next) => {
|
|
443
|
-
if (isInteractiveOAuthCallback(req)) {
|
|
444
|
-
return next();
|
|
445
|
-
}
|
|
446
|
-
|
|
447
531
|
const timeout = setTimeout(() => {
|
|
448
532
|
req[PROXY_TIMEOUT_MARKER] = true;
|
|
449
533
|
if (sendProxyErrorResponse(res, 504)) {
|
|
@@ -475,7 +559,7 @@ export const registerOpenCodeProxy = (app, deps) => {
|
|
|
475
559
|
const requestUrl = typeof req.originalUrl === 'string' && req.originalUrl.length > 0
|
|
476
560
|
? req.originalUrl
|
|
477
561
|
: (typeof req.url === 'string' ? req.url : '');
|
|
478
|
-
const upstreamPath = requestUrl
|
|
562
|
+
const upstreamPath = requestUrl;
|
|
479
563
|
const headers = normalizeForwardedDirectoryHeaders(
|
|
480
564
|
collectForwardProxyHeaders(req.headers, getOpenCodeAuthHeaders())
|
|
481
565
|
);
|
|
@@ -653,8 +737,9 @@ export const registerOpenCodeProxy = (app, deps) => {
|
|
|
653
737
|
const requestUrl = typeof req.originalUrl === 'string' && req.originalUrl.length > 0
|
|
654
738
|
? req.originalUrl
|
|
655
739
|
: (typeof req.url === 'string' ? req.url : '');
|
|
656
|
-
|
|
657
|
-
|
|
740
|
+
// OpenCode 2.x serves everything under `/api/*` itself, so the upstream
|
|
741
|
+
// path is the request path — nothing is stripped.
|
|
742
|
+
return canonicalizeDirectoryQuery(requestUrl);
|
|
658
743
|
};
|
|
659
744
|
|
|
660
745
|
const forwardSanitizedSessionListRequest = async (req, res, next, logLabel) => {
|
|
@@ -671,14 +756,14 @@ export const registerOpenCodeProxy = (app, deps) => {
|
|
|
671
756
|
return;
|
|
672
757
|
}
|
|
673
758
|
|
|
674
|
-
if (result.parseError || !
|
|
759
|
+
if (result.parseError || !sessionListRecords(result.payload)) {
|
|
675
760
|
res.setHeader('content-type', result.contentType);
|
|
676
761
|
res.end(result.bodyText);
|
|
677
762
|
return;
|
|
678
763
|
}
|
|
679
764
|
|
|
680
765
|
res.setHeader('content-type', result.contentType);
|
|
681
|
-
res.json(sanitizeSessionListPayload(result.payload));
|
|
766
|
+
res.json(await overlayOwnedStateOnList(sanitizeSessionListPayload(result.payload)));
|
|
682
767
|
} catch (error) {
|
|
683
768
|
if (isAbortError(error)) {
|
|
684
769
|
return;
|
|
@@ -719,7 +804,7 @@ export const registerOpenCodeProxy = (app, deps) => {
|
|
|
719
804
|
const classifyReadinessRoute = (requestPath) => {
|
|
720
805
|
if (/^\/session\/[^/]+\/message(?:\/|$)/.test(requestPath)) return 'session-messages';
|
|
721
806
|
if (requestPath === '/session' || requestPath.startsWith('/session/')) return 'session';
|
|
722
|
-
if (requestPath === '/event'
|
|
807
|
+
if (requestPath === '/event') return 'events';
|
|
723
808
|
return 'other';
|
|
724
809
|
};
|
|
725
810
|
|
|
@@ -808,103 +893,68 @@ export const registerOpenCodeProxy = (app, deps) => {
|
|
|
808
893
|
}
|
|
809
894
|
});
|
|
810
895
|
|
|
811
|
-
//
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
if (rawUrl.includes('directory=')) return next();
|
|
816
|
-
|
|
817
|
-
const fetchWindowsSessionList = async (sessionPath) => {
|
|
818
|
-
const result = await fetchSessionListPayload(sessionPath, { req, timeoutMs: 10000 });
|
|
819
|
-
if (!result.upstream.ok || !Array.isArray(result.payload)) return null;
|
|
820
|
-
return sanitizeSessionListPayload(result.payload);
|
|
821
|
-
};
|
|
822
|
-
|
|
823
|
-
try {
|
|
824
|
-
const globalSessions = await fetchWindowsSessionList('/session').catch((error) => {
|
|
825
|
-
console.log(`[SessionMerge] Global session list failed: ${error.message}`);
|
|
826
|
-
return null;
|
|
827
|
-
});
|
|
896
|
+
// V2 lists sessions across directories on every platform and owns pagination.
|
|
897
|
+
app.get('/api/session', (req, res, next) => {
|
|
898
|
+
return forwardSanitizedSessionListRequest(req, res, next, 'session.list');
|
|
899
|
+
});
|
|
828
900
|
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
.filter(Boolean);
|
|
837
|
-
} catch {
|
|
838
|
-
}
|
|
901
|
+
// One session: the same overlay, so a detail read agrees with the list it
|
|
902
|
+
// came from. Everything else about the record is forwarded untouched.
|
|
903
|
+
app.get('/api/session/:sessionID', async (req, res, next) => {
|
|
904
|
+
if (typeof getArchivedSessions !== 'function' && typeof getStoredSessionMetadata !== 'function') return next();
|
|
905
|
+
try {
|
|
906
|
+
const upstreamPath = await getRequestUpstreamPath(req);
|
|
907
|
+
const result = await fetchSessionListPayload(upstreamPath, { req });
|
|
839
908
|
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
.filter((id) => typeof id === 'string')
|
|
844
|
-
);
|
|
845
|
-
const extraSessions = [];
|
|
846
|
-
let successfulProjectReads = 0;
|
|
847
|
-
for (const dir of projectDirs) {
|
|
848
|
-
const candidates = Array.from(new Set([
|
|
849
|
-
dir,
|
|
850
|
-
dir.replace(/\\/g, '/'),
|
|
851
|
-
dir.replace(/\//g, '\\'),
|
|
852
|
-
]));
|
|
853
|
-
for (const candidateDir of candidates) {
|
|
854
|
-
const encoded = encodeURIComponent(candidateDir);
|
|
855
|
-
try {
|
|
856
|
-
const dirSessions = await fetchWindowsSessionList(`/session?directory=${encoded}`);
|
|
857
|
-
if (dirSessions) {
|
|
858
|
-
successfulProjectReads += 1;
|
|
859
|
-
}
|
|
860
|
-
for (const session of dirSessions || []) {
|
|
861
|
-
const id = session && typeof session.id === 'string' ? session.id : null;
|
|
862
|
-
if (id && !seen.has(id)) {
|
|
863
|
-
seen.add(id);
|
|
864
|
-
extraSessions.push(session);
|
|
865
|
-
}
|
|
866
|
-
}
|
|
867
|
-
} catch {
|
|
868
|
-
}
|
|
869
|
-
}
|
|
870
|
-
}
|
|
909
|
+
res.status(result.upstream.status);
|
|
910
|
+
applyForwardProxyResponseHeaders(result.upstream.headers, res);
|
|
911
|
+
res.setHeader('content-type', result.contentType);
|
|
871
912
|
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
913
|
+
const record = result.isJson && !result.parseError ? result.payload : null;
|
|
914
|
+
const session = record && typeof record === 'object' && !Array.isArray(record)
|
|
915
|
+
? (record.data && typeof record.data === 'object' ? record.data : record)
|
|
916
|
+
: null;
|
|
917
|
+
if (!session || typeof session.id !== 'string') {
|
|
918
|
+
res.end(result.bodyText);
|
|
919
|
+
return;
|
|
920
|
+
}
|
|
875
921
|
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
return bTime - aTime;
|
|
881
|
-
});
|
|
882
|
-
console.log(`[SessionMerge] ${globalSessions?.length || 0} global + ${extraSessions.length} extra = ${merged.length} total`);
|
|
883
|
-
return res.json(sanitizeSessionListPayload(merged));
|
|
884
|
-
} catch (error) {
|
|
885
|
-
console.log(`[SessionMerge] Error: ${error.message}`);
|
|
886
|
-
return res.status(500).json({ error: error.message || 'Failed to merge Windows sessions' });
|
|
922
|
+
const [archived, stored] = await Promise.all([readArchivedSessions(), readStoredSessionMetadata()]);
|
|
923
|
+
if (!archived && !stored) {
|
|
924
|
+
res.end(result.bodyText);
|
|
925
|
+
return;
|
|
887
926
|
}
|
|
888
|
-
});
|
|
889
|
-
}
|
|
890
927
|
|
|
891
|
-
|
|
892
|
-
|
|
928
|
+
const overlaid = overlaySession(session, archived, stored);
|
|
929
|
+
res.json(record.data && typeof record.data === 'object' ? { ...record, data: overlaid } : overlaid);
|
|
930
|
+
} catch (error) {
|
|
931
|
+
if (isAbortError(error)) return;
|
|
932
|
+
console.error('[proxy] OpenCode session.get proxy error:', error?.message ?? error);
|
|
933
|
+
if (!res.headersSent) {
|
|
934
|
+
res.status(503).json({ error: 'OpenCode service unavailable' });
|
|
935
|
+
return;
|
|
936
|
+
}
|
|
937
|
+
next(error);
|
|
938
|
+
}
|
|
893
939
|
});
|
|
894
940
|
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
941
|
+
// v2 has one event stream. `/api/global/event` stays as an alias so a client
|
|
942
|
+
// that has not reloaded yet keeps working; both reach upstream `/api/event`.
|
|
943
|
+
app.get('/api/global/event', (req, res, next) => {
|
|
944
|
+
req.url = req.url.replace('/api/global/event', '/api/event');
|
|
945
|
+
if (typeof req.originalUrl === 'string') {
|
|
946
|
+
req.originalUrl = req.originalUrl.replace('/api/global/event', '/api/event');
|
|
947
|
+
}
|
|
948
|
+
return forwardSseRequest(req, res, next);
|
|
900
949
|
});
|
|
950
|
+
app.get('/api/event', forwardSseRequest);
|
|
901
951
|
|
|
902
952
|
// Generic proxy for non-SSE OpenCode API routes.
|
|
903
953
|
// The agent is exposed as a getter so its class is resolved per request, not
|
|
904
954
|
// at registration: the proxy is registered before OpenCode bootstraps, so an
|
|
905
955
|
// https target configured via OPENCODE_HOST is not yet visible here. Agents
|
|
906
956
|
// are memoized per scheme, so this is still one shared pool per scheme across
|
|
907
|
-
// `apiProxy
|
|
957
|
+
// `apiProxy`.
|
|
908
958
|
const resolveOpenCodeProxyAgent = createOpenCodeProxyAgentResolver(resolveProxyTarget);
|
|
909
959
|
|
|
910
960
|
const createApiProxy = (timeoutMs) => createProxyMiddleware({
|
|
@@ -913,9 +963,12 @@ export const registerOpenCodeProxy = (app, deps) => {
|
|
|
913
963
|
return resolveOpenCodeProxyAgent();
|
|
914
964
|
},
|
|
915
965
|
changeOrigin: true,
|
|
916
|
-
pathRewrite: { '^/api': '' },
|
|
917
966
|
timeout: timeoutMs,
|
|
918
967
|
proxyTimeout: timeoutMs,
|
|
968
|
+
// The proxy is mounted on `/api`, so Express has already stripped that
|
|
969
|
+
// prefix by the time the middleware sees the request. OpenCode 2.x serves
|
|
970
|
+
// everything under `/api/*` itself, so put it back.
|
|
971
|
+
pathRewrite: (proxiedPath) => `/api${proxiedPath === '/' ? '' : proxiedPath}`,
|
|
919
972
|
// Dynamic target — port can change after restart
|
|
920
973
|
router: () => resolveProxyTarget(),
|
|
921
974
|
on: {
|
|
@@ -963,7 +1016,6 @@ export const registerOpenCodeProxy = (app, deps) => {
|
|
|
963
1016
|
});
|
|
964
1017
|
|
|
965
1018
|
const apiProxy = createApiProxy(PROXY_REQUEST_TIMEOUT_MS);
|
|
966
|
-
const interactiveOAuthProxy = createApiProxy(INTERACTIVE_OAUTH_TIMEOUT_MS);
|
|
967
1019
|
|
|
968
1020
|
// Best-effort fallback for stale clients still sending symlink paths.
|
|
969
1021
|
// Settings and project selection normalize at source; this cached async path
|
|
@@ -981,10 +1033,8 @@ export const registerOpenCodeProxy = (app, deps) => {
|
|
|
981
1033
|
});
|
|
982
1034
|
|
|
983
1035
|
app.use('/api', applyProxyResponseDeadline);
|
|
984
|
-
|
|
985
|
-
//
|
|
986
|
-
//
|
|
987
|
-
// timeout), so it needs the interactive-OAuth deadline, not the default one.
|
|
988
|
-
app.post('/api/mcp/:name/auth/authenticate', interactiveOAuthProxy);
|
|
1036
|
+
// v1's interactive provider/MCP OAuth callbacks are gone: v2 runs provider
|
|
1037
|
+
// connection through `/api/integration/*`, which answers immediately and
|
|
1038
|
+
// needs no special deadline.
|
|
989
1039
|
app.use('/api', apiProxy);
|
|
990
1040
|
};
|
|
@@ -101,7 +101,7 @@ export const registerPwaManifestRoute = (app, dependencies) => {
|
|
|
101
101
|
return `?directory=${encodeURIComponent(preparedDirectory)}`;
|
|
102
102
|
})();
|
|
103
103
|
|
|
104
|
-
const response = await fetch(buildOpenCodeUrl(`/session${query}`, ''), {
|
|
104
|
+
const response = await fetch(buildOpenCodeUrl(`/api/session${query}`, ''), {
|
|
105
105
|
method: 'GET',
|
|
106
106
|
headers: {
|
|
107
107
|
Accept: 'application/json',
|
|
@@ -114,7 +114,9 @@ export const registerPwaManifestRoute = (app, dependencies) => {
|
|
|
114
114
|
return [];
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
-
|
|
117
|
+
// v2 pages the session list as `{ data, cursor }`.
|
|
118
|
+
const body = await response.json().catch(() => null);
|
|
119
|
+
const payload = Array.isArray(body) ? body : body?.data;
|
|
118
120
|
return Array.isArray(payload) ? payload : [];
|
|
119
121
|
};
|
|
120
122
|
|
|
@@ -41,9 +41,10 @@ describe('PWA manifest route', () => {
|
|
|
41
41
|
time: { updated: 2 },
|
|
42
42
|
},
|
|
43
43
|
];
|
|
44
|
+
// v2 pages the session list as `{ data, cursor }`.
|
|
44
45
|
return {
|
|
45
46
|
ok: true,
|
|
46
|
-
json: async () => sessions,
|
|
47
|
+
json: async () => ({ data: sessions, cursor: {} }),
|
|
47
48
|
};
|
|
48
49
|
};
|
|
49
50
|
|
|
@@ -118,7 +119,7 @@ describe('PWA manifest route', () => {
|
|
|
118
119
|
await handler({ query: {} }, res);
|
|
119
120
|
|
|
120
121
|
const manifest = JSON.parse(res.body);
|
|
121
|
-
expect(fetchCalls).toEqual(['/session?directory=%2F']);
|
|
122
|
+
expect(fetchCalls).toEqual(['/api/session?directory=%2F']);
|
|
122
123
|
expect(manifest.shortcuts).toContainEqual({
|
|
123
124
|
name: 'Root child',
|
|
124
125
|
short_name: 'Root child',
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCode 2.x wraps `/api/*` answers in an envelope, and not always the same
|
|
3
|
+
* one: a single record (`GET /api/session/:id`, one message) comes back as
|
|
4
|
+
* `{ data }`, some routes (`/api/command`) as `{ location, data }`, and pages
|
|
5
|
+
* as `{ data, cursor }`. Records and plain lists are unwrapped; a page keeps
|
|
6
|
+
* its envelope because callers read its cursor.
|
|
7
|
+
*
|
|
8
|
+
* Unwrapping only when `location` was present left record envelopes in place,
|
|
9
|
+
* so a session's `parentID` and a message's `id` read as missing.
|
|
10
|
+
*/
|
|
11
|
+
export const unwrapOpenCodeResponse = (body) => {
|
|
12
|
+
if (!body || typeof body !== 'object' || Array.isArray(body)) return body;
|
|
13
|
+
return 'data' in body && !('cursor' in body) ? body.data : body;
|
|
14
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { unwrapOpenCodeResponse } from './response-envelope.js';
|
|
4
|
+
|
|
5
|
+
describe('unwrapOpenCodeResponse', () => {
|
|
6
|
+
it('unwraps a single record sent as { data } alone', () => {
|
|
7
|
+
expect(unwrapOpenCodeResponse({ data: { id: 'ses_1', parentID: 'ses_0' } })).toEqual({ id: 'ses_1', parentID: 'ses_0' });
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it('unwraps a { location, data } answer', () => {
|
|
11
|
+
expect(unwrapOpenCodeResponse({ location: { directory: '/repo' }, data: [{ name: 'review' }] })).toEqual([{ name: 'review' }]);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('keeps a page envelope so its cursor stays readable', () => {
|
|
15
|
+
const page = { data: [{ id: 'ses_1' }], cursor: { next: 'c1' } };
|
|
16
|
+
expect(unwrapOpenCodeResponse(page)).toBe(page);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('passes through what is not an envelope', () => {
|
|
20
|
+
expect(unwrapOpenCodeResponse(null)).toBe(null);
|
|
21
|
+
expect(unwrapOpenCodeResponse([1])).toEqual([1]);
|
|
22
|
+
});
|
|
23
|
+
});
|