@openchambery/web 2.0.0-beta.8 → 2.0.0
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 +14 -0
- package/dist/assets/{AgentAvatar-CzTtVMnI.js → AgentAvatar-C1MGZ7qC.js} +1 -1
- package/dist/assets/AssistantView-vqCFvVo0.js +3 -0
- package/dist/assets/{DiagramView-BBPsLqLq.js → DiagramView-CYO5iVVm.js} +1 -1
- package/dist/assets/{FadeInOnReveal-DCqrQkkz.js → FadeInOnReveal-BMSk_tY-.js} +1 -1
- package/dist/assets/Icon-BdzPRCSy.js +1 -0
- package/dist/assets/MarkdownRenderer-FMynjkGR.js +2 -0
- package/dist/assets/{MarkdownRendererImpl-CD0RYNWX.js → MarkdownRendererImpl-DoWvw5jj.js} +3 -3
- package/dist/assets/{MarkstreamRendererImpl-CECVYTH6.js → MarkstreamRendererImpl-DoeYajmw.js} +1 -1
- package/dist/assets/{MobileDetailNavigation-DOwW-F0i.js → MobileDetailNavigation-BzBz-co_.js} +1 -1
- package/dist/assets/{MobileShareBridge-d0hQ2FSQ.js → MobileShareBridge-D1Uoj6l_.js} +3 -3
- package/dist/assets/MobileSurface-8YpZ51Qo.js +2 -0
- package/dist/assets/{MultiRunWindow-DOdFEgMg.js → MultiRunWindow-BxcYUPta.js} +1 -1
- package/dist/assets/{OnboardingScreen-O4cGdz5p.js → OnboardingScreen-yRdQTR1M.js} +1 -1
- package/dist/assets/{OverlayScrollbar-S-7EePsU.js → OverlayScrollbar-BUjtqnxn.js} +1 -1
- package/dist/assets/SettingsView-D7KrvqvH.js +25 -0
- package/dist/assets/{SettingsWindow-vKn-MWGU.js → SettingsWindow-DX2w-cDc.js} +1 -1
- package/dist/assets/{TerminalView-BgPjY0CO.js → TerminalView-CiUcCQPm.js} +1 -1
- package/dist/assets/{ToolOutputDialog-DtT3wbie.js → ToolOutputDialog-DQvXtK71.js} +1 -1
- package/dist/assets/appThemeRegistry-BXC6cIDE.js +1 -0
- package/dist/assets/{assistant-attachment-cache-DphJleHr.js → assistant-attachment-cache-sG8YQsVs.js} +1 -1
- package/dist/assets/{button-DIIrsMjW.js → button-CUwjpv-i.js} +1 -1
- package/dist/assets/{collapsible-CnvUeYst.js → collapsible-paItOH3A.js} +1 -1
- package/dist/assets/{context-menu-BGBUpZEQ.js → context-menu-K6zNumqN.js} +1 -1
- package/dist/assets/{desktopHosts-JGFqhlaM.js → desktopHosts-DuftEj8B.js} +1 -1
- package/dist/assets/desktopRelayRestore-g378BdZf.js +1 -0
- package/dist/assets/{diagram-rvn7_qBM.js → diagram-D6nY5hbD.js} +1 -1
- package/dist/assets/dialog-PjKWayIU.js +1 -0
- package/dist/assets/dropdown-menu.styles-ayLWk2gf.js +1 -0
- package/dist/assets/{es-Dsmc0A_3.js → es-BBbon7SL.js} +4 -4
- package/dist/assets/fileMentionTouchSelection-78v5mNTE.js +2 -0
- package/dist/assets/{fileQueries-DN6gbLhM.js → fileQueries-DuF89nFO.js} +1 -1
- package/dist/assets/{fr-C_ZlZDI2.js → fr-BsKj2CTi.js} +4 -4
- package/dist/assets/{gitApi-D9ygh31C.js → gitApi-eoKSJcvW.js} +4 -4
- package/dist/assets/{imageSource-BMztRu0c.js → imageSource-BpUsiwFd.js} +1 -1
- package/dist/assets/{input-BVu-iykl.js → input-BeVw4D0W.js} +1 -1
- package/dist/assets/insertionBoundaries-BI3RHOpf.js +1 -0
- package/dist/assets/{ja-BqqTaWTi.js → ja-1mgxunoM.js} +4 -4
- package/dist/assets/{ko-DP2XPM37.js → ko-Ra9znPcL.js} +4 -4
- package/dist/assets/main-CQpUPjPK.js +169 -0
- package/dist/assets/{main-B97oNWoh.js → main-MN0YKJ0_.js} +2 -2
- package/dist/assets/main-qSeeJRW1.css +1 -0
- package/dist/assets/{markdownHeightCache-D9-B1wdY.js → markdownHeightCache-CGCWmHgq.js} +1 -1
- package/dist/assets/{mcpQueries-zC4-V5b8.js → mcpQueries-X424Iq2z.js} +1 -1
- package/dist/assets/miniChat-DFIl76ku.js +2 -0
- package/dist/assets/mobile-C3fWXGQJ.js +2 -0
- package/dist/assets/multirun-DuSX3YRs.js +3 -0
- package/dist/assets/persistence-Dnj1QOdX.js +3 -0
- package/dist/assets/pl-B714sdql.js +15 -0
- package/dist/assets/platform-jCZ3KfMQ.js +6 -0
- package/dist/assets/projectMeta-Cn08pTNi.js +1 -0
- package/dist/assets/{pt-BR-W09Q2pvn.js → pt-BR-DVoT_I7_.js} +4 -4
- package/dist/assets/{relay-image-stream-DrdVj99c.js → relay-image-stream-D2XooXKZ.js} +1 -1
- package/dist/assets/renderElectronMiniChatApp-CRPdctIn.js +2 -0
- package/dist/assets/renderMobileApp-CVURQbeh.js +28 -0
- package/dist/assets/{runtime-fetch-Fs9ZV2V8.js → runtime-fetch-CiqQd__Y.js} +1 -1
- package/dist/assets/runtime-switch-BJM2k_uH.js +1 -0
- package/dist/assets/runtimeConfig-xivE1wxC.js +2 -0
- package/dist/assets/runtimeEndpointReset-B8DzKzwg.js +148 -0
- package/dist/assets/runtimeEndpointReset-D_vfNOQz.css +1 -0
- package/dist/assets/sessionChangeSummary-yBsR2pyd.js +2 -0
- package/dist/assets/{sessionLookup-DcyGP3xg.js → sessionLookup-1Z1FWZRB.js} +1 -1
- package/dist/assets/{sortable-tabs-strip-BsObV7Pl.js → sortable-tabs-strip-D611LPyd.js} +1 -1
- package/dist/assets/src-TBM7scxy.css +2 -0
- package/dist/assets/{store-jaP79mqI.js → store-l_X_jQNz.js} +4 -4
- package/dist/assets/{terminalApi-Dhqqp4xi.js → terminalApi-BZW_rAc3.js} +1 -1
- package/dist/assets/{textarea-BoXpPjTd.js → textarea-QtZjf4L8.js} +1 -1
- package/dist/assets/{tooltip-DYeQ_Hvy.js → tooltip-DDNnmcXI.js} +1 -1
- package/dist/assets/{uk-BgFzz8m2.js → uk-ChNnTgKP.js} +4 -4
- package/dist/assets/{url-CGbJbiJq.js → url-DNRMq_0R.js} +1 -1
- package/dist/assets/{useAppFontEffects-BauzQXF0.js → useAppFontEffects-DJy8P5By.js} +51 -51
- package/dist/assets/{useAssistantUIStore-Bz4tRflF.js → useAssistantUIStore-DBwfg9nX.js} +1 -1
- package/dist/assets/useDesktopWindowControlsLayout-DlwSomVK.js +1 -0
- package/dist/assets/{useEffectiveDirectory-qDoROBC_.js → useEffectiveDirectory-eJorVTT9.js} +1 -1
- package/dist/assets/{useFontPreferences-Xw1vSkmC.js → useFontPreferences-Bjgt_lzl.js} +1 -1
- package/dist/assets/useMobileNavigationStore-A_ZNTfpl.js +415 -0
- package/dist/assets/useSessionAutoCleanup-CqC3dEou.js +2 -0
- package/dist/assets/{useUIStore-syVmbuDA.js → useUIStore-Dfx6TKvi.js} +1 -1
- package/dist/assets/{useWorkerHighlightedLines-D8luxgem.js → useWorkerHighlightedLines-B0vSL6jr.js} +1 -1
- package/dist/assets/vendor-opencode-client-MF2GBrFx.js +12 -0
- package/dist/assets/{vendor-zustand-B4dlGEM2.js → vendor-zustand-DzRqcymP.js} +1 -1
- package/dist/assets/{zh-CN-BhqSLgUY.js → zh-CN-DHEFf4u1.js} +4 -4
- package/dist/assets/{zh-TW-BsaBgY-p.js → zh-TW-rdSa8atW.js} +4 -4
- package/dist/index.html +13 -13
- package/dist/mini-chat.html +13 -13
- package/dist/mobile.html +13 -13
- package/package.json +3 -3
- package/server/index.js +8 -20
- package/server/lib/client-auth/remote-clients.js +15 -0
- package/server/lib/client-auth/remote-clients.test.js +42 -1
- package/server/lib/git/DOCUMENTATION.md +1 -0
- package/server/lib/git/routes.js +7 -4
- package/server/lib/git/routes.test.js +34 -0
- package/server/lib/git/service.js +3 -1
- package/server/lib/llm/DOCUMENTATION.md +6 -4
- package/server/lib/llm/generate.js +8 -9
- package/server/lib/llm/generate.test.js +33 -6
- package/server/lib/message-queue/service.js +1 -1
- package/server/lib/opencode/DOCUMENTATION.md +33 -22
- package/server/lib/opencode/agents.js +13 -22
- package/server/lib/opencode/auth-state-runtime.js +3 -3
- package/server/lib/opencode/auth-state-runtime.test.js +59 -0
- package/server/lib/opencode/auth.d.ts +13 -0
- package/server/lib/opencode/auth.js +74 -64
- package/server/lib/opencode/auth.test.js +57 -0
- package/server/lib/opencode/commands.js +1 -1
- package/server/lib/opencode/config-entities-layering.test.js +72 -0
- package/server/lib/opencode/config-entity-routes.js +10 -123
- package/server/lib/opencode/config-entity-routes.test.js +23 -11
- package/server/lib/opencode/config-files.d.ts +17 -0
- package/server/lib/opencode/config-files.js +187 -0
- package/server/lib/opencode/config-files.test.js +201 -0
- package/server/lib/opencode/core-routes.js +17 -1
- package/server/lib/opencode/core-routes.test.js +43 -15
- package/server/lib/opencode/credential-boundary.d.ts +1 -0
- package/server/lib/opencode/credential-boundary.js +11 -0
- package/server/lib/opencode/ensure-cli.js +4 -1
- package/server/lib/opencode/env-config.js +4 -1
- package/server/lib/opencode/env-config.test.js +28 -0
- package/server/lib/opencode/env-runtime.test.js +18 -1
- package/server/lib/opencode/feature-routes-runtime.js +9 -7
- package/server/lib/opencode/hmr-state-runtime.js +4 -4
- package/server/lib/opencode/lifecycle.js +1 -49
- package/server/lib/opencode/lifecycle.test.js +30 -4
- package/server/lib/opencode/location-release.d.ts +8 -0
- package/server/lib/opencode/location-release.js +32 -0
- package/server/lib/opencode/location-release.test.js +43 -0
- package/server/lib/opencode/mcp.js +1 -1
- package/server/lib/opencode/opencode2-pin.js +2 -1
- package/server/lib/opencode/owned-runtime-upgrade.test.js +5 -5
- package/server/lib/opencode/plugin-routes.test.js +4 -2
- package/server/lib/opencode/plugins.js +12 -60
- package/server/lib/opencode/plugins.test.js +5 -3
- package/server/lib/opencode/providers.js +6 -3
- package/server/lib/opencode/proxy.js +7 -0
- package/server/lib/opencode/proxy.test.js +13 -0
- package/server/lib/opencode/remote-upgrade.integration.test.js +66 -0
- package/server/lib/opencode/routes.js +10 -8
- package/server/lib/opencode/routes.test.js +51 -10
- package/server/lib/opencode/runtime-contract.js +3 -2
- package/server/lib/opencode/runtime-contract.test.js +12 -12
- package/server/lib/opencode/settings-normalization-runtime.js +3 -1
- package/server/lib/opencode/settings-normalization-runtime.test.js +36 -0
- package/server/lib/opencode/settings-runtime.test.js +58 -1
- package/server/lib/opencode/shared.js +6 -175
- package/server/lib/opencode/update-discovery.js +39 -0
- package/server/lib/opencode/update-discovery.test.js +39 -0
- package/server/lib/opencode/upgrade-screen.js +4 -11
- package/server/lib/opencode/upgrade-screen.test.js +25 -25
- package/server/lib/opencode/version-alignment.test.js +40 -0
- package/server/lib/question-auto-delegate/DOCUMENTATION.md +21 -6
- package/server/lib/question-auto-delegate/core.d.ts +5 -1
- package/server/lib/question-auto-delegate/core.js +21 -13
- package/server/lib/question-auto-delegate/form-io.d.ts +6 -0
- package/server/lib/question-auto-delegate/form-io.js +39 -0
- package/server/lib/question-auto-delegate/runtime.js +4 -24
- package/server/lib/question-auto-delegate/runtime.test.js +138 -0
- package/server/lib/quota/DOCUMENTATION.md +9 -0
- package/server/lib/quota/provider-credentials.test.js +29 -0
- package/server/lib/quota/providers/claude.js +4 -4
- package/server/lib/quota/providers/codex.js +4 -4
- package/server/lib/quota/providers/codex.test.js +1 -1
- package/server/lib/quota/providers/copilot.js +5 -5
- package/server/lib/quota/providers/google/auth.js +3 -3
- package/server/lib/quota/providers/google/index.js +2 -2
- package/server/lib/quota/providers/index.js +3 -7
- package/server/lib/quota/providers/index.test.js +2 -2
- package/server/lib/quota/providers/kimi.js +4 -4
- package/server/lib/quota/providers/minimax-shared.js +4 -4
- package/server/lib/quota/providers/nanogpt.js +4 -4
- package/server/lib/quota/providers/openai.js +4 -4
- package/server/lib/quota/providers/openrouter.js +4 -4
- package/server/lib/quota/providers/wafer.js +4 -4
- package/server/lib/quota/providers/zai.js +4 -4
- package/server/lib/quota/providers/zhipuai-coding-plan.js +6 -20
- package/server/lib/quota/routes.js +1 -1
- package/server/lib/relay/DOCUMENTATION.md +3 -1
- package/server/lib/relay/service.js +1 -0
- package/server/lib/relay/service.test.js +13 -0
- package/server/lib/scheduled-tasks/DOCUMENTATION.md +8 -1
- package/server/lib/scheduled-tasks/runtime.js +12 -4
- package/server/lib/scheduled-tasks/runtime.test.js +81 -6
- package/server/lib/session-goal/DOCUMENTATION.md +11 -0
- package/server/lib/session-goal/runtime.js +4 -4
- package/server/lib/session-goal/runtime.test.js +32 -3
- package/server/lib/session-index/DOCUMENTATION.md +7 -0
- package/server/lib/session-index/event-ingest.js +13 -0
- package/server/lib/session-index/event-ingest.test.js +7 -0
- package/server/lib/session-index/service.js +45 -0
- package/server/lib/session-index/service.test.js +47 -0
- package/server/lib/session-metadata/DOCUMENTATION.md +6 -0
- package/server/lib/session-metadata/session-metadata-store.test.js +23 -0
- package/server/lib/session-metadata/session-projection.d.ts +1 -0
- package/server/lib/session-metadata/session-projection.js +23 -0
- package/server/lib/session-metadata/session-projection.test.js +35 -0
- package/server/lib/session-title/DOCUMENTATION.md +52 -14
- package/server/lib/session-title/native-runtime.test.js +59 -5
- package/server/lib/session-title/runtime.js +63 -17
- package/server/lib/session-title/session-access.js +108 -3
- package/server/lib/session-title/session-access.test.js +125 -7
- package/server/lib/tts/DOCUMENTATION.md +7 -5
- package/server/lib/tts/credential-source.test.js +23 -0
- package/server/lib/tts/routes.js +2 -2
- package/server/lib/tts/service.js +16 -37
- package/dist/assets/AssistantView-CxAzKd78.js +0 -3
- package/dist/assets/Icon-B8fSTLdP.js +0 -1
- package/dist/assets/MarkdownRenderer-yq2DPZUG.js +0 -2
- package/dist/assets/MobileSurface-qalyCN7X.js +0 -2
- package/dist/assets/SettingsView-DVxMbeKC.js +0 -25
- package/dist/assets/appThemeRegistry-DC5NRV_i.js +0 -1
- package/dist/assets/desktopRelayRestore-CHEwrI93.js +0 -1
- package/dist/assets/dialog-CQ63sc9E.js +0 -1
- package/dist/assets/dropdown-menu.styles-Bzo6L3FH.js +0 -1
- package/dist/assets/fileMentionTouchSelection-B8Blr6EI.js +0 -3
- package/dist/assets/insertionBoundaries-DYu8VdgE.js +0 -1
- package/dist/assets/main-D2BzpnUt.css +0 -1
- package/dist/assets/main-SXMSe99Y.js +0 -169
- package/dist/assets/miniChat-CIi4PmQO.js +0 -2
- package/dist/assets/mobile-BK0Ov-k5.js +0 -2
- package/dist/assets/multirun-UIu_EbC2.js +0 -4
- package/dist/assets/persistence-D0y7226-.js +0 -3
- package/dist/assets/pl-BCf5uLlK.js +0 -15
- package/dist/assets/platform-D_c-T5nO.js +0 -6
- package/dist/assets/projectMeta-BPi16qGY.js +0 -3
- package/dist/assets/renderElectronMiniChatApp-CnTCJpBd.js +0 -2
- package/dist/assets/renderMobileApp-BFDrOvfv.js +0 -28
- package/dist/assets/runtime-switch-LvWDU-SL.js +0 -1
- package/dist/assets/runtimeConfig-023C-il6.js +0 -2
- package/dist/assets/runtimeEndpointReset-Bdx3CutT.js +0 -149
- package/dist/assets/sessionChangeSummary-B8xrgW5-.js +0 -2
- package/dist/assets/src-lS1keDVV.css +0 -2
- package/dist/assets/useDesktopWindowControlsLayout-kZ4wtPnO.js +0 -1
- package/dist/assets/useMobileNavigationStore-BDXu5FSG.js +0 -414
- package/dist/assets/useSessionAutoCleanup-BHloMhRp.js +0 -2
- package/dist/assets/vendor-opencode-client-C12gfjag.js +0 -12
- package/server/lib/opencode/snippets.js +0 -242
- package/server/lib/opencode/snippets.test.js +0 -97
package/dist/index.html
CHANGED
|
@@ -552,28 +552,28 @@
|
|
|
552
552
|
opacity: 0.72;
|
|
553
553
|
}
|
|
554
554
|
</style>
|
|
555
|
-
<script type="module" crossorigin src="/assets/main-
|
|
555
|
+
<script type="module" crossorigin src="/assets/main-MN0YKJ0_.js"></script>
|
|
556
556
|
<link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-C0FnF6B9.js">
|
|
557
557
|
<link rel="modulepreload" crossorigin href="/assets/runtime-auth-D66yq6ie.js">
|
|
558
|
-
<link rel="modulepreload" crossorigin href="/assets/runtime-switch-
|
|
559
|
-
<link rel="modulepreload" crossorigin href="/assets/runtime-fetch-
|
|
558
|
+
<link rel="modulepreload" crossorigin href="/assets/runtime-switch-BJM2k_uH.js">
|
|
559
|
+
<link rel="modulepreload" crossorigin href="/assets/runtime-fetch-CiqQd__Y.js">
|
|
560
560
|
<link rel="modulepreload" crossorigin href="/assets/vendor-base-ui-CnHfUmDN.js">
|
|
561
|
-
<link rel="modulepreload" crossorigin href="/assets/vendor-zustand-
|
|
562
|
-
<link rel="modulepreload" crossorigin href="/assets/useUIStore-
|
|
561
|
+
<link rel="modulepreload" crossorigin href="/assets/vendor-zustand-DzRqcymP.js">
|
|
562
|
+
<link rel="modulepreload" crossorigin href="/assets/useUIStore-Dfx6TKvi.js">
|
|
563
563
|
<link rel="modulepreload" crossorigin href="/assets/vendor-react-DLPElpoQ.js">
|
|
564
564
|
<link rel="modulepreload" crossorigin href="/assets/vendor-pathe-vBmvQE8f.js">
|
|
565
565
|
<link rel="modulepreload" crossorigin href="/assets/vendor-tanstack-query-core-BWz0ose6.js">
|
|
566
|
-
<link rel="modulepreload" crossorigin href="/assets/persistence-
|
|
567
|
-
<link rel="modulepreload" crossorigin href="/assets/desktopHosts-
|
|
568
|
-
<link rel="modulepreload" crossorigin href="/assets/desktopRelayRestore-
|
|
569
|
-
<link rel="modulepreload" crossorigin href="/assets/vendor-opencode-client-
|
|
566
|
+
<link rel="modulepreload" crossorigin href="/assets/persistence-Dnj1QOdX.js">
|
|
567
|
+
<link rel="modulepreload" crossorigin href="/assets/desktopHosts-DuftEj8B.js">
|
|
568
|
+
<link rel="modulepreload" crossorigin href="/assets/desktopRelayRestore-g378BdZf.js">
|
|
569
|
+
<link rel="modulepreload" crossorigin href="/assets/vendor-opencode-client-MF2GBrFx.js">
|
|
570
570
|
<link rel="modulepreload" crossorigin href="/assets/vendor-aparajita-capacitor-secure-storage-CjQdwfjS.js">
|
|
571
|
-
<link rel="modulepreload" crossorigin href="/assets/platform-
|
|
572
|
-
<link rel="modulepreload" crossorigin href="/assets/terminalApi-
|
|
571
|
+
<link rel="modulepreload" crossorigin href="/assets/platform-jCZ3KfMQ.js">
|
|
572
|
+
<link rel="modulepreload" crossorigin href="/assets/terminalApi-BZW_rAc3.js">
|
|
573
573
|
<link rel="modulepreload" crossorigin href="/assets/types-BuiqMmh_.js">
|
|
574
574
|
<link rel="modulepreload" crossorigin href="/assets/capgoAdapter-DCowZ2ab.js">
|
|
575
|
-
<link rel="modulepreload" crossorigin href="/assets/runtimeConfig-
|
|
576
|
-
<link rel="stylesheet" crossorigin href="/assets/src-
|
|
575
|
+
<link rel="modulepreload" crossorigin href="/assets/runtimeConfig-xivE1wxC.js">
|
|
576
|
+
<link rel="stylesheet" crossorigin href="/assets/src-TBM7scxy.css">
|
|
577
577
|
</head>
|
|
578
578
|
<body class="h-full bg-background text-foreground">
|
|
579
579
|
<!-- Keep the startup layer outside #root. React replaces every child of
|
package/dist/mini-chat.html
CHANGED
|
@@ -74,28 +74,28 @@
|
|
|
74
74
|
}
|
|
75
75
|
</style>
|
|
76
76
|
|
|
77
|
-
<script type="module" crossorigin src="/assets/miniChat-
|
|
77
|
+
<script type="module" crossorigin src="/assets/miniChat-DFIl76ku.js"></script>
|
|
78
78
|
<link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-C0FnF6B9.js">
|
|
79
79
|
<link rel="modulepreload" crossorigin href="/assets/runtime-auth-D66yq6ie.js">
|
|
80
|
-
<link rel="modulepreload" crossorigin href="/assets/runtime-switch-
|
|
81
|
-
<link rel="modulepreload" crossorigin href="/assets/runtime-fetch-
|
|
80
|
+
<link rel="modulepreload" crossorigin href="/assets/runtime-switch-BJM2k_uH.js">
|
|
81
|
+
<link rel="modulepreload" crossorigin href="/assets/runtime-fetch-CiqQd__Y.js">
|
|
82
82
|
<link rel="modulepreload" crossorigin href="/assets/vendor-base-ui-CnHfUmDN.js">
|
|
83
|
-
<link rel="modulepreload" crossorigin href="/assets/vendor-zustand-
|
|
84
|
-
<link rel="modulepreload" crossorigin href="/assets/useUIStore-
|
|
83
|
+
<link rel="modulepreload" crossorigin href="/assets/vendor-zustand-DzRqcymP.js">
|
|
84
|
+
<link rel="modulepreload" crossorigin href="/assets/useUIStore-Dfx6TKvi.js">
|
|
85
85
|
<link rel="modulepreload" crossorigin href="/assets/vendor-react-DLPElpoQ.js">
|
|
86
86
|
<link rel="modulepreload" crossorigin href="/assets/vendor-pathe-vBmvQE8f.js">
|
|
87
87
|
<link rel="modulepreload" crossorigin href="/assets/vendor-tanstack-query-core-BWz0ose6.js">
|
|
88
|
-
<link rel="modulepreload" crossorigin href="/assets/persistence-
|
|
89
|
-
<link rel="modulepreload" crossorigin href="/assets/desktopHosts-
|
|
90
|
-
<link rel="modulepreload" crossorigin href="/assets/desktopRelayRestore-
|
|
91
|
-
<link rel="modulepreload" crossorigin href="/assets/vendor-opencode-client-
|
|
88
|
+
<link rel="modulepreload" crossorigin href="/assets/persistence-Dnj1QOdX.js">
|
|
89
|
+
<link rel="modulepreload" crossorigin href="/assets/desktopHosts-DuftEj8B.js">
|
|
90
|
+
<link rel="modulepreload" crossorigin href="/assets/desktopRelayRestore-g378BdZf.js">
|
|
91
|
+
<link rel="modulepreload" crossorigin href="/assets/vendor-opencode-client-MF2GBrFx.js">
|
|
92
92
|
<link rel="modulepreload" crossorigin href="/assets/vendor-aparajita-capacitor-secure-storage-CjQdwfjS.js">
|
|
93
|
-
<link rel="modulepreload" crossorigin href="/assets/platform-
|
|
94
|
-
<link rel="modulepreload" crossorigin href="/assets/terminalApi-
|
|
93
|
+
<link rel="modulepreload" crossorigin href="/assets/platform-jCZ3KfMQ.js">
|
|
94
|
+
<link rel="modulepreload" crossorigin href="/assets/terminalApi-BZW_rAc3.js">
|
|
95
95
|
<link rel="modulepreload" crossorigin href="/assets/types-BuiqMmh_.js">
|
|
96
96
|
<link rel="modulepreload" crossorigin href="/assets/capgoAdapter-DCowZ2ab.js">
|
|
97
|
-
<link rel="modulepreload" crossorigin href="/assets/runtimeConfig-
|
|
98
|
-
<link rel="stylesheet" crossorigin href="/assets/src-
|
|
97
|
+
<link rel="modulepreload" crossorigin href="/assets/runtimeConfig-xivE1wxC.js">
|
|
98
|
+
<link rel="stylesheet" crossorigin href="/assets/src-TBM7scxy.css">
|
|
99
99
|
</head>
|
|
100
100
|
<body class="h-full bg-background text-foreground">
|
|
101
101
|
<div id="root" class="h-full">
|
package/dist/mobile.html
CHANGED
|
@@ -63,28 +63,28 @@
|
|
|
63
63
|
})();
|
|
64
64
|
</script>
|
|
65
65
|
|
|
66
|
-
<script type="module" crossorigin src="/assets/mobile-
|
|
66
|
+
<script type="module" crossorigin src="/assets/mobile-C3fWXGQJ.js"></script>
|
|
67
67
|
<link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-C0FnF6B9.js">
|
|
68
68
|
<link rel="modulepreload" crossorigin href="/assets/runtime-auth-D66yq6ie.js">
|
|
69
|
-
<link rel="modulepreload" crossorigin href="/assets/runtime-switch-
|
|
70
|
-
<link rel="modulepreload" crossorigin href="/assets/runtime-fetch-
|
|
69
|
+
<link rel="modulepreload" crossorigin href="/assets/runtime-switch-BJM2k_uH.js">
|
|
70
|
+
<link rel="modulepreload" crossorigin href="/assets/runtime-fetch-CiqQd__Y.js">
|
|
71
71
|
<link rel="modulepreload" crossorigin href="/assets/vendor-base-ui-CnHfUmDN.js">
|
|
72
|
-
<link rel="modulepreload" crossorigin href="/assets/vendor-zustand-
|
|
73
|
-
<link rel="modulepreload" crossorigin href="/assets/useUIStore-
|
|
72
|
+
<link rel="modulepreload" crossorigin href="/assets/vendor-zustand-DzRqcymP.js">
|
|
73
|
+
<link rel="modulepreload" crossorigin href="/assets/useUIStore-Dfx6TKvi.js">
|
|
74
74
|
<link rel="modulepreload" crossorigin href="/assets/vendor-react-DLPElpoQ.js">
|
|
75
75
|
<link rel="modulepreload" crossorigin href="/assets/vendor-pathe-vBmvQE8f.js">
|
|
76
76
|
<link rel="modulepreload" crossorigin href="/assets/vendor-tanstack-query-core-BWz0ose6.js">
|
|
77
|
-
<link rel="modulepreload" crossorigin href="/assets/persistence-
|
|
78
|
-
<link rel="modulepreload" crossorigin href="/assets/desktopHosts-
|
|
79
|
-
<link rel="modulepreload" crossorigin href="/assets/desktopRelayRestore-
|
|
80
|
-
<link rel="modulepreload" crossorigin href="/assets/vendor-opencode-client-
|
|
77
|
+
<link rel="modulepreload" crossorigin href="/assets/persistence-Dnj1QOdX.js">
|
|
78
|
+
<link rel="modulepreload" crossorigin href="/assets/desktopHosts-DuftEj8B.js">
|
|
79
|
+
<link rel="modulepreload" crossorigin href="/assets/desktopRelayRestore-g378BdZf.js">
|
|
80
|
+
<link rel="modulepreload" crossorigin href="/assets/vendor-opencode-client-MF2GBrFx.js">
|
|
81
81
|
<link rel="modulepreload" crossorigin href="/assets/vendor-aparajita-capacitor-secure-storage-CjQdwfjS.js">
|
|
82
|
-
<link rel="modulepreload" crossorigin href="/assets/platform-
|
|
83
|
-
<link rel="modulepreload" crossorigin href="/assets/terminalApi-
|
|
82
|
+
<link rel="modulepreload" crossorigin href="/assets/platform-jCZ3KfMQ.js">
|
|
83
|
+
<link rel="modulepreload" crossorigin href="/assets/terminalApi-BZW_rAc3.js">
|
|
84
84
|
<link rel="modulepreload" crossorigin href="/assets/types-BuiqMmh_.js">
|
|
85
85
|
<link rel="modulepreload" crossorigin href="/assets/capgoAdapter-DCowZ2ab.js">
|
|
86
|
-
<link rel="modulepreload" crossorigin href="/assets/runtimeConfig-
|
|
87
|
-
<link rel="stylesheet" crossorigin href="/assets/src-
|
|
86
|
+
<link rel="modulepreload" crossorigin href="/assets/runtimeConfig-xivE1wxC.js">
|
|
87
|
+
<link rel="stylesheet" crossorigin href="/assets/src-TBM7scxy.css">
|
|
88
88
|
</head>
|
|
89
89
|
<body class="h-full bg-background text-foreground">
|
|
90
90
|
<div id="root" class="h-full"></div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openchambery/web",
|
|
3
|
-
"version": "2.0.0
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./server/index.js",
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
"@clack/prompts": "^1.1.0",
|
|
28
28
|
"@earendil-works/pi-agent-core": "0.84.4",
|
|
29
29
|
"@octokit/rest": "^22.0.1",
|
|
30
|
-
"@opencode/client": "2.0.
|
|
31
|
-
"@opencode/schema": "2.0.
|
|
30
|
+
"@opencode/client": "2.0.23",
|
|
31
|
+
"@opencode/schema": "2.0.23",
|
|
32
32
|
"@simplewebauthn/server": "13.3.1",
|
|
33
33
|
"adm-zip": "^0.5.16",
|
|
34
34
|
"better-sqlite3": "^12.10.0",
|
package/server/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import 'reflect-metadata';
|
|
2
2
|
import express from 'express';
|
|
3
|
+
import { configureOpenCodeCredentials, openCodeCredentialSource } from './lib/opencode/auth.js';
|
|
3
4
|
import compression from 'compression';
|
|
4
5
|
import path from 'path';
|
|
5
6
|
import { spawn, spawnSync } from 'child_process';
|
|
@@ -601,6 +602,7 @@ const waitForReady = (...args) => openCodeNetworkRuntime.waitForReady(...args);
|
|
|
601
602
|
const normalizeApiPrefix = (...args) => openCodeNetworkRuntime.normalizeApiPrefix(...args);
|
|
602
603
|
const setDetectedOpenCodeApiPrefix = (...args) => openCodeNetworkRuntime.setDetectedOpenCodeApiPrefix(...args);
|
|
603
604
|
const buildOpenCodeUrl = (...args) => openCodeNetworkRuntime.buildOpenCodeUrl(...args);
|
|
605
|
+
configureOpenCodeCredentials(openCodeCredentialSource({ buildOpenCodeUrl, getOpenCodeAuthHeaders }));
|
|
604
606
|
const ensureOpenCodeApiPrefix = (...args) => openCodeNetworkRuntime.ensureOpenCodeApiPrefix(...args);
|
|
605
607
|
const scheduleOpenCodeApiDetection = (...args) => openCodeNetworkRuntime.scheduleOpenCodeApiDetection(...args);
|
|
606
608
|
|
|
@@ -755,6 +757,8 @@ const sessionTitleRuntime = createSessionTitleRuntime({
|
|
|
755
757
|
buildOpenCodeUrl,
|
|
756
758
|
getOpenCodeAuthHeaders,
|
|
757
759
|
readSessionMetadata: (sessionID) => sessionMetadataStore.get(sessionID),
|
|
760
|
+
mutateSessionMetadata: (sessionID, decide) => sessionMetadataStore.mutateSessionMetadata(sessionID, decide),
|
|
761
|
+
listSessionMetadata: () => sessionMetadataStore.getAll(),
|
|
758
762
|
persistSessionMetadata: (sessionID, patch) => persistSessionMetadataToStore(sessionID, patch),
|
|
759
763
|
publishSession: (session, directory) => {
|
|
760
764
|
const payload = { type: 'session.updated', properties: { info: session, directory } };
|
|
@@ -1043,6 +1047,9 @@ const openCodeWatcherRuntime = createOpenCodeWatcherRuntime({
|
|
|
1043
1047
|
// envelope's directory to route their own OpenCode calls to the right instance.
|
|
1044
1048
|
console.log('[session-title] listening for session events');
|
|
1045
1049
|
console.log('[session-goal] listening for session events');
|
|
1050
|
+
globalMessageStreamHub.subscribeStatus((status) => {
|
|
1051
|
+
if (status.type === 'connect') sessionTitleRuntime.processPayload({ type: 'server.connected' });
|
|
1052
|
+
});
|
|
1046
1053
|
globalMessageStreamHub.subscribeEvent((event) => {
|
|
1047
1054
|
const raw = event?.payload;
|
|
1048
1055
|
const payload = raw?.payload && typeof raw.payload === 'object' ? raw.payload : raw;
|
|
@@ -1293,26 +1300,6 @@ const openCodeLifecycleRuntime = createOpenCodeLifecycleRuntime({
|
|
|
1293
1300
|
getManagedOpenCodeShellEnvSnapshot: getLoginShellEnvSnapshot,
|
|
1294
1301
|
managedCapabilitiesRuntime,
|
|
1295
1302
|
getActiveSessionCount,
|
|
1296
|
-
// Most-recently-used directories first: OpenCode initializes each directory
|
|
1297
|
-
// lazily on first request (seconds on large session stores), so the
|
|
1298
|
-
// lifecycle warms these right after readiness — before the UI's first
|
|
1299
|
-
// interactive request would otherwise pay that cost.
|
|
1300
|
-
getWarmupDirectories: async () => {
|
|
1301
|
-
const settings = await readSettingsFromDiskMigrated().catch(() => null);
|
|
1302
|
-
if (!settings) return [];
|
|
1303
|
-
const directories = [];
|
|
1304
|
-
if (typeof settings.lastDirectory === 'string' && settings.lastDirectory) {
|
|
1305
|
-
directories.push(settings.lastDirectory);
|
|
1306
|
-
}
|
|
1307
|
-
const projects = Array.isArray(settings.projects) ? [...settings.projects] : [];
|
|
1308
|
-
projects.sort((a, b) => (b?.lastOpenedAt ?? 0) - (a?.lastOpenedAt ?? 0));
|
|
1309
|
-
for (const project of projects) {
|
|
1310
|
-
if (typeof project?.path === 'string' && project.path) {
|
|
1311
|
-
directories.push(project.path);
|
|
1312
|
-
}
|
|
1313
|
-
}
|
|
1314
|
-
return [...new Set(directories)];
|
|
1315
|
-
},
|
|
1316
1303
|
});
|
|
1317
1304
|
|
|
1318
1305
|
const restartOpenCode = (...args) => openCodeLifecycleRuntime.restartOpenCode(...args);
|
|
@@ -2188,6 +2175,7 @@ async function main(options = {}) {
|
|
|
2188
2175
|
};
|
|
2189
2176
|
},
|
|
2190
2177
|
stop: async (shutdownOptions = {}) => {
|
|
2178
|
+
sessionTitleRuntime.stop();
|
|
2191
2179
|
try {
|
|
2192
2180
|
unsubscribeSessionIndexEvents();
|
|
2193
2181
|
unsubscribeMessageQueueEvents();
|
|
@@ -204,6 +204,20 @@ export const createRemoteClientAuthRuntime = ({ fsPromises, path, crypto, storeP
|
|
|
204
204
|
});
|
|
205
205
|
};
|
|
206
206
|
|
|
207
|
+
const renameClient = async (id, label) => {
|
|
208
|
+
if (typeof label !== 'string' || !label.trim() || label.trim().length > MAX_LABEL_LENGTH) {
|
|
209
|
+
throw Object.assign(new Error('Client label must contain 1–80 characters'), { status: 400 });
|
|
210
|
+
}
|
|
211
|
+
return withStoreMutation(async () => {
|
|
212
|
+
const store = await readStore();
|
|
213
|
+
const client = store.clients.find((entry) => entry.id === id);
|
|
214
|
+
if (!client) return null;
|
|
215
|
+
client.label = label.trim();
|
|
216
|
+
await writeStore(store);
|
|
217
|
+
return publicClient(client);
|
|
218
|
+
});
|
|
219
|
+
};
|
|
220
|
+
|
|
207
221
|
const revokeClient = async (id) => {
|
|
208
222
|
if (typeof id !== 'string' || id.trim().length === 0) {
|
|
209
223
|
return { revoked: false };
|
|
@@ -264,6 +278,7 @@ export const createRemoteClientAuthRuntime = ({ fsPromises, path, crypto, storeP
|
|
|
264
278
|
listClients,
|
|
265
279
|
hasActiveRelayClients,
|
|
266
280
|
purgeRevokedClients,
|
|
281
|
+
renameClient,
|
|
267
282
|
revokeClient,
|
|
268
283
|
};
|
|
269
284
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, expect, it } from '
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
2
|
import fs from 'node:fs/promises';
|
|
3
3
|
import os from 'node:os';
|
|
4
4
|
import path from 'node:path';
|
|
@@ -17,6 +17,47 @@ const createRuntime = async () => {
|
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
describe('remote client auth runtime', () => {
|
|
20
|
+
it('persists renamed labels without changing credentials, metadata or revocation', async () => {
|
|
21
|
+
const { dir, runtime } = await createRuntime();
|
|
22
|
+
try {
|
|
23
|
+
const created = await runtime.createClient({ label: 'Phone', devicePlatform: 'iOS', usesRelay: true });
|
|
24
|
+
const renamed = await runtime.renameClient(created.client.id, ' Work phone ');
|
|
25
|
+
expect(renamed).toEqual({ ...created.client, label: 'Work phone' });
|
|
26
|
+
expect('tokenHash' in renamed).toBe(false);
|
|
27
|
+
const reopened = createRemoteClientAuthRuntime({ fsPromises: fs, path, crypto, storePath: path.join(dir, 'remote-clients.json') });
|
|
28
|
+
expect(await reopened.listClients()).toEqual([renamed]);
|
|
29
|
+
expect((await reopened.authenticateBearerToken(created.token))?.clientId).toBe(created.client.id);
|
|
30
|
+
await Promise.all([
|
|
31
|
+
runtime.renameClient(created.client.id, 'Archived phone'),
|
|
32
|
+
runtime.revokeClient(created.client.id),
|
|
33
|
+
]);
|
|
34
|
+
expect(await runtime.authenticateBearerToken(created.token)).toBe(null);
|
|
35
|
+
expect((await runtime.listClients())[0]).toMatchObject({ label: 'Archived phone', revokedAt: expect.any(String) });
|
|
36
|
+
} finally {
|
|
37
|
+
await fs.rm(dir, { recursive: true, force: true });
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('rejects invalid labels and failed writes without committing a new name', async () => {
|
|
42
|
+
const { dir, runtime } = await createRuntime();
|
|
43
|
+
try {
|
|
44
|
+
const created = await runtime.createClient({ label: 'Phone' });
|
|
45
|
+
for (const label of [undefined, null, 42, '', ' ', 'x'.repeat(81)]) {
|
|
46
|
+
await expect(runtime.renameClient(created.client.id, label)).rejects.toMatchObject({ status: 400 });
|
|
47
|
+
}
|
|
48
|
+
expect(await runtime.renameClient('missing', 'Name')).toBe(null);
|
|
49
|
+
const failing = createRemoteClientAuthRuntime({
|
|
50
|
+
fsPromises: { ...fs, writeFile: async () => { throw new Error('Write failed'); } },
|
|
51
|
+
path, crypto, storePath: path.join(dir, 'remote-clients.json'),
|
|
52
|
+
});
|
|
53
|
+
await expect(failing.renameClient(created.client.id, 'New name')).rejects.toThrow('Write failed');
|
|
54
|
+
expect(await runtime.listClients()).toEqual([created.client]);
|
|
55
|
+
expect((await runtime.authenticateBearerToken(created.token))?.ok).toBe(true);
|
|
56
|
+
} finally {
|
|
57
|
+
await fs.rm(dir, { recursive: true, force: true });
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
20
61
|
it('creates, authenticates, lists, and revokes client tokens', async () => {
|
|
21
62
|
const { dir, runtime } = await createRuntime();
|
|
22
63
|
try {
|
|
@@ -54,6 +54,7 @@ The following functions are exported and used by the web server:
|
|
|
54
54
|
- `validateWorktreeCreate(directory, input)`: Validate worktree creation parameters (mode, branchName, startRef, upstream config).
|
|
55
55
|
- `createWorktree(directory, input)`: Create a new worktree (supports 'new' and 'existing' modes, upstream setup).
|
|
56
56
|
- `removeWorktree(directory, input)`: Remove a worktree (optionally delete local branch).
|
|
57
|
+
- `removeWorktree(directory, input, { beforeRemove })`: optional host hook after primary-workspace/catalog validation, immediately before removal of a matched worktree or managed orphan. The hook never runs for unrelated orphan paths. Production hosts use the official v2 `debug.location.evict` with the validated target and current upstream URL/auth; the 2s budget returns `released`, `failed`, `timeout`, or `unavailable`. Eviction is best effort and never counts as successful Git removal. DELETE responses include `locationRelease` when attempted. Shared-service explicit removal retains the existing force-delete semantics for that target; it is not automatic idle cleanup and does not interrupt unrelated sessions. An eviction acknowledgement is cache detachment, not a guarantee that active borrowers released OS handles. Queue rollback and topology publication remain tied to the Git result.
|
|
57
58
|
- `isLinkedWorktree(directory)`: Check if directory is a linked worktree (not primary).
|
|
58
59
|
|
|
59
60
|
### Commit and Remote Operations
|
package/server/lib/git/routes.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export function registerGitRoutes(app, { messageQueueService = null, createRequestID = () => globalThis.crypto.randomUUID(), broadcastWorktreeTopologyChanged = () => {} } = {}) {
|
|
1
|
+
export function registerGitRoutes(app, { messageQueueService = null, createRequestID = () => globalThis.crypto.randomUUID(), broadcastWorktreeTopologyChanged = () => {}, releaseWorktreeLocation } = {}) {
|
|
2
2
|
const normalizeWorktreePath = (value) => typeof value === 'string' ? value.trim().replace(/\\/g, '/').replace(/\/+$/, '') : '';
|
|
3
3
|
const lifecycleOperations = new Map();
|
|
4
4
|
const acquireWorktreeLifecycleOperation = (runtimeKey, directory) => {
|
|
@@ -1282,11 +1282,14 @@ export function registerGitRoutes(app, { messageQueueService = null, createReque
|
|
|
1282
1282
|
}
|
|
1283
1283
|
|
|
1284
1284
|
let result;
|
|
1285
|
+
let locationRelease;
|
|
1285
1286
|
try {
|
|
1286
1287
|
result = await removeWorktree(directory, {
|
|
1287
1288
|
directory: worktreeDirectory,
|
|
1288
1289
|
deleteLocalBranch: req.body?.deleteLocalBranch === true,
|
|
1289
|
-
})
|
|
1290
|
+
}, ...(releaseWorktreeLocation ? [{ beforeRemove: async (target) => {
|
|
1291
|
+
locationRelease = await releaseWorktreeLocation(target);
|
|
1292
|
+
} }] : []));
|
|
1290
1293
|
} catch (error) {
|
|
1291
1294
|
if (messageQueueService) {
|
|
1292
1295
|
try {
|
|
@@ -1328,7 +1331,7 @@ export function registerGitRoutes(app, { messageQueueService = null, createReque
|
|
|
1328
1331
|
console.warn('Failed to roll back message queue worktree deletion');
|
|
1329
1332
|
}
|
|
1330
1333
|
}
|
|
1331
|
-
return res.json({ success: false });
|
|
1334
|
+
return res.json({ success: false, ...(locationRelease ? { locationRelease } : {}) });
|
|
1332
1335
|
}
|
|
1333
1336
|
|
|
1334
1337
|
notifyWorktreeTopologyChanged(directory, worktreeDirectory, 'removed');
|
|
@@ -1346,7 +1349,7 @@ export function registerGitRoutes(app, { messageQueueService = null, createReque
|
|
|
1346
1349
|
return res.status(500).json({ error: 'Failed to commit worktree queue deletion' });
|
|
1347
1350
|
}
|
|
1348
1351
|
}
|
|
1349
|
-
res.json({ success: true });
|
|
1352
|
+
res.json({ success: true, ...(locationRelease ? { locationRelease } : {}) });
|
|
1350
1353
|
} finally {
|
|
1351
1354
|
releaseWorktreeLifecycleOperation(operation);
|
|
1352
1355
|
}
|
|
@@ -69,6 +69,40 @@ const createMockResponse = () => {
|
|
|
69
69
|
};
|
|
70
70
|
};
|
|
71
71
|
|
|
72
|
+
describe('worktree eviction best effort', () => {
|
|
73
|
+
it.each(['released', 'failed', 'timeout', 'unavailable'])('preserves successful deletion with release outcome %s', async (state) => {
|
|
74
|
+
const { app, getRoute } = createRouteRegistry();
|
|
75
|
+
const releaseWorktreeLocation = vi.fn(async () => ({ state }));
|
|
76
|
+
const broadcastWorktreeTopologyChanged = vi.fn();
|
|
77
|
+
registerGitRoutes(app, { releaseWorktreeLocation, broadcastWorktreeTopologyChanged });
|
|
78
|
+
gitLibraries.removeWorktree.mockImplementation(async (_root, _input, options) => {
|
|
79
|
+
await options.beforeRemove('/validated/worktree');
|
|
80
|
+
return true;
|
|
81
|
+
});
|
|
82
|
+
const response = createMockResponse();
|
|
83
|
+
await getRoute('DELETE', '/api/git/worktrees')({ query: { directory: '/repo' }, body: { directory: '/requested/worktree' } }, response);
|
|
84
|
+
expect(releaseWorktreeLocation).toHaveBeenCalledWith('/validated/worktree');
|
|
85
|
+
expect(response.body).toEqual({ success: true, locationRelease: { state } });
|
|
86
|
+
expect(broadcastWorktreeTopologyChanged).toHaveBeenCalledTimes(1);
|
|
87
|
+
});
|
|
88
|
+
it('does not publish topology when removal fails after successful release', async () => {
|
|
89
|
+
const { app, getRoute } = createRouteRegistry();
|
|
90
|
+
const broadcastWorktreeTopologyChanged = vi.fn();
|
|
91
|
+
registerGitRoutes(app, { releaseWorktreeLocation: async () => ({ state: 'released' }), broadcastWorktreeTopologyChanged });
|
|
92
|
+
gitLibraries.removeWorktree.mockImplementation(async (_root, _input, options) => {
|
|
93
|
+
await options.beforeRemove('/repo/worktree');
|
|
94
|
+
throw new Error('deletion failed');
|
|
95
|
+
});
|
|
96
|
+
const response = createMockResponse();
|
|
97
|
+
const log = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
98
|
+
try {
|
|
99
|
+
await getRoute('DELETE', '/api/git/worktrees')({ query: { directory: '/repo' }, body: { directory: '/repo/worktree' } }, response);
|
|
100
|
+
} finally { log.mockRestore(); }
|
|
101
|
+
expect(response.statusCode).toBe(500);
|
|
102
|
+
expect(broadcastWorktreeTopologyChanged).not.toHaveBeenCalled();
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
|
72
106
|
describe('git routes batch discover', () => {
|
|
73
107
|
beforeEach(() => {
|
|
74
108
|
gitLibraries.isGitRepository.mockReset();
|
|
@@ -3851,7 +3851,7 @@ export async function getWorktreeBootstrapStatus(directory) {
|
|
|
3851
3851
|
};
|
|
3852
3852
|
}
|
|
3853
3853
|
|
|
3854
|
-
export async function removeWorktree(directory, input = {}) {
|
|
3854
|
+
export async function removeWorktree(directory, input = {}, { beforeRemove } = {}) {
|
|
3855
3855
|
const targetDirectory = normalizeDirectoryPath(input?.directory);
|
|
3856
3856
|
if (!targetDirectory) {
|
|
3857
3857
|
throw new Error('Worktree directory is required');
|
|
@@ -3887,6 +3887,7 @@ export async function removeWorktree(directory, input = {}) {
|
|
|
3887
3887
|
|
|
3888
3888
|
const targetExists = await checkPathExists(targetDirectory);
|
|
3889
3889
|
if (targetExists && isManagedOrphan) {
|
|
3890
|
+
await beforeRemove?.(targetDirectory);
|
|
3890
3891
|
await fsp.rm(targetDirectory, { recursive: true, force: true });
|
|
3891
3892
|
}
|
|
3892
3893
|
|
|
@@ -3901,6 +3902,7 @@ export async function removeWorktree(directory, input = {}) {
|
|
|
3901
3902
|
return true;
|
|
3902
3903
|
}
|
|
3903
3904
|
|
|
3905
|
+
await beforeRemove?.(matchedEntry.worktree);
|
|
3904
3906
|
await runGitCommandOrThrow(
|
|
3905
3907
|
context.primaryWorktree,
|
|
3906
3908
|
['worktree', 'remove', '--force', matchedEntry.worktree],
|
|
@@ -39,8 +39,8 @@ call Anthropic/OpenAI/plugin SDKs, and does not use the `openai` npm package.
|
|
|
39
39
|
`openchamber-text` agent (same temp root, same deny-all verification and
|
|
40
40
|
`session.create` / `session.remove` lifecycle as the attachment path below):
|
|
41
41
|
|
|
42
|
-
1. Ensure `.opencode/agent/openchamber-text.md` and verify its
|
|
43
|
-
permission rule is deny-all before creating the session.
|
|
42
|
+
1. Ensure `.opencode/agent/openchamber-text.md` and verify its last catch-all
|
|
43
|
+
permission rule is deny-all, with only deny rules after it, before creating the session.
|
|
44
44
|
2. `session.create({ location, agent: 'openchamber-text', model, metadata })`.
|
|
45
45
|
3. `session.generate({ sessionID, prompt })` → `{ text }`. One model call with
|
|
46
46
|
the session's provider context, no tool loop, no messages written. System
|
|
@@ -62,8 +62,10 @@ data-URL file parts:
|
|
|
62
62
|
|
|
63
63
|
1. Ensure the throwaway temp directory agent markdown (`openchamber-llm`,
|
|
64
64
|
permissions array ending in `action:* resource:* effect:deny`).
|
|
65
|
-
2. `agent.get({ agentID: 'openchamber-llm', location })` and require the **
|
|
66
|
-
permissions rule to be deny-all.
|
|
65
|
+
2. `agent.get({ agentID: 'openchamber-llm', location })` and require the **last catch-all**
|
|
66
|
+
permissions rule to be deny-all, with only deny rules after it. This accepts
|
|
67
|
+
OpenCode's appended `browser` deny while rejecting trailing allow/ask rules.
|
|
68
|
+
Failure → `llm_attachment_generation_unavailable`
|
|
67
69
|
**before** any `session.prompt`. Title is never treated as permission isolation.
|
|
68
70
|
3. `session.create({ location, agent, model: { id, providerID, variant? },
|
|
69
71
|
metadata: { openchamber: { llm: { purpose: 'chat-completions' } } } })`
|
|
@@ -18,7 +18,7 @@ const positiveMs = (value, fallback) => (
|
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* Agent frontmatter for official client agent permissions (action/resource/effect).
|
|
21
|
-
* The final
|
|
21
|
+
* The final catch-all must deny, with only denies after it; verified before prompt.
|
|
22
22
|
*/
|
|
23
23
|
const AGENT_MARKDOWN = `---
|
|
24
24
|
mode: primary
|
|
@@ -308,7 +308,7 @@ const readGeneratorAgent = async ({ client, location, signal, agentID, waitMs, p
|
|
|
308
308
|
};
|
|
309
309
|
|
|
310
310
|
/**
|
|
311
|
-
* Verify the generator agent
|
|
311
|
+
* Verify the generator agent has a deny-all rule followed only by denies.
|
|
312
312
|
* Title alone is never treated as isolation. Failure must happen before prompt.
|
|
313
313
|
*/
|
|
314
314
|
export async function assertLlmAgentDenyAll({
|
|
@@ -336,13 +336,12 @@ export async function assertLlmAgentDenyAll({
|
|
|
336
336
|
'llm_attachment_generation_unavailable',
|
|
337
337
|
);
|
|
338
338
|
}
|
|
339
|
-
const
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
) {
|
|
339
|
+
const lastCatchAll = permissions.findLastIndex(
|
|
340
|
+
(rule) => rule?.action === '*' && rule?.resource === '*',
|
|
341
|
+
);
|
|
342
|
+
const isolated = lastCatchAll > -1
|
|
343
|
+
&& permissions.slice(lastCatchAll).every((rule) => rule?.effect === 'deny');
|
|
344
|
+
if (!isolated) {
|
|
346
345
|
failGenerate(
|
|
347
346
|
'OpenCode LLM agent final permission is not deny-all; generation blocked',
|
|
348
347
|
'llm_attachment_generation_unavailable',
|
|
@@ -127,8 +127,16 @@ function textSessionClient({ generate = vi.fn(async () => ({ text: 'reply' })),
|
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
describe('generateOpenCodeText — text path', () => {
|
|
130
|
-
it
|
|
131
|
-
|
|
130
|
+
it.each([
|
|
131
|
+
{ name: 'deny-all alone', suffix: [] },
|
|
132
|
+
{ name: 'appended browser deny', suffix: [{ action: 'browser', resource: '*', effect: 'deny' }] },
|
|
133
|
+
])('runs session.generate in a verified throwaway session ($name) and removes it', async ({ suffix }) => {
|
|
134
|
+
const { client, agentGet, create, generate, remove, prompt } = textSessionClient({
|
|
135
|
+
agentGet: vi.fn(async () => ({
|
|
136
|
+
...denyAllTextAgent,
|
|
137
|
+
data: { ...denyAllTextAgent.data, permissions: [...denyAllTextAgent.data.permissions, ...suffix] },
|
|
138
|
+
})),
|
|
139
|
+
})
|
|
132
140
|
const ensureTempDirectory = vi.fn(async ({ agentName, agentMarkdown }) => {
|
|
133
141
|
expect(agentName).toBe(_test.TEXT_AGENT_NAME)
|
|
134
142
|
expect(agentMarkdown).toBe(_test.TEXT_AGENT_MARKDOWN)
|
|
@@ -188,10 +196,23 @@ describe('generateOpenCodeText — text path', () => {
|
|
|
188
196
|
}
|
|
189
197
|
})
|
|
190
198
|
|
|
191
|
-
it(
|
|
199
|
+
it.each([
|
|
200
|
+
{ name: 'no catch-all', permissions: [{ action: 'browser', resource: '*', effect: 'deny' }] },
|
|
201
|
+
...['allow', 'ask'].flatMap((effect) => ['*', 'edit'].map((action) => ({
|
|
202
|
+
name: `trailing ${action} ${effect} before browser deny`,
|
|
203
|
+
permissions: [
|
|
204
|
+
...denyAllTextAgent.data.permissions,
|
|
205
|
+
{ action, resource: '*', effect },
|
|
206
|
+
{ action: 'browser', resource: '*', effect: 'deny' },
|
|
207
|
+
],
|
|
208
|
+
}))),
|
|
209
|
+
{ name: 'missing permissions', permissions: undefined },
|
|
210
|
+
{ name: 'empty permissions', permissions: [] },
|
|
211
|
+
{ name: 'invalid trailing rule', permissions: [...denyAllTextAgent.data.permissions, null] },
|
|
212
|
+
])('blocks before creating a session: $name', async ({ permissions }) => {
|
|
192
213
|
const { client, create, generate } = textSessionClient({
|
|
193
214
|
agentGet: vi.fn(async () => ({
|
|
194
|
-
data: { id: 'openchamber-text', permissions
|
|
215
|
+
data: { id: 'openchamber-text', permissions },
|
|
195
216
|
})),
|
|
196
217
|
})
|
|
197
218
|
await expect(generateOpenCodeText({
|
|
@@ -316,8 +337,14 @@ describe('generateOpenCodeText — text path', () => {
|
|
|
316
337
|
})
|
|
317
338
|
|
|
318
339
|
describe('generateOpenCodeText — attachment session path', () => {
|
|
319
|
-
it(
|
|
320
|
-
|
|
340
|
+
it.each([
|
|
341
|
+
{ name: 'deny-all alone', suffix: [] },
|
|
342
|
+
{ name: 'appended browser deny', suffix: [{ action: 'browser', resource: '*', effect: 'deny' }] },
|
|
343
|
+
])('verifies deny-all ($name) before prompt, prompts with files, waits, lists, and removes', async ({ suffix }) => {
|
|
344
|
+
const agentGet = vi.fn(async () => ({
|
|
345
|
+
...denyAllAgent,
|
|
346
|
+
data: { ...denyAllAgent.data, permissions: [...denyAllAgent.data.permissions, ...suffix] },
|
|
347
|
+
}))
|
|
321
348
|
const create = vi.fn(async () => ({ id: 'ses_tmp' }))
|
|
322
349
|
const put = vi.fn(async () => undefined)
|
|
323
350
|
const prompt = vi.fn(async () => ({ id: 'inbox_1', type: 'user' }))
|
|
@@ -246,7 +246,7 @@ export const createMessageQueueService = ({ dbPath, getRuntimeConfig = () => nul
|
|
|
246
246
|
totalPasteLength += reference.text.length;
|
|
247
247
|
if (totalPasteLength > MAX_COMPOSER_PASTE_TOTAL_LENGTH) fail('validation_error');
|
|
248
248
|
canonical += reference.text;
|
|
249
|
-
} else if (reference.kind === 'skill' && Object.keys(reference).every((key) => ['id', 'kind', 'start', 'end', 'display', 'skillName'].includes(key)) && nonEmptyString(reference.skillName, MAX_COMPOSER_SKILL_NAME) && /^[
|
|
249
|
+
} else if (reference.kind === 'skill' && Object.keys(reference).every((key) => ['id', 'kind', 'start', 'end', 'display', 'skillName'].includes(key)) && nonEmptyString(reference.skillName, MAX_COMPOSER_SKILL_NAME) && /^[^\s/\\[\]\r\n]+$/u.test(reference.skillName) && [`/${reference.skillName}`, `/\u2003${reference.skillName}`, `@${reference.skillName}`, `@\u2003${reference.skillName}`].includes(reference.display)) canonical += `[skill:${reference.skillName}]`;
|
|
250
250
|
else if (reference.kind === 'command' && Object.keys(reference).every((key) => ['id', 'kind', 'start', 'end', 'display', 'commandName', 'reference'].includes(key)) && nonEmptyString(reference.commandName, MAX_COMPOSER_COMMAND_NAME) && /^[\p{L}\p{N}._/-]+$/u.test(reference.commandName) && nonEmptyString(reference.reference, MAX_COMPOSER_COMMAND_REFERENCE) && !/[\]\r\n]/u.test(reference.reference) && (reference.display === `/${reference.commandName}` || reference.display === `/\u2003${reference.commandName}`)) canonical += `[command:${reference.reference}]`;
|
|
251
251
|
else fail('validation_error');
|
|
252
252
|
previousEnd = reference.end;
|