@openchambery/web 1.19.22-beta.1 → 2.0.0-beta.3
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 +13 -7
- package/bin/cli-opencode.test.js +208 -0
- package/bin/cli.js +62 -9
- package/bin/lib/cli-startup.js +105 -2
- package/bin/lib/cli-startup.test.js +179 -0
- package/dist/assets/{AgentAvatar-CRaBJXKs.js → AgentAvatar-B_JQoreg.js} +1 -1
- package/dist/assets/AssistantView-ClQAfGOT.js +3 -0
- package/dist/assets/{DiagramView-D1LGK_s2.js → DiagramView-BNnda8qO.js} +1 -1
- package/dist/assets/{FadeInOnReveal-B0kJo9Ii.js → FadeInOnReveal-VYQTSAkr.js} +1 -1
- package/dist/assets/{Icon-BgdfPhNs.js → Icon-BiEQEcih.js} +1 -1
- package/dist/assets/MarkdownRenderer-CJHSsWin.js +2 -0
- package/dist/assets/{MarkdownRendererImpl-DfoCJ1Mq.js → MarkdownRendererImpl-DwrHFGUp.js} +3 -3
- package/dist/assets/{MarkstreamRendererImpl-D2dsW5Lg.js → MarkstreamRendererImpl-CSnbQMu3.js} +1 -1
- package/dist/assets/MobileDetailNavigation-BXr6AGLT.js +1 -0
- package/dist/assets/{MobileShareBridge-2Yjn6wXg.js → MobileShareBridge-D7OE9OP_.js} +2 -2
- package/dist/assets/MobileSurface-D2jUkX9l.js +2 -0
- package/dist/assets/{MultiRunWindow-tTbMFowS.js → MultiRunWindow-DF3iIFCd.js} +1 -1
- package/dist/assets/{OnboardingScreen-CD2KtlF3.js → OnboardingScreen-BjHmlwlw.js} +1 -1
- package/dist/assets/OverlayScrollbar-DLtWO_Gp.js +2 -0
- package/dist/assets/SettingsView-BRxELjow.js +25 -0
- package/dist/assets/{SettingsWindow-zIa76s4t.js → SettingsWindow-BI5swspb.js} +1 -1
- package/dist/assets/{TerminalView-CDdzF4yi.js → TerminalView-BU3x8-fr.js} +1 -1
- package/dist/assets/{ToolOutputDialog-DNqA4uBl.js → ToolOutputDialog-BEWhBcRd.js} +2 -2
- package/dist/assets/appThemeRegistry-4MR0F7Jz.js +1 -0
- package/dist/assets/{button-CI7Q-Zw_.js → button-DKuz3L8l.js} +1 -1
- package/dist/assets/collapsible-DXbDynN4.js +3 -0
- package/dist/assets/context-menu-CqolsLCm.js +1 -0
- package/dist/assets/{desktopHosts-B1ixEylr.js → desktopHosts-CoDkOnyS.js} +1 -1
- package/dist/assets/{desktopRelayRestore-4JmFUw8r.js → desktopRelayRestore-DDlVKyOE.js} +1 -1
- package/dist/assets/{diagram-DdLO8HZ9.js → diagram-D7T8HQgj.js} +1 -1
- package/dist/assets/{dialog-B6pnIYmp.js → dialog-DQYl3W_o.js} +1 -1
- package/dist/assets/es-CN3E_zXj.js +15 -0
- package/dist/assets/{fileMentionTouchSelection-DNeiqns-.js → fileMentionTouchSelection-BZ6ovBYH.js} +1 -1
- package/dist/assets/{fileQueries-DQc8EJUf.js → fileQueries-JrpcsOCs.js} +1 -1
- package/dist/assets/fr-9zZvQZD2.js +13 -0
- package/dist/assets/gitApi-CM42qUed.js +10 -0
- package/dist/assets/{imageSource-BlAJJUEI.js → imageSource-CIl0FL10.js} +1 -1
- package/dist/assets/{input-D-__fwRi.js → input-CzQy8UhV.js} +1 -1
- package/dist/assets/insertionBoundaries-CtkZ9720.js +1 -0
- package/dist/assets/ja-Dnjk_82N.js +15 -0
- package/dist/assets/ko-BxCEE99C.js +15 -0
- package/dist/assets/main-BILgBI3B.js +169 -0
- package/dist/assets/main-BeveiW6F.js +2 -0
- package/dist/assets/markdown-worker-bS5OgGO3.js +2 -0
- package/dist/assets/{markdownHeightCache-BhxqJOvf.js → markdownHeightCache-Cj1I5NDw.js} +1 -1
- package/dist/assets/{markdownTheme-DOfGg-A7.js → markdownTheme-CKac3wKC.js} +1 -1
- package/dist/assets/mcpQueries-D5mQymgM.js +1 -0
- package/dist/assets/miniChat-BgrzM3u7.js +2 -0
- package/dist/assets/mobile-DtYoQE8C.js +2 -0
- package/dist/assets/multirun-jy8dEI96.js +4 -0
- package/dist/assets/{persistence-Ce3AnpEr.js → persistence-C9LQpiOS.js} +2 -2
- package/dist/assets/pl-CNNsWnPM.js +15 -0
- package/dist/assets/platform-BQghHRfp.js +6 -0
- package/dist/assets/pt-BR-BZB2DJMp.js +15 -0
- package/dist/assets/renderElectronMiniChatApp-BGrrmeME.js +2 -0
- package/dist/assets/renderMobileApp-u3c4GtFC.js +28 -0
- package/dist/assets/{runtimeConfig-CMkwoQ_X.js → runtimeConfig-BIGBRGQI.js} +1 -1
- package/dist/assets/{runtimeEndpointReset-ChOxt-J7.js → runtimeEndpointReset-D2savkuw.js} +12 -12
- package/dist/assets/{sessionChangeSummary-NnxcqCTI.js → sessionChangeSummary-eIbpSfrO.js} +1 -1
- package/dist/assets/{sessionLookup-BrU3rFHJ.js → sessionLookup-myHe5ltu.js} +1 -1
- package/dist/assets/{sortable-tabs-strip-Fnfhf3K9.js → sortable-tabs-strip-BQtmHx9r.js} +1 -1
- package/dist/assets/src-CLN7Z-zx.css +2 -0
- package/dist/assets/store-DLCiZaA6.js +15 -0
- package/dist/assets/{terminalApi-xjqssKke.js → terminalApi-BxcdiduX.js} +1 -1
- package/dist/assets/{textarea-bHYxZtfH.js → textarea-CakKvqFw.js} +1 -1
- package/dist/assets/{tooltip-0T5jv0cu.js → tooltip-Cx5gTbdY.js} +1 -1
- package/dist/assets/uk-rwQrxeYM.js +15 -0
- package/dist/assets/{url-_z7evzdK.js → url-CDHnjlsm.js} +1 -1
- package/dist/assets/{useAppFontEffects-C02YFyAv.js → useAppFontEffects-CNeo0aZs.js} +59 -45
- package/dist/assets/{useAssistantUIStore-CMutVh9B.js → useAssistantUIStore-asNG6K7n.js} +1 -1
- package/dist/assets/useDesktopWindowControlsLayout-DBJ56qIo.js +1 -0
- package/dist/assets/useEffectiveDirectory-B4LvR8Gt.js +1 -0
- package/dist/assets/{useFontPreferences-B1JYyzpK.js → useFontPreferences-CUnQ4TVm.js} +1 -1
- package/dist/assets/useMobileNavigationStore-ZHJPxlHt.js +414 -0
- package/dist/assets/{useRuntimeAPIs-DSLXJybW.js → useRuntimeAPIs-DwLtO0c_.js} +1 -1
- package/dist/assets/useSessionAutoCleanup-BIVmrnYU.js +2 -0
- package/dist/assets/{useThemeSystem-DMzWVJaW.js → useThemeSystem-BlNUWH47.js} +1 -1
- package/dist/assets/useUIStore-CQYKJnrg.js +1 -0
- package/dist/assets/{useWorkerHighlightedLines-C9z0ML1z.js → useWorkerHighlightedLines-CJZbJDW3.js} +2 -2
- package/dist/assets/{vendor-base-ui-CnHfUmDN.js → vendor-base-ui-CDjyofeD.js} +1 -1
- package/dist/assets/{vendor-cmdk-CeAGfrLo.js → vendor-cmdk-BFH0UpXX.js} +1 -1
- package/dist/assets/{vendor-dnd-kit-accessibility-BY5LhhUB.js → vendor-dnd-kit-accessibility-BrR3AKvM.js} +1 -1
- package/dist/assets/{vendor-dnd-kit-core-CvWxUPdb.js → vendor-dnd-kit-core-CNfdvk6H.js} +1 -1
- package/dist/assets/{vendor-dnd-kit-sortable-hHdP9lLb.js → vendor-dnd-kit-sortable-DQUaTeGj.js} +1 -1
- package/dist/assets/{vendor-framer-motion-DFy_BSVd.js → vendor-framer-motion-4dogsUk3.js} +1 -1
- package/dist/assets/{vendor-legendapp-list-WE32lwNk.js → vendor-legendapp-list-BU7MdPZR.js} +1 -1
- package/dist/assets/{vendor-markstream-react-DX55XLGO.js → vendor-markstream-react-qHC2FykU.js} +3 -3
- package/dist/assets/vendor-opencode-client-BOe2qpQb.js +12 -0
- package/dist/assets/{vendor-pierre-diffs-DX7XTPG3.js → vendor-pierre-diffs-CUfqC003.js} +1 -1
- package/dist/assets/{vendor-react-DLPElpoQ.js → vendor-react-C94A0AHl.js} +1 -1
- package/dist/assets/{vendor-react-drawio-ECtEwvbc.js → vendor-react-drawio-FVlWqKVE.js} +1 -1
- package/dist/assets/{vendor-reactuses-core-BnW6GxJt.js → vendor-reactuses-core-C2A3EjUi.js} +1 -1
- package/dist/assets/{vendor-sonner-DVzioF3d.js → vendor-sonner-DNqHmUQs.js} +1 -1
- package/dist/assets/{vendor-stream-diffs-DWy3xSmp.js → vendor-stream-diffs-CwbKsmlv.js} +2 -2
- package/dist/assets/{vendor-tanstack-react-query-D7pys_c9.js → vendor-tanstack-react-query-D0iFK57C.js} +1 -1
- package/dist/assets/{vendor-tanstack-react-virtual-tlDgH6PG.js → vendor-tanstack-react-virtual-B2kV6cnT.js} +1 -1
- package/dist/assets/{vendor-yaml-DIcAK3EC.js → vendor-yaml-C9KlJAlj.js} +37 -37
- package/dist/assets/{vendor-zustand-B4dlGEM2.js → vendor-zustand-BqLKf6ZY.js} +1 -1
- package/dist/assets/zh-CN-BvmBDnkD.js +15 -0
- package/dist/assets/zh-TW-9wcHTX0y.js +15 -0
- package/dist/index.html +13 -13
- package/dist/mini-chat.html +13 -13
- package/dist/mobile.html +13 -13
- package/package.json +2 -2
- package/server/index.js +392 -20
- package/server/lib/assistants/DOCUMENTATION.md +26 -21
- package/server/lib/assistants/assign.js +2 -1
- package/server/lib/assistants/contact-store.js +37 -19
- package/server/lib/assistants/contracts.js +11 -1
- package/server/lib/assistants/projection-history.test.js +454 -0
- package/server/lib/assistants/routes.js +11 -5
- package/server/lib/assistants/service.js +850 -294
- package/server/lib/assistants/service.test.js +588 -274
- package/server/lib/conversations/DOCUMENTATION.md +5 -5
- package/server/lib/conversations/service.js +87 -57
- package/server/lib/conversations/service.test.js +132 -100
- package/server/lib/conversations/validation.js +11 -1
- package/server/lib/event-stream/directory-ws-bridge.js +13 -2
- package/server/lib/event-stream/global-hub.js +51 -5
- package/server/lib/event-stream/runtime.js +57 -20
- package/server/lib/event-stream/runtime.test.js +112 -0
- package/server/lib/llm/DOCUMENTATION.md +95 -84
- package/server/lib/llm/catalog.js +111 -45
- package/server/lib/llm/catalog.test.js +121 -59
- package/server/lib/llm/completions.js +41 -12
- package/server/lib/llm/completions.test.js +45 -4
- package/server/lib/llm/generate.js +516 -453
- package/server/lib/llm/generate.test.js +705 -484
- package/server/lib/llm/routes.js +8 -4
- package/server/lib/message-queue/DOCUMENTATION.md +5 -5
- package/server/lib/message-queue/opencode-adapter.js +191 -104
- package/server/lib/message-queue/opencode-adapter.test.js +328 -58
- package/server/lib/message-queue/runtime.js +15 -2
- package/server/lib/message-queue/runtime.test.js +6 -6
- package/server/lib/message-queue/worker.js +7 -1
- package/server/lib/notifications/runtime.js +5 -0
- package/server/lib/opencode/DOCUMENTATION.md +103 -16
- package/server/lib/opencode/agent-document.d.ts +28 -0
- package/server/lib/opencode/agent-document.js +280 -0
- package/server/lib/opencode/agent-document.test.js +137 -0
- package/server/lib/opencode/agents.js +96 -0
- package/server/lib/opencode/auth-state-runtime.js +5 -0
- package/server/lib/opencode/bootstrap-runtime.js +2 -0
- package/server/lib/opencode/config-entity-routes.js +111 -102
- package/server/lib/opencode/config-entity-routes.test.js +123 -10
- package/server/lib/opencode/config-hot-reload.integration.test.js +105 -0
- package/server/lib/opencode/ensure-cli.js +238 -0
- package/server/lib/opencode/ensure-cli.test.js +119 -0
- package/server/lib/opencode/env-runtime.js +208 -111
- package/server/lib/opencode/env-runtime.test.js +211 -14
- package/server/lib/opencode/feature-routes-runtime.js +80 -5
- package/server/lib/opencode/feature-routes-runtime.test.js +18 -0
- package/server/lib/opencode/http-server-connections.js +98 -0
- package/server/lib/opencode/http-server-connections.test.js +321 -0
- package/server/lib/opencode/lifecycle.js +539 -95
- package/server/lib/opencode/lifecycle.test.js +698 -6
- package/server/lib/opencode/managed-capabilities-runtime.js +15 -2
- package/server/lib/opencode/managed-capabilities-runtime.test.js +1 -0
- package/server/lib/opencode/network-runtime.js +28 -16
- package/server/lib/opencode/network-runtime.test.js +68 -4
- package/server/lib/opencode/openchamber-routes.js +7 -1
- package/server/lib/opencode/opencode2-pin.js +139 -0
- package/server/lib/opencode/opencode2-pin.test.js +88 -0
- package/server/lib/opencode/opencode2-sidecar.e2e.test.js +326 -0
- package/server/lib/opencode/owned-runtime-upgrade.js +431 -0
- package/server/lib/opencode/owned-runtime-upgrade.test.js +160 -0
- package/server/lib/opencode/plugin-compatibility.js +15 -0
- package/server/lib/opencode/plugin-compatibility.test.js +21 -0
- package/server/lib/opencode/plugin-routes.js +3 -18
- package/server/lib/opencode/plugin-routes.test.js +9 -9
- package/server/lib/opencode/plugins.js +4 -3
- package/server/lib/opencode/plugins.test.js +12 -0
- package/server/lib/opencode/provider-catalog.js +58 -12
- package/server/lib/opencode/provider-catalog.test.js +48 -0
- package/server/lib/opencode/proxy.js +368 -28
- package/server/lib/opencode/proxy.test.js +338 -0
- package/server/lib/opencode/routes.js +303 -76
- package/server/lib/opencode/routes.test.js +315 -50
- package/server/lib/opencode/runtime-contract.js +401 -0
- package/server/lib/opencode/runtime-contract.test.js +214 -0
- package/server/lib/opencode/server-opencode-fetch.js +242 -0
- package/server/lib/opencode/server-opencode-fetch.test.js +130 -0
- package/server/lib/opencode/server-utils-runtime.js +35 -0
- package/server/lib/opencode/shared-service.integration.test.js +66 -0
- package/server/lib/opencode/shared-service.js +147 -0
- package/server/lib/opencode/shared-service.test.js +93 -0
- package/server/lib/opencode/shared.js +3 -3
- package/server/lib/opencode/shutdown-runtime.js +15 -6
- package/server/lib/opencode/shutdown-runtime.test.js +49 -1
- package/server/lib/opencode/skill-routes.js +26 -40
- package/server/lib/opencode/skill-routes.test.js +71 -7
- package/server/lib/opencode/skills.test.js +15 -0
- package/server/lib/opencode/startup-pipeline-runtime.js +4 -0
- package/server/lib/opencode/static-routes-runtime.js +3 -1
- package/server/lib/opencode/static-routes-runtime.test.js +33 -0
- package/server/lib/opencode/v1-migration-gate.js +157 -0
- package/server/lib/opencode/v1-migration-gate.test.js +111 -0
- package/server/lib/opencode/v2-client.js +60 -0
- package/server/lib/opencode/v2-plugin-host-shim/index.mjs +155 -0
- package/server/lib/opencode/v2-plugin-host-shim/package.json +4 -0
- package/server/lib/opencode/watcher.js +2 -1
- package/server/lib/opencode/watcher.test.js +1 -1
- package/server/lib/permission-auto-accept/DOCUMENTATION.md +5 -5
- package/server/lib/permission-auto-accept/runtime.js +14 -7
- package/server/lib/permission-auto-accept/runtime.test.js +27 -12
- package/server/lib/relay/reasoning-projection.e2e.test.ts +22 -7
- package/server/lib/scheduled-tasks/DOCUMENTATION.md +79 -39
- package/server/lib/scheduled-tasks/managed-tool-route.js +31 -1
- package/server/lib/scheduled-tasks/managed-tool-route.test.js +23 -0
- package/server/lib/scheduled-tasks/routes.js +27 -0
- package/server/lib/scheduled-tasks/routes.test.js +94 -0
- package/server/lib/scheduled-tasks/runtime.js +381 -310
- package/server/lib/scheduled-tasks/runtime.test.js +600 -309
- package/server/lib/session-goal/DOCUMENTATION.md +173 -22
- package/server/lib/session-goal/capability.js +57 -0
- package/server/lib/session-goal/capability.test.js +33 -0
- package/server/lib/session-goal/routes.js +240 -2
- package/server/lib/session-goal/routes.test.js +211 -0
- package/server/lib/session-goal/runtime.js +1617 -113
- package/server/lib/session-goal/runtime.test.js +1500 -70
- package/server/lib/session-index/DOCUMENTATION.md +18 -15
- package/server/lib/session-index/event-ingest.js +89 -16
- package/server/lib/session-index/event-ingest.test.js +73 -0
- package/server/lib/session-index/host-authority-snapshot.js +92 -0
- package/server/lib/session-index/routes.host-archive.test.js +201 -0
- package/server/lib/session-index/routes.js +63 -10
- package/server/lib/session-index/routes.test.js +8 -8
- package/server/lib/session-index/service.js +7 -1
- package/server/lib/session-index/sync-runtime.js +171 -41
- package/server/lib/session-index/sync-runtime.test.js +164 -9
- package/server/lib/session-metadata/DOCUMENTATION.md +86 -0
- package/server/lib/session-metadata/routes.js +193 -0
- package/server/lib/session-metadata/routes.test.js +156 -0
- package/server/lib/session-metadata/session-archive.d.ts +73 -0
- package/server/lib/session-metadata/session-archive.js +654 -0
- package/server/lib/session-metadata/session-archive.test.js +359 -0
- package/server/lib/session-metadata/session-metadata-store.d.ts +36 -0
- package/server/lib/session-metadata/session-metadata-store.js +375 -0
- package/server/lib/session-metadata/session-metadata-store.test.js +379 -0
- package/server/lib/session-metadata/session-projection.d.ts +41 -0
- package/server/lib/session-metadata/session-projection.js +255 -0
- package/server/lib/session-metadata/session-projection.test.js +64 -0
- package/server/lib/session-metadata/system-session.js +55 -0
- package/server/lib/session-metadata/system-session.test.js +55 -0
- package/server/lib/session-title/DOCUMENTATION.md +40 -7
- package/server/lib/session-title/native-runtime.test.js +167 -0
- package/server/lib/session-title/runtime.js +67 -42
- package/server/lib/session-title/session-access.js +46 -0
- package/server/lib/session-title/session-access.test.js +91 -0
- package/server/lib/session-turn-pages/DOCUMENTATION.md +12 -11
- package/server/lib/session-turn-pages/reconcile.service.js +1 -1
- package/server/lib/session-turn-pages/reconcile.service.test.js +16 -0
- package/server/lib/session-turn-pages/routes.js +123 -75
- package/server/lib/session-turn-pages/routes.test.js +566 -0
- package/server/lib/session-turn-pages/service.js +6 -2
- package/server/lib/session-turn-pages/service.test.js +34 -0
- package/server/lib/session-turn-pages/session-message-projection.d.ts +13 -0
- package/server/lib/session-turn-pages/session-message-projection.js +281 -0
- package/server/lib/small-model/DOCUMENTATION.md +101 -146
- package/server/lib/small-model/catalog.js +84 -54
- package/server/lib/small-model/catalog.test.js +111 -9
- package/server/lib/small-model/custom-api.js +68 -0
- package/server/lib/small-model/custom-api.test.js +64 -0
- package/server/lib/small-model/index.js +125 -241
- package/server/lib/small-model/resolve.js +35 -120
- package/server/lib/small-model/resolve.test.js +20 -110
- package/server/lib/small-model/routes.js +1 -3
- package/server/lib/small-model/summary-settings.test.js +157 -253
- package/server/opencode-proxy.test.js +63 -12
- package/dist/assets/AssistantView-BUveAgc4.js +0 -3
- package/dist/assets/MarkdownRenderer-BbrbDH6-.js +0 -2
- package/dist/assets/MobileDetailNavigation-B0aqKXd7.js +0 -1
- package/dist/assets/MobileSurface-DW7WfPsy.js +0 -2
- package/dist/assets/OverlayScrollbar-DrA0Vsg5.js +0 -2
- package/dist/assets/SettingsView-PkXIavr7.js +0 -25
- package/dist/assets/appThemeRegistry-D5Yxg4YF.js +0 -1
- package/dist/assets/collapsible-D5Phaykh.js +0 -1
- package/dist/assets/context-menu-CubSDW2g.js +0 -1
- package/dist/assets/es-DA3MqOo0.js +0 -15
- package/dist/assets/fr-Dbz0CdIA.js +0 -13
- package/dist/assets/gitApi-DXyC4MiV.js +0 -7
- package/dist/assets/insertionBoundaries-KlOVrHHt.js +0 -1
- package/dist/assets/ja-DumanVQA.js +0 -15
- package/dist/assets/ko-C0IkiUjQ.js +0 -15
- package/dist/assets/main-BPS5ylib.js +0 -2
- package/dist/assets/main-Cpd9VWYs.js +0 -169
- package/dist/assets/markdown-worker-CptoICmq.js +0 -2
- package/dist/assets/miniChat-B8_KLWiY.js +0 -2
- package/dist/assets/mobile-CtktKrXn.js +0 -2
- package/dist/assets/multirun-CYfP9Cfm.js +0 -4
- package/dist/assets/pl-B9No6MQp.js +0 -15
- package/dist/assets/platform-0w_Q9_ji.js +0 -2
- package/dist/assets/projectMeta-S1hJ7oBp.js +0 -3
- package/dist/assets/pt-BR-DyQ0WTDh.js +0 -15
- package/dist/assets/renderElectronMiniChatApp-iK14LWm8.js +0 -2
- package/dist/assets/renderMobileApp-DORLPWnb.js +0 -28
- package/dist/assets/src-rS3zCcsC.css +0 -2
- package/dist/assets/store-5B_LDnLM.js +0 -15
- package/dist/assets/uk-C_1KkqyP.js +0 -15
- package/dist/assets/useDesktopWindowControlsLayout-Dnr-jX0C.js +0 -1
- package/dist/assets/useEffectiveDirectory-CZLLyarc.js +0 -1
- package/dist/assets/useMobileNavigationStore-CoCiqNgm.js +0 -413
- package/dist/assets/useSessionAutoCleanup-xQyETQkG.js +0 -2
- package/dist/assets/useUIStore-Bxv3f23A.js +0 -1
- package/dist/assets/vendor-opencode-sdk-C07HQypa.js +0 -7
- package/dist/assets/zh-CN-uWILgBdt.js +0 -15
- package/dist/assets/zh-TW-MYfdvhbD.js +0 -15
- package/server/lib/small-model/call.js +0 -427
- package/server/lib/small-model/call.test.js +0 -382
- package/server/lib/small-model/opencode-session.js +0 -339
- package/server/lib/small-model/opencode-session.test.js +0 -177
|
@@ -122,15 +122,28 @@ export const createManagedCapabilitiesRuntime = ({
|
|
|
122
122
|
bridgeOrigin = parsed.origin;
|
|
123
123
|
};
|
|
124
124
|
|
|
125
|
+
const publishHostShim = async () => {
|
|
126
|
+
const sourceDir = pathLike.join(pathLike.dirname(fileURLToPath(import.meta.url)), 'v2-plugin-host-shim');
|
|
127
|
+
const target = pathLike.join(dataDir, 'managed-opencode-capabilities', 'v2-plugin-host-shim');
|
|
128
|
+
await fsLike.mkdir(target, { recursive: true });
|
|
129
|
+
await Promise.all(['package.json', 'index.mjs'].map((name) => fsLike.copyFile(pathLike.join(sourceDir, name), pathLike.join(target, name))));
|
|
130
|
+
return target;
|
|
131
|
+
};
|
|
132
|
+
|
|
125
133
|
const prepareManagedChildEnv = async (baseEnv = {}) => {
|
|
126
134
|
if (!bridgeOrigin) throw new Error('Managed scheduled-task bridge origin is unavailable');
|
|
127
|
-
const { pluginPath, instructionsPath } = await
|
|
135
|
+
const [{ pluginPath, instructionsPath }, shimDirectory] = await Promise.all([
|
|
136
|
+
publishResources(),
|
|
137
|
+
publishHostShim(),
|
|
138
|
+
]);
|
|
128
139
|
const token = cryptoLike.randomBytes(32).toString('hex');
|
|
129
140
|
const pluginUrl = pathToFileURL(pluginPath).href;
|
|
130
141
|
identity = { version: materialVersion, origin: bridgeOrigin, token, childPid: null };
|
|
142
|
+
const merged = JSON.parse(mergeManagedOpenCodeConfig({ configContent: baseEnv.OPENCODE_CONFIG_CONTENT, pluginUrl, instructionsUrl: instructionsPath }));
|
|
143
|
+
merged.plugin = stableDedupe([...(Array.isArray(merged.plugin) ? merged.plugin : []), shimDirectory]);
|
|
131
144
|
return {
|
|
132
145
|
...baseEnv,
|
|
133
|
-
OPENCODE_CONFIG_CONTENT:
|
|
146
|
+
OPENCODE_CONFIG_CONTENT: JSON.stringify(merged),
|
|
134
147
|
OPENCHAMBER_SCHEDULED_TASK_BRIDGE_ORIGIN: bridgeOrigin,
|
|
135
148
|
OPENCHAMBER_SCHEDULED_TASK_BRIDGE_PATH: MANAGED_SCHEDULED_TASK_TOOL_PATH,
|
|
136
149
|
OPENCHAMBER_SCHEDULED_TASK_TOKEN_HEADER: MANAGED_SCHEDULED_TASK_TOKEN_HEADER,
|
|
@@ -24,6 +24,7 @@ describe('managed OpenCode capabilities runtime', () => {
|
|
|
24
24
|
runtime.recordManagedChildPid(44);
|
|
25
25
|
const identity = runtime.getCapabilityIdentity();
|
|
26
26
|
expect(childEnv.OPENCODE_CONFIG_CONTENT).toContain('scheduled-task.mjs');
|
|
27
|
+
expect(JSON.parse(childEnv.OPENCODE_CONFIG_CONTENT).plugin).toContain(path.join(dataDir, 'managed-opencode-capabilities', 'v2-plugin-host-shim'));
|
|
27
28
|
expect(JSON.parse(childEnv.OPENCODE_CONFIG_CONTENT).instructions[0]).toMatch(/^\//);
|
|
28
29
|
expect(childEnv.OPENCHAMBER_SCHEDULED_TASK_BRIDGE_PATH).toBe(MANAGED_SCHEDULED_TASK_TOOL_PATH);
|
|
29
30
|
expect(childEnv.OPENCHAMBER_SCHEDULED_TASK_TOKEN_HEADER).toBe(MANAGED_SCHEDULED_TASK_TOKEN_HEADER);
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { evaluateOpenCodeHealthBody } from './opencode2-pin.js';
|
|
2
|
+
|
|
1
3
|
export const createOpenCodeNetworkRuntime = (deps) => {
|
|
2
4
|
const {
|
|
3
5
|
state,
|
|
@@ -41,26 +43,29 @@ export const createOpenCodeNetworkRuntime = (deps) => {
|
|
|
41
43
|
|
|
42
44
|
const waitForReady = async (url, timeoutMs = 10000) => {
|
|
43
45
|
const start = Date.now();
|
|
46
|
+
const baseUrl = url.replace(/\/+$/, '');
|
|
44
47
|
while (Date.now() - start < timeoutMs) {
|
|
45
48
|
let timeout = null;
|
|
46
49
|
try {
|
|
47
50
|
const controller = new AbortController();
|
|
48
51
|
timeout = setTimeout(() => controller.abort(), 3000);
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
52
|
+
const headers = {
|
|
53
|
+
Accept: 'application/json',
|
|
54
|
+
...getOpenCodeAuthHeaders(),
|
|
55
|
+
};
|
|
56
|
+
for (const healthPath of ['/api/info', '/global/health']) {
|
|
57
|
+
const response = await fetch(`${baseUrl}${healthPath}`, {
|
|
58
|
+
method: 'GET',
|
|
59
|
+
headers,
|
|
60
|
+
signal: controller.signal,
|
|
61
|
+
});
|
|
62
|
+
if (response.ok) {
|
|
63
|
+
const body = await response.json().catch(() => null);
|
|
64
|
+
// Official 2.x /api/info is ServerInfo (version, no healthy);
|
|
65
|
+
// /global/health may still return { healthy, version }.
|
|
66
|
+
if (evaluateOpenCodeHealthBody(body).ok) {
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
64
69
|
}
|
|
65
70
|
}
|
|
66
71
|
} catch {
|
|
@@ -88,8 +93,15 @@ export const createOpenCodeNetworkRuntime = (deps) => {
|
|
|
88
93
|
throw new Error('OpenCode port is not available');
|
|
89
94
|
}
|
|
90
95
|
const normalizedPath = path.startsWith('/') ? path : `/${path}`;
|
|
96
|
+
// v2 protocol mounts every route under `/api` (`@opencode-ai/protocol`).
|
|
97
|
+
// Callers pass unprefixed API paths (`/session`, `/event`, …); the bare
|
|
98
|
+
// upstream path falls through to the v2 Web UI HTML fallback. `/` stays the
|
|
99
|
+
// upstream origin for SDK base URLs, and already-prefixed paths are kept.
|
|
100
|
+
const apiPath = normalizedPath === '/' || normalizedPath.startsWith('/api')
|
|
101
|
+
? normalizedPath
|
|
102
|
+
: `/api${normalizedPath}`;
|
|
91
103
|
const prefix = normalizeApiPrefix(prefixOverride !== undefined ? prefixOverride : '');
|
|
92
|
-
const fullPath = `${prefix}${
|
|
104
|
+
const fullPath = `${prefix}${apiPath}`;
|
|
93
105
|
const base = state.openCodeBaseUrl ?? `http://${resolveConnectHostname()}:${state.openCodePort}`;
|
|
94
106
|
return `${base}${fullPath}`;
|
|
95
107
|
};
|
|
@@ -23,6 +23,63 @@ describe('OpenCode network runtime', () => {
|
|
|
23
23
|
globalThis.fetch = originalFetch;
|
|
24
24
|
});
|
|
25
25
|
|
|
26
|
+
it('probes /api/info with Authorization and treats healthy true as ready', async () => {
|
|
27
|
+
const fetchMock = vi.fn(async (url, options) => {
|
|
28
|
+
expect(String(url)).toContain('/api/info');
|
|
29
|
+
expect(options.headers.Authorization).toMatch(/^Basic /);
|
|
30
|
+
return new Response(JSON.stringify({ version: '2.0.12', pid: 1, urls: [], paths: { tmp: '/tmp' } }), {
|
|
31
|
+
status: 200,
|
|
32
|
+
headers: { 'content-type': 'application/json' },
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
globalThis.fetch = fetchMock;
|
|
36
|
+
|
|
37
|
+
const runtime = createOpenCodeNetworkRuntime({
|
|
38
|
+
state: {
|
|
39
|
+
openCodePort: 4096,
|
|
40
|
+
openCodeBaseUrl: null,
|
|
41
|
+
openCodeApiPrefix: '',
|
|
42
|
+
openCodeApiPrefixDetected: false,
|
|
43
|
+
openCodeApiDetectionTimer: null,
|
|
44
|
+
},
|
|
45
|
+
getOpenCodeAuthHeaders: () => ({ Authorization: 'Basic Zml4dHVyZQ==' }),
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
await expect(runtime.waitForReady('http://127.0.0.1:4096', 1000)).resolves.toBe(true);
|
|
49
|
+
expect(String(fetchMock.mock.calls[0][0])).toContain('/api/info');
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('falls back to /global/health when /api/info is not healthy', async () => {
|
|
53
|
+
const fetchMock = vi.fn(async (url) => {
|
|
54
|
+
if (String(url).includes('/api/info')) {
|
|
55
|
+
return new Response('not found', { status: 404 });
|
|
56
|
+
}
|
|
57
|
+
if (String(url).includes('/global/health')) {
|
|
58
|
+
return new Response(JSON.stringify({ healthy: true, version: '2.0.12' }), {
|
|
59
|
+
status: 200,
|
|
60
|
+
headers: { 'content-type': 'application/json' },
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
return new Response('no', { status: 500 });
|
|
64
|
+
});
|
|
65
|
+
globalThis.fetch = fetchMock;
|
|
66
|
+
|
|
67
|
+
const runtime = createOpenCodeNetworkRuntime({
|
|
68
|
+
state: {
|
|
69
|
+
openCodePort: 4096,
|
|
70
|
+
openCodeBaseUrl: null,
|
|
71
|
+
openCodeApiPrefix: '',
|
|
72
|
+
openCodeApiPrefixDetected: false,
|
|
73
|
+
openCodeApiDetectionTimer: null,
|
|
74
|
+
},
|
|
75
|
+
getOpenCodeAuthHeaders: () => ({ Authorization: 'Basic Zml4dHVyZQ==' }),
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
await expect(runtime.waitForReady('http://127.0.0.1:4096', 1000)).resolves.toBe(true);
|
|
79
|
+
expect(String(fetchMock.mock.calls[0][0])).toContain('/api/info');
|
|
80
|
+
expect(String(fetchMock.mock.calls[1][0])).toContain('/global/health');
|
|
81
|
+
});
|
|
82
|
+
|
|
26
83
|
it('returns false when readiness fetch rejects', async () => {
|
|
27
84
|
globalThis.fetch = vi.fn(async () => {
|
|
28
85
|
throw new Error('offline');
|
|
@@ -37,7 +94,7 @@ describe('OpenCode network runtime', () => {
|
|
|
37
94
|
it('builds managed OpenCode URLs against IPv4 loopback by default', () => {
|
|
38
95
|
const runtime = createRuntime();
|
|
39
96
|
|
|
40
|
-
expect(runtime.buildOpenCodeUrl('/provider')).toBe('http://127.0.0.1:4096/provider');
|
|
97
|
+
expect(runtime.buildOpenCodeUrl('/provider')).toBe('http://127.0.0.1:4096/api/provider');
|
|
41
98
|
});
|
|
42
99
|
|
|
43
100
|
it('keeps external OpenCode base URLs authoritative', () => {
|
|
@@ -45,13 +102,20 @@ describe('OpenCode network runtime', () => {
|
|
|
45
102
|
state: { openCodeBaseUrl: 'http://remote.example:4096' },
|
|
46
103
|
});
|
|
47
104
|
|
|
48
|
-
expect(runtime.buildOpenCodeUrl('/provider')).toBe('http://remote.example:4096/provider');
|
|
105
|
+
expect(runtime.buildOpenCodeUrl('/provider')).toBe('http://remote.example:4096/api/provider');
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('keeps SDK base URLs (/) and already-prefixed paths untouched', () => {
|
|
109
|
+
const runtime = createRuntime();
|
|
110
|
+
|
|
111
|
+
expect(runtime.buildOpenCodeUrl('/')).toBe('http://127.0.0.1:4096/');
|
|
112
|
+
expect(runtime.buildOpenCodeUrl('/api/info')).toBe('http://127.0.0.1:4096/api/info');
|
|
49
113
|
});
|
|
50
114
|
|
|
51
115
|
it('normalizes wildcard and IPv6 OpenCode bind hosts for local connects', () => {
|
|
52
116
|
expect(createRuntime({ configuredOpenCodeHostname: '0.0.0.0' }).buildOpenCodeUrl('/provider'))
|
|
53
|
-
.toBe('http://127.0.0.1:4096/provider');
|
|
117
|
+
.toBe('http://127.0.0.1:4096/api/provider');
|
|
54
118
|
expect(createRuntime({ configuredOpenCodeHostname: '::1' }).buildOpenCodeUrl('/provider'))
|
|
55
|
-
.toBe('http://[::1]:4096/provider');
|
|
119
|
+
.toBe('http://[::1]:4096/api/provider');
|
|
56
120
|
});
|
|
57
121
|
});
|
|
@@ -14,11 +14,17 @@ export const registerOpenChamberRoutes = (app, dependencies) => {
|
|
|
14
14
|
getCachedZenModels,
|
|
15
15
|
sessionIndexService,
|
|
16
16
|
sessionIndexSyncRuntime,
|
|
17
|
+
/** Production: committed Host metadata for archive-aware snapshot/lookup. */
|
|
18
|
+
getCommittedHostMetadata = null,
|
|
17
19
|
transcriptCacheService,
|
|
18
20
|
express,
|
|
19
21
|
} = dependencies;
|
|
20
22
|
|
|
21
|
-
registerSessionIndexRoutes(app, {
|
|
23
|
+
registerSessionIndexRoutes(app, {
|
|
24
|
+
sessionIndexService,
|
|
25
|
+
sessionIndexSyncRuntime,
|
|
26
|
+
getCommittedHostMetadata,
|
|
27
|
+
});
|
|
22
28
|
registerTranscriptCacheRoutes(app, { transcriptCacheService });
|
|
23
29
|
registerConfigSyncRoutes(app, { express });
|
|
24
30
|
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
// Pinned desktop/runtime opencode2. Upgrade and prepare must not fall back to 1.18.x.
|
|
2
|
+
export const PINNED_OPENCODE2_VERSION = '2.0.12';
|
|
3
|
+
|
|
4
|
+
// Global npm/bun package that installs the opencode v2 binary (not 1.x opencode-ai).
|
|
5
|
+
export const OPENCODE2_NPM_PACKAGE = '@opencode/cli';
|
|
6
|
+
|
|
7
|
+
export function isOpenCode1xVersion(value) {
|
|
8
|
+
if (typeof value !== 'string') return false;
|
|
9
|
+
const normalized = value.trim().replace(/^v/i, '');
|
|
10
|
+
if (!normalized) return false;
|
|
11
|
+
return /^1(?:\.|$)/.test(normalized);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Health admission for managed/external opencode2.
|
|
16
|
+
* healthy alone is not enough: reject 1.x and missing/unknown version strings.
|
|
17
|
+
*/
|
|
18
|
+
export function isAcceptableOpenCode2HealthVersion(value) {
|
|
19
|
+
if (typeof value !== 'string') return false;
|
|
20
|
+
const normalized = value.trim().replace(/^v/i, '');
|
|
21
|
+
if (!normalized) return false;
|
|
22
|
+
// Official CLI is 2.x. 1.x and pre-2 names like `opencode2 v0.0.0-beta-*` are not it.
|
|
23
|
+
if (!/^2(?:\.|$)/.test(normalized)) return false;
|
|
24
|
+
return /^\d+(?:\.\d+)*(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(normalized);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Pure health/info body gate used by lifecycle and VS Code sidecar.
|
|
29
|
+
* Accepts classic `{ healthy: true, version }` and official 2.x `ServerInfo`
|
|
30
|
+
* (`GET /api/info` → `{ version, pid, urls, paths }` without `healthy`).
|
|
31
|
+
* @param {{ healthy?: unknown, version?: unknown } | null | undefined} body
|
|
32
|
+
* @returns {{ ok: boolean, version: string | null, reason?: string }}
|
|
33
|
+
*/
|
|
34
|
+
export function evaluateOpenCodeHealthBody(body) {
|
|
35
|
+
if (!body || typeof body !== 'object' || Array.isArray(body)) {
|
|
36
|
+
return { ok: false, version: null, reason: 'invalid-body' };
|
|
37
|
+
}
|
|
38
|
+
// Classic /api/health required healthy:true; /api/info omits healthy entirely.
|
|
39
|
+
if (Object.prototype.hasOwnProperty.call(body, 'healthy') && body.healthy !== true) {
|
|
40
|
+
return { ok: false, version: null, reason: 'unhealthy' };
|
|
41
|
+
}
|
|
42
|
+
const raw = typeof body.version === 'string' ? body.version.trim() : '';
|
|
43
|
+
const version = raw ? raw.replace(/^v/i, '') : null;
|
|
44
|
+
if (!isAcceptableOpenCode2HealthVersion(version)) {
|
|
45
|
+
if (isOpenCode1xVersion(version)) {
|
|
46
|
+
return { ok: false, version, reason: '1x-version' };
|
|
47
|
+
}
|
|
48
|
+
return { ok: false, version, reason: 'unknown-version' };
|
|
49
|
+
}
|
|
50
|
+
return { ok: true, version };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function rejectOpenCode1xUpgradeTarget(target) {
|
|
54
|
+
if (isOpenCode1xVersion(target)) {
|
|
55
|
+
const error = new Error(`OpenCode upgrade refuses 1.x target: ${target}`);
|
|
56
|
+
error.code = 'OPENCODE_UPGRADE_1X_REFUSED';
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
return target;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function resolveOpenCode2UpgradeTarget(target) {
|
|
63
|
+
const trimmed = typeof target === 'string' ? target.trim() : '';
|
|
64
|
+
if (trimmed) {
|
|
65
|
+
rejectOpenCode1xUpgradeTarget(trimmed);
|
|
66
|
+
return trimmed;
|
|
67
|
+
}
|
|
68
|
+
return PINNED_OPENCODE2_VERSION;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Parse `opencode2 --version` / `opencode --version` stdout.
|
|
73
|
+
* v2: `opencode2 v2.0.12` (name then version). 1.x: `1.18.18`.
|
|
74
|
+
*/
|
|
75
|
+
export function parseOpenCode2VersionOutput(stdout) {
|
|
76
|
+
const tokens = String(stdout || '').trim().split(/\s+/);
|
|
77
|
+
const versionToken = tokens.find((token) => /^v?\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(token));
|
|
78
|
+
if (!versionToken) return '';
|
|
79
|
+
return versionToken.replace(/^v/, '');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function openCodeBinaryName(platform = process.platform) {
|
|
83
|
+
return platform === 'win32' ? 'opencode.exe' : 'opencode';
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** @deprecated Packaging leftover. Runtime install uses `openCodeBinaryName`. */
|
|
87
|
+
export function openCode2BinaryName(platform = process.platform) {
|
|
88
|
+
return openCodeBinaryName(platform);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function resolveOpenCode2NpmArchitecture(arch = process.arch) {
|
|
92
|
+
const normalized = String(arch || '').trim().toLowerCase();
|
|
93
|
+
if (normalized === 'x64' || normalized === 'amd64' || normalized === 'x86_64') return 'x64';
|
|
94
|
+
if (normalized === 'arm64' || normalized === 'aarch64') return 'arm64';
|
|
95
|
+
throw new Error(`Unsupported OpenCode architecture ${JSON.stringify(arch)}`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Official 2.x ships as `@opencode/cli-<os>-<arch>[-baseline]`.
|
|
100
|
+
* `targetArchitecture` may be `{ opencode }` (Electron prepare) or an arch string.
|
|
101
|
+
*/
|
|
102
|
+
export function npmPackageForOpenCode2(platform = process.platform, targetArchitecture = process.arch) {
|
|
103
|
+
const rawArch = targetArchitecture && typeof targetArchitecture === 'object'
|
|
104
|
+
? (targetArchitecture.opencode ?? targetArchitecture.node)
|
|
105
|
+
: targetArchitecture;
|
|
106
|
+
const arch = resolveOpenCode2NpmArchitecture(rawArch);
|
|
107
|
+
const os = { darwin: 'darwin', win32: 'windows', linux: 'linux' }[platform];
|
|
108
|
+
if (!os) throw new Error(`No opencode2 npm package mapping for platform ${platform}`);
|
|
109
|
+
const suffix = arch === 'x64' ? '-baseline' : '';
|
|
110
|
+
return `@opencode/cli-${os}-${arch}${suffix}`;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function parseVersionForComparison(value) {
|
|
114
|
+
const normalized = String(value || '').replace(/^v/i, '').split('+')[0];
|
|
115
|
+
const prereleaseIndex = normalized.indexOf('-');
|
|
116
|
+
const core = prereleaseIndex >= 0 ? normalized.slice(0, prereleaseIndex) : normalized;
|
|
117
|
+
const parts = core.split('.').map((part) => {
|
|
118
|
+
const parsed = Number.parseInt(part || '0', 10);
|
|
119
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
120
|
+
});
|
|
121
|
+
return { parts, prerelease: prereleaseIndex >= 0 };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function compareOpenCode2Versions(left, right) {
|
|
125
|
+
const a = parseVersionForComparison(left);
|
|
126
|
+
const b = parseVersionForComparison(right);
|
|
127
|
+
const length = Math.max(a.parts.length, b.parts.length);
|
|
128
|
+
for (let index = 0; index < length; index += 1) {
|
|
129
|
+
const diff = (a.parts[index] || 0) - (b.parts[index] || 0);
|
|
130
|
+
if (diff !== 0) return diff;
|
|
131
|
+
}
|
|
132
|
+
if (a.prerelease !== b.prerelease) return a.prerelease ? -1 : 1;
|
|
133
|
+
return 0;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function isOpenCode2VersionAtLeast(version, minimum = PINNED_OPENCODE2_VERSION) {
|
|
137
|
+
if (!isAcceptableOpenCode2HealthVersion(version)) return false;
|
|
138
|
+
return compareOpenCode2Versions(version, minimum) >= 0;
|
|
139
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
OPENCODE2_NPM_PACKAGE,
|
|
4
|
+
PINNED_OPENCODE2_VERSION,
|
|
5
|
+
compareOpenCode2Versions,
|
|
6
|
+
evaluateOpenCodeHealthBody,
|
|
7
|
+
isAcceptableOpenCode2HealthVersion,
|
|
8
|
+
isOpenCode1xVersion,
|
|
9
|
+
isOpenCode2VersionAtLeast,
|
|
10
|
+
npmPackageForOpenCode2,
|
|
11
|
+
parseOpenCode2VersionOutput,
|
|
12
|
+
rejectOpenCode1xUpgradeTarget,
|
|
13
|
+
resolveOpenCode2UpgradeTarget,
|
|
14
|
+
} from './opencode2-pin.js';
|
|
15
|
+
|
|
16
|
+
describe('opencode2 pin (ticket 12)', () => {
|
|
17
|
+
it('rejects 1.x version strings and accepts the pinned v2', () => {
|
|
18
|
+
expect(isOpenCode1xVersion('1.18.4')).toBe(true);
|
|
19
|
+
expect(isOpenCode1xVersion('v1.18.18')).toBe(true);
|
|
20
|
+
expect(isOpenCode1xVersion('1.18.x')).toBe(true);
|
|
21
|
+
expect(isOpenCode1xVersion('1')).toBe(true);
|
|
22
|
+
expect(isOpenCode1xVersion(PINNED_OPENCODE2_VERSION)).toBe(false);
|
|
23
|
+
expect(isOpenCode1xVersion('2.0.12')).toBe(false);
|
|
24
|
+
expect(isOpenCode1xVersion('')).toBe(false);
|
|
25
|
+
expect(isOpenCode1xVersion(null)).toBe(false);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('throws on 1.x upgrade targets and defaults empty to the pin', () => {
|
|
29
|
+
expect(() => rejectOpenCode1xUpgradeTarget('1.18.4')).toThrow(/1\.x/);
|
|
30
|
+
expect(() => rejectOpenCode1xUpgradeTarget('1.18.4')).toThrow(expect.objectContaining({ code: 'OPENCODE_UPGRADE_1X_REFUSED' }));
|
|
31
|
+
expect(resolveOpenCode2UpgradeTarget(undefined)).toBe(PINNED_OPENCODE2_VERSION);
|
|
32
|
+
expect(resolveOpenCode2UpgradeTarget('')).toBe(PINNED_OPENCODE2_VERSION);
|
|
33
|
+
expect(resolveOpenCode2UpgradeTarget(PINNED_OPENCODE2_VERSION)).toBe(PINNED_OPENCODE2_VERSION);
|
|
34
|
+
expect(() => resolveOpenCode2UpgradeTarget('1.18.18')).toThrow(/1\.x/);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('pins the opencode2 npm package name (not 1.x opencode-ai)', () => {
|
|
38
|
+
expect(OPENCODE2_NPM_PACKAGE).toBe('@opencode/cli');
|
|
39
|
+
expect(OPENCODE2_NPM_PACKAGE).not.toBe('opencode-ai');
|
|
40
|
+
expect(OPENCODE2_NPM_PACKAGE).not.toBe('@opencode-ai/cli');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('accepts authoritative v2 health/info versions and rejects 1.x / missing / noise', () => {
|
|
44
|
+
expect(isAcceptableOpenCode2HealthVersion(PINNED_OPENCODE2_VERSION)).toBe(true);
|
|
45
|
+
expect(isAcceptableOpenCode2HealthVersion('v2.0.12')).toBe(true);
|
|
46
|
+
expect(isAcceptableOpenCode2HealthVersion('0.0.0-beta-18155')).toBe(false);
|
|
47
|
+
expect(isAcceptableOpenCode2HealthVersion('1.15.0')).toBe(false);
|
|
48
|
+
expect(isAcceptableOpenCode2HealthVersion('v1.18.18')).toBe(false);
|
|
49
|
+
expect(isAcceptableOpenCode2HealthVersion('')).toBe(false);
|
|
50
|
+
expect(isAcceptableOpenCode2HealthVersion(null)).toBe(false);
|
|
51
|
+
expect(isAcceptableOpenCode2HealthVersion('not-a-version')).toBe(false);
|
|
52
|
+
|
|
53
|
+
expect(evaluateOpenCodeHealthBody({ healthy: true, version: PINNED_OPENCODE2_VERSION })).toEqual({
|
|
54
|
+
ok: true,
|
|
55
|
+
version: PINNED_OPENCODE2_VERSION,
|
|
56
|
+
});
|
|
57
|
+
// Official 2.x ServerInfo from GET /api/info has version without healthy.
|
|
58
|
+
expect(evaluateOpenCodeHealthBody({ version: PINNED_OPENCODE2_VERSION, pid: 1 })).toEqual({
|
|
59
|
+
ok: true,
|
|
60
|
+
version: PINNED_OPENCODE2_VERSION,
|
|
61
|
+
});
|
|
62
|
+
expect(evaluateOpenCodeHealthBody({ healthy: true, version: '1.15.0' })).toMatchObject({
|
|
63
|
+
ok: false,
|
|
64
|
+
version: '1.15.0',
|
|
65
|
+
reason: '1x-version',
|
|
66
|
+
});
|
|
67
|
+
expect(evaluateOpenCodeHealthBody({ healthy: true })).toMatchObject({
|
|
68
|
+
ok: false,
|
|
69
|
+
reason: 'unknown-version',
|
|
70
|
+
});
|
|
71
|
+
expect(evaluateOpenCodeHealthBody({ healthy: false, version: PINNED_OPENCODE2_VERSION })).toMatchObject({
|
|
72
|
+
ok: false,
|
|
73
|
+
reason: 'unhealthy',
|
|
74
|
+
});
|
|
75
|
+
expect(evaluateOpenCodeHealthBody(null)).toMatchObject({ ok: false, reason: 'invalid-body' });
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('parses --version output and compares against the pin', () => {
|
|
79
|
+
expect(parseOpenCode2VersionOutput('opencode2 v2.0.12\n')).toBe('2.0.12');
|
|
80
|
+
expect(parseOpenCode2VersionOutput('1.18.18\n')).toBe('1.18.18');
|
|
81
|
+
expect(compareOpenCode2Versions('2.0.12', '2.0.1')).toBeGreaterThan(0);
|
|
82
|
+
expect(isOpenCode2VersionAtLeast('2.0.12', '2.0.12')).toBe(true);
|
|
83
|
+
expect(isOpenCode2VersionAtLeast('2.0.1', '2.0.12')).toBe(false);
|
|
84
|
+
expect(isOpenCode2VersionAtLeast('1.18.18', '2.0.12')).toBe(false);
|
|
85
|
+
expect(npmPackageForOpenCode2('darwin', 'arm64')).toBe('@opencode/cli-darwin-arm64');
|
|
86
|
+
expect(npmPackageForOpenCode2('darwin', { opencode: 'x64' })).toBe('@opencode/cli-darwin-x64-baseline');
|
|
87
|
+
});
|
|
88
|
+
});
|