@jingyi0605/codingns4dsh 0.2.0-beta.2 → 0.2.0-rc.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.en.md +4 -4
- package/README.md +18 -7
- package/assets/provider-icons/antigravity.svg +39 -0
- package/assets/provider-icons/codebuddy.svg +37 -0
- package/assets/provider-icons/cursor-cli.svg +1 -0
- package/assets/provider-icons/kiro-cli.svg +11 -0
- package/assets/provider-icons/minimax-code.svg +4 -0
- package/assets/provider-icons/qoder-cn.svg +5 -0
- package/assets/provider-icons/qoder.svg +5 -0
- package/assets/provider-icons/workbuddy.svg +29 -0
- package/assets/provider-icons/zcode.svg +14 -0
- package/data/build/dist/bootstrap/dsh-peer-host-preboot-shim.d.ts.map +1 -1
- package/data/build/dist/bootstrap/dsh-peer-host-preboot-shim.js +78 -1
- package/data/build/dist/bootstrap/dsh-peer-host-preboot-shim.js.map +1 -1
- package/data/build/dist/client/account-bar.d.ts +2 -1
- package/data/build/dist/client/account-bar.d.ts.map +1 -1
- package/data/build/dist/client/account-bar.js +21 -16
- package/data/build/dist/client/account-bar.js.map +1 -1
- package/data/build/dist/client/bundle.js +8147 -2158
- package/data/build/dist/client/bundle.js.map +1 -1
- package/data/build/dist/client/cli-catalog.d.ts +1 -1
- package/data/build/dist/client/cli-catalog.d.ts.map +1 -1
- package/data/build/dist/client/cli-catalog.js +11 -5
- package/data/build/dist/client/cli-catalog.js.map +1 -1
- package/data/build/dist/client/cli-slots.d.ts.map +1 -1
- package/data/build/dist/client/cli-slots.js +127 -21
- package/data/build/dist/client/cli-slots.js.map +1 -1
- package/data/build/dist/client/debug/ui.d.ts +2 -1
- package/data/build/dist/client/debug/ui.d.ts.map +1 -1
- package/data/build/dist/client/debug/ui.js +41 -34
- package/data/build/dist/client/debug/ui.js.map +1 -1
- package/data/build/dist/client/delegate-command.d.ts +17 -0
- package/data/build/dist/client/delegate-command.d.ts.map +1 -0
- package/data/build/dist/client/delegate-command.js +597 -0
- package/data/build/dist/client/delegate-command.js.map +1 -0
- package/data/build/dist/client/delegate-plan.d.ts +40 -0
- package/data/build/dist/client/delegate-plan.d.ts.map +1 -0
- package/data/build/dist/client/delegate-plan.js +63 -0
- package/data/build/dist/client/delegate-plan.js.map +1 -0
- package/data/build/dist/client/delegate-ui-dom.d.ts +86 -0
- package/data/build/dist/client/delegate-ui-dom.d.ts.map +1 -0
- package/data/build/dist/client/delegate-ui-dom.js +647 -0
- package/data/build/dist/client/delegate-ui-dom.js.map +1 -0
- package/data/build/dist/client/external-tool-stream.d.ts.map +1 -1
- package/data/build/dist/client/external-tool-stream.js +44 -36
- package/data/build/dist/client/external-tool-stream.js.map +1 -1
- package/data/build/dist/client/features/cli-adapters.d.ts +7 -2
- package/data/build/dist/client/features/cli-adapters.d.ts.map +1 -1
- package/data/build/dist/client/features/cli-adapters.js +153 -95
- package/data/build/dist/client/features/cli-adapters.js.map +1 -1
- package/data/build/dist/client/features/debug.d.ts.map +1 -1
- package/data/build/dist/client/features/debug.js +5 -3
- package/data/build/dist/client/features/debug.js.map +1 -1
- package/data/build/dist/client/features/file-management.d.ts.map +1 -1
- package/data/build/dist/client/features/file-management.js +7 -4
- package/data/build/dist/client/features/file-management.js.map +1 -1
- package/data/build/dist/client/features/lan-access-panel.js +10 -10
- package/data/build/dist/client/features/lan-access-panel.js.map +1 -1
- package/data/build/dist/client/features/lan-access.d.ts.map +1 -1
- package/data/build/dist/client/features/lan-access.js +8 -3
- package/data/build/dist/client/features/lan-access.js.map +1 -1
- package/data/build/dist/client/features/login-protection-panel.d.ts.map +1 -1
- package/data/build/dist/client/features/login-protection-panel.js +9 -7
- package/data/build/dist/client/features/login-protection-panel.js.map +1 -1
- package/data/build/dist/client/features/login-protection.js +2 -2
- package/data/build/dist/client/features/login-protection.js.map +1 -1
- package/data/build/dist/client/features/mobile-access-panel.d.ts.map +1 -1
- package/data/build/dist/client/features/mobile-access-panel.js +8 -7
- package/data/build/dist/client/features/mobile-access-panel.js.map +1 -1
- package/data/build/dist/client/features/mobile-access.d.ts.map +1 -1
- package/data/build/dist/client/features/mobile-access.js +6 -2
- package/data/build/dist/client/features/mobile-access.js.map +1 -1
- package/data/build/dist/client/features/peer-host.d.ts +6 -4
- package/data/build/dist/client/features/peer-host.d.ts.map +1 -1
- package/data/build/dist/client/features/peer-host.js +459 -27
- package/data/build/dist/client/features/peer-host.js.map +1 -1
- package/data/build/dist/client/features/reverse-proxy.js +2 -2
- package/data/build/dist/client/features/reverse-proxy.js.map +1 -1
- package/data/build/dist/client/features/terminal-enhancement.js +2 -2
- package/data/build/dist/client/features/terminal-enhancement.js.map +1 -1
- package/data/build/dist/client/features/workspace-session-enhancement-panel.js +1 -1
- package/data/build/dist/client/features/workspace-session-enhancement-panel.js.map +1 -1
- package/data/build/dist/client/features/workspace-session-enhancement.d.ts.map +1 -1
- package/data/build/dist/client/features/workspace-session-enhancement.js +8 -3
- package/data/build/dist/client/features/workspace-session-enhancement.js.map +1 -1
- package/data/build/dist/client/file-management-dom.d.ts +3 -0
- package/data/build/dist/client/file-management-dom.d.ts.map +1 -1
- package/data/build/dist/client/file-management-dom.js +41 -30
- package/data/build/dist/client/file-management-dom.js.map +1 -1
- package/data/build/dist/client/git-history-graph.d.ts +2 -2
- package/data/build/dist/client/git-history-graph.d.ts.map +1 -1
- package/data/build/dist/client/git-history-graph.js +2 -2
- package/data/build/dist/client/git-history-graph.js.map +1 -1
- package/data/build/dist/client/git-management.d.ts +2 -0
- package/data/build/dist/client/git-management.d.ts.map +1 -1
- package/data/build/dist/client/git-management.js +103 -75
- package/data/build/dist/client/git-management.js.map +1 -1
- package/data/build/dist/client/git-workspace-events.d.ts +7 -0
- package/data/build/dist/client/git-workspace-events.d.ts.map +1 -0
- package/data/build/dist/client/git-workspace-events.js +32 -0
- package/data/build/dist/client/git-workspace-events.js.map +1 -0
- package/data/build/dist/client/index.d.ts +2 -0
- package/data/build/dist/client/index.d.ts.map +1 -1
- package/data/build/dist/client/index.js +6 -5
- package/data/build/dist/client/index.js.map +1 -1
- package/data/build/dist/client/locale.d.ts +7 -0
- package/data/build/dist/client/locale.d.ts.map +1 -1
- package/data/build/dist/client/locale.js +140 -42
- package/data/build/dist/client/locale.js.map +1 -1
- package/data/build/dist/client/locales/debug.d.ts +9 -0
- package/data/build/dist/client/locales/debug.d.ts.map +1 -0
- package/data/build/dist/client/locales/debug.js +163 -0
- package/data/build/dist/client/locales/debug.js.map +1 -0
- package/data/build/dist/client/locales/fileWorkspace.d.ts +9 -0
- package/data/build/dist/client/locales/fileWorkspace.d.ts.map +1 -0
- package/data/build/dist/client/locales/fileWorkspace.js +139 -0
- package/data/build/dist/client/locales/fileWorkspace.js.map +1 -0
- package/data/build/dist/client/locales/git.d.ts +9 -0
- package/data/build/dist/client/locales/git.d.ts.map +1 -0
- package/data/build/dist/client/locales/git.js +223 -0
- package/data/build/dist/client/locales/git.js.map +1 -0
- package/data/build/dist/client/locales/panels.d.ts +9 -0
- package/data/build/dist/client/locales/panels.d.ts.map +1 -0
- package/data/build/dist/client/locales/panels.js +33 -0
- package/data/build/dist/client/locales/panels.js.map +1 -0
- package/data/build/dist/client/locales/peerHost.d.ts +9 -0
- package/data/build/dist/client/locales/peerHost.d.ts.map +1 -0
- package/data/build/dist/client/locales/peerHost.js +153 -0
- package/data/build/dist/client/locales/peerHost.js.map +1 -0
- package/data/build/dist/client/locales/peerHostWorkspace.d.ts +9 -0
- package/data/build/dist/client/locales/peerHostWorkspace.d.ts.map +1 -0
- package/data/build/dist/client/locales/peerHostWorkspace.js +69 -0
- package/data/build/dist/client/locales/peerHostWorkspace.js.map +1 -0
- package/data/build/dist/client/locales/terminalAuth.d.ts +9 -0
- package/data/build/dist/client/locales/terminalAuth.d.ts.map +1 -0
- package/data/build/dist/client/locales/terminalAuth.js +71 -0
- package/data/build/dist/client/locales/terminalAuth.js.map +1 -0
- package/data/build/dist/client/locales/usage.d.ts +9 -0
- package/data/build/dist/client/locales/usage.d.ts.map +1 -0
- package/data/build/dist/client/locales/usage.js +215 -0
- package/data/build/dist/client/locales/usage.js.map +1 -0
- package/data/build/dist/client/mobile-session-interaction.d.ts +44 -0
- package/data/build/dist/client/mobile-session-interaction.d.ts.map +1 -0
- package/data/build/dist/client/mobile-session-interaction.js +308 -0
- package/data/build/dist/client/mobile-session-interaction.js.map +1 -0
- package/data/build/dist/client/mobile-sidebar-gestures.d.ts +55 -7
- package/data/build/dist/client/mobile-sidebar-gestures.d.ts.map +1 -1
- package/data/build/dist/client/mobile-sidebar-gestures.js +339 -40
- package/data/build/dist/client/mobile-sidebar-gestures.js.map +1 -1
- package/data/build/dist/client/mobile-sidebar-rail-dom.d.ts +10 -2
- package/data/build/dist/client/mobile-sidebar-rail-dom.d.ts.map +1 -1
- package/data/build/dist/client/mobile-sidebar-rail-dom.js +14 -6
- package/data/build/dist/client/mobile-sidebar-rail-dom.js.map +1 -1
- package/data/build/dist/client/model-catalog-cache.d.ts +5 -0
- package/data/build/dist/client/model-catalog-cache.d.ts.map +1 -0
- package/data/build/dist/client/model-catalog-cache.js +58 -0
- package/data/build/dist/client/model-catalog-cache.js.map +1 -0
- package/data/build/dist/client/peer-host-aggregate-refresh.d.ts +7 -0
- package/data/build/dist/client/peer-host-aggregate-refresh.d.ts.map +1 -1
- package/data/build/dist/client/peer-host-aggregate-refresh.js +13 -1
- package/data/build/dist/client/peer-host-aggregate-refresh.js.map +1 -1
- package/data/build/dist/client/peer-host-connection-button.d.ts +14 -1
- package/data/build/dist/client/peer-host-connection-button.d.ts.map +1 -1
- package/data/build/dist/client/peer-host-connection-button.js +12 -4
- package/data/build/dist/client/peer-host-connection-button.js.map +1 -1
- package/data/build/dist/client/peer-host-management-panel.d.ts +3 -0
- package/data/build/dist/client/peer-host-management-panel.d.ts.map +1 -1
- package/data/build/dist/client/peer-host-management-panel.js +83 -79
- package/data/build/dist/client/peer-host-management-panel.js.map +1 -1
- package/data/build/dist/client/peer-host-native-projection.d.ts +17 -1
- package/data/build/dist/client/peer-host-native-projection.d.ts.map +1 -1
- package/data/build/dist/client/peer-host-native-projection.js +54 -8
- package/data/build/dist/client/peer-host-native-projection.js.map +1 -1
- package/data/build/dist/client/peer-host-native-store-projection.d.ts +6 -2
- package/data/build/dist/client/peer-host-native-store-projection.d.ts.map +1 -1
- package/data/build/dist/client/peer-host-native-store-projection.js +308 -10
- package/data/build/dist/client/peer-host-native-store-projection.js.map +1 -1
- package/data/build/dist/client/peer-host-scoped-client.js +5 -5
- package/data/build/dist/client/peer-host-scoped-client.js.map +1 -1
- package/data/build/dist/client/peer-host-workspace-tab.d.ts +8 -0
- package/data/build/dist/client/peer-host-workspace-tab.d.ts.map +1 -1
- package/data/build/dist/client/peer-host-workspace-tab.js +48 -28
- package/data/build/dist/client/peer-host-workspace-tab.js.map +1 -1
- package/data/build/dist/client/provider-icon-assets.js +18 -0
- package/data/build/dist/client/provider-icon-assets.js.map +1 -1
- package/data/build/dist/client/provider-icons.d.ts +8 -2
- package/data/build/dist/client/provider-icons.d.ts.map +1 -1
- package/data/build/dist/client/provider-icons.js +28 -7
- package/data/build/dist/client/provider-icons.js.map +1 -1
- package/data/build/dist/client/pwa-install-prompt.d.ts +4 -1
- package/data/build/dist/client/pwa-install-prompt.d.ts.map +1 -1
- package/data/build/dist/client/pwa-install-prompt.js +8 -7
- package/data/build/dist/client/pwa-install-prompt.js.map +1 -1
- package/data/build/dist/client/quick-phrase-layout.d.ts +11 -0
- package/data/build/dist/client/quick-phrase-layout.d.ts.map +1 -1
- package/data/build/dist/client/quick-phrase-layout.js +20 -0
- package/data/build/dist/client/quick-phrase-layout.js.map +1 -1
- package/data/build/dist/client/quick-phrase-slot.d.ts.map +1 -1
- package/data/build/dist/client/quick-phrase-slot.js +20 -8
- package/data/build/dist/client/quick-phrase-slot.js.map +1 -1
- package/data/build/dist/client/remote-web-context.js +10 -6
- package/data/build/dist/client/remote-web-context.js.map +1 -1
- package/data/build/dist/client/service-tier.d.ts +54 -0
- package/data/build/dist/client/service-tier.d.ts.map +1 -0
- package/data/build/dist/client/service-tier.js +78 -0
- package/data/build/dist/client/service-tier.js.map +1 -0
- package/data/build/dist/client/session-changed-files-view.d.ts +8 -1
- package/data/build/dist/client/session-changed-files-view.d.ts.map +1 -1
- package/data/build/dist/client/session-changed-files-view.js +71 -28
- package/data/build/dist/client/session-changed-files-view.js.map +1 -1
- package/data/build/dist/client/settings-anchor.d.ts +9 -3
- package/data/build/dist/client/settings-anchor.d.ts.map +1 -1
- package/data/build/dist/client/settings-anchor.js +8 -2
- package/data/build/dist/client/settings-anchor.js.map +1 -1
- package/data/build/dist/client/settings-promo-styles.d.ts +28 -0
- package/data/build/dist/client/settings-promo-styles.d.ts.map +1 -0
- package/data/build/dist/client/settings-promo-styles.js +58 -0
- package/data/build/dist/client/settings-promo-styles.js.map +1 -0
- package/data/build/dist/client/settings-promo.d.ts +14 -0
- package/data/build/dist/client/settings-promo.d.ts.map +1 -0
- package/data/build/dist/client/settings-promo.js +68 -0
- package/data/build/dist/client/settings-promo.js.map +1 -0
- package/data/build/dist/client/settings-section.d.ts +2 -1
- package/data/build/dist/client/settings-section.d.ts.map +1 -1
- package/data/build/dist/client/settings-section.js +10 -7
- package/data/build/dist/client/settings-section.js.map +1 -1
- package/data/build/dist/client/subscription-slot.d.ts +4 -1
- package/data/build/dist/client/subscription-slot.d.ts.map +1 -1
- package/data/build/dist/client/subscription-slot.js +497 -56
- package/data/build/dist/client/subscription-slot.js.map +1 -1
- package/data/build/dist/client/terminal/model.d.ts +94 -4
- package/data/build/dist/client/terminal/model.d.ts.map +1 -1
- package/data/build/dist/client/terminal/model.js +392 -33
- package/data/build/dist/client/terminal/model.js.map +1 -1
- package/data/build/dist/client/terminal/recovery.d.ts +16 -1
- package/data/build/dist/client/terminal/recovery.d.ts.map +1 -1
- package/data/build/dist/client/terminal/recovery.js +101 -39
- package/data/build/dist/client/terminal/recovery.js.map +1 -1
- package/data/build/dist/client/terminal/styles.d.ts +10 -0
- package/data/build/dist/client/terminal/styles.d.ts.map +1 -1
- package/data/build/dist/client/terminal/styles.js +27 -3
- package/data/build/dist/client/terminal/styles.js.map +1 -1
- package/data/build/dist/client/terminal/ui.d.ts +3 -2
- package/data/build/dist/client/terminal/ui.d.ts.map +1 -1
- package/data/build/dist/client/terminal/ui.js +216 -103
- package/data/build/dist/client/terminal/ui.js.map +1 -1
- package/data/build/dist/client/terminal/xterm-view.d.ts +8 -1
- package/data/build/dist/client/terminal/xterm-view.d.ts.map +1 -1
- package/data/build/dist/client/terminal/xterm-view.js +394 -34
- package/data/build/dist/client/terminal/xterm-view.js.map +1 -1
- package/data/build/dist/client/workspace-session-archive-dom.d.ts +3 -0
- package/data/build/dist/client/workspace-session-archive-dom.d.ts.map +1 -1
- package/data/build/dist/client/workspace-session-archive-dom.js +131 -45
- package/data/build/dist/client/workspace-session-archive-dom.js.map +1 -1
- package/data/build/dist/client/workspace-session-logo-dom.d.ts +3 -0
- package/data/build/dist/client/workspace-session-logo-dom.d.ts.map +1 -1
- package/data/build/dist/client/workspace-session-logo-dom.js +3 -1
- package/data/build/dist/client/workspace-session-logo-dom.js.map +1 -1
- package/data/build/dist/client/workspace-session-visibility-dom.d.ts +3 -0
- package/data/build/dist/client/workspace-session-visibility-dom.d.ts.map +1 -1
- package/data/build/dist/client/workspace-session-visibility-dom.js +46 -17
- package/data/build/dist/client/workspace-session-visibility-dom.js.map +1 -1
- package/data/build/dist/dsh-capabilities/client/primitives-adapter.d.ts +10 -0
- package/data/build/dist/dsh-capabilities/client/primitives-adapter.d.ts.map +1 -1
- package/data/build/dist/dsh-capabilities/client/primitives-adapter.js +20 -0
- package/data/build/dist/dsh-capabilities/client/primitives-adapter.js.map +1 -1
- package/data/build/dist/host/browser-text.d.ts +30 -0
- package/data/build/dist/host/browser-text.d.ts.map +1 -0
- package/data/build/dist/host/browser-text.js +105 -0
- package/data/build/dist/host/browser-text.js.map +1 -0
- package/data/build/dist/host/cli-adapters/acp-cli-driver.d.ts +58 -0
- package/data/build/dist/host/cli-adapters/acp-cli-driver.d.ts.map +1 -0
- package/data/build/dist/host/cli-adapters/acp-cli-driver.js +271 -0
- package/data/build/dist/host/cli-adapters/acp-cli-driver.js.map +1 -0
- package/data/build/dist/host/cli-adapters/antigravity-driver.d.ts +30 -0
- package/data/build/dist/host/cli-adapters/antigravity-driver.d.ts.map +1 -0
- package/data/build/dist/host/cli-adapters/antigravity-driver.js +210 -0
- package/data/build/dist/host/cli-adapters/antigravity-driver.js.map +1 -0
- package/data/build/dist/host/cli-adapters/claude-driver.d.ts +28 -2
- package/data/build/dist/host/cli-adapters/claude-driver.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/claude-driver.js +74 -4
- package/data/build/dist/host/cli-adapters/claude-driver.js.map +1 -1
- package/data/build/dist/host/cli-adapters/claude-model-options.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/claude-model-options.js +22 -9
- package/data/build/dist/host/cli-adapters/claude-model-options.js.map +1 -1
- package/data/build/dist/host/cli-adapters/codebuddy-driver.d.ts +139 -0
- package/data/build/dist/host/cli-adapters/codebuddy-driver.d.ts.map +1 -0
- package/data/build/dist/host/cli-adapters/codebuddy-driver.js +1721 -0
- package/data/build/dist/host/cli-adapters/codebuddy-driver.js.map +1 -0
- package/data/build/dist/host/cli-adapters/codebuddy-subscription.d.ts +53 -0
- package/data/build/dist/host/cli-adapters/codebuddy-subscription.d.ts.map +1 -0
- package/data/build/dist/host/cli-adapters/codebuddy-subscription.js +778 -0
- package/data/build/dist/host/cli-adapters/codebuddy-subscription.js.map +1 -0
- package/data/build/dist/host/cli-adapters/codex-driver.d.ts +22 -0
- package/data/build/dist/host/cli-adapters/codex-driver.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/codex-driver.js +254 -7
- package/data/build/dist/host/cli-adapters/codex-driver.js.map +1 -1
- package/data/build/dist/host/cli-adapters/command-code-driver.d.ts +43 -3
- package/data/build/dist/host/cli-adapters/command-code-driver.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/command-code-driver.js +681 -74
- package/data/build/dist/host/cli-adapters/command-code-driver.js.map +1 -1
- package/data/build/dist/host/cli-adapters/command-code-history.d.ts +147 -0
- package/data/build/dist/host/cli-adapters/command-code-history.d.ts.map +1 -0
- package/data/build/dist/host/cli-adapters/command-code-history.js +659 -0
- package/data/build/dist/host/cli-adapters/command-code-history.js.map +1 -0
- package/data/build/dist/host/cli-adapters/cursor-driver.d.ts +12 -0
- package/data/build/dist/host/cli-adapters/cursor-driver.d.ts.map +1 -0
- package/data/build/dist/host/cli-adapters/cursor-driver.js +20 -0
- package/data/build/dist/host/cli-adapters/cursor-driver.js.map +1 -0
- package/data/build/dist/host/cli-adapters/custom-upstream-classifier.d.ts +67 -0
- package/data/build/dist/host/cli-adapters/custom-upstream-classifier.d.ts.map +1 -0
- package/data/build/dist/host/cli-adapters/custom-upstream-classifier.js +169 -0
- package/data/build/dist/host/cli-adapters/custom-upstream-classifier.js.map +1 -0
- package/data/build/dist/host/cli-adapters/delegate-dispatch.d.ts +48 -0
- package/data/build/dist/host/cli-adapters/delegate-dispatch.d.ts.map +1 -0
- package/data/build/dist/host/cli-adapters/delegate-dispatch.js +111 -0
- package/data/build/dist/host/cli-adapters/delegate-dispatch.js.map +1 -0
- package/data/build/dist/host/cli-adapters/delegation-authorization.d.ts +19 -0
- package/data/build/dist/host/cli-adapters/delegation-authorization.d.ts.map +1 -0
- package/data/build/dist/host/cli-adapters/delegation-authorization.js +49 -0
- package/data/build/dist/host/cli-adapters/delegation-authorization.js.map +1 -0
- package/data/build/dist/host/cli-adapters/delegation-mention-rewrite.d.ts +34 -0
- package/data/build/dist/host/cli-adapters/delegation-mention-rewrite.d.ts.map +1 -0
- package/data/build/dist/host/cli-adapters/delegation-mention-rewrite.js +121 -0
- package/data/build/dist/host/cli-adapters/delegation-mention-rewrite.js.map +1 -0
- package/data/build/dist/host/cli-adapters/desktop-app-runtime.d.ts +32 -0
- package/data/build/dist/host/cli-adapters/desktop-app-runtime.d.ts.map +1 -0
- package/data/build/dist/host/cli-adapters/desktop-app-runtime.js +121 -0
- package/data/build/dist/host/cli-adapters/desktop-app-runtime.js.map +1 -0
- package/data/build/dist/host/cli-adapters/driver.d.ts +6 -0
- package/data/build/dist/host/cli-adapters/driver.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/dsh-message-projector.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/dsh-message-projector.js +5 -4
- package/data/build/dist/host/cli-adapters/dsh-message-projector.js.map +1 -1
- package/data/build/dist/host/cli-adapters/dsh-virtual-providers.d.ts +16 -0
- package/data/build/dist/host/cli-adapters/dsh-virtual-providers.d.ts.map +1 -0
- package/data/build/dist/host/cli-adapters/dsh-virtual-providers.js +73 -0
- package/data/build/dist/host/cli-adapters/dsh-virtual-providers.js.map +1 -0
- package/data/build/dist/host/cli-adapters/feature.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/feature.js +459 -26
- package/data/build/dist/host/cli-adapters/feature.js.map +1 -1
- package/data/build/dist/host/cli-adapters/gemini-driver.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/gemini-driver.js +21 -5
- package/data/build/dist/host/cli-adapters/gemini-driver.js.map +1 -1
- package/data/build/dist/host/cli-adapters/grok-driver.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/grok-driver.js +24 -7
- package/data/build/dist/host/cli-adapters/grok-driver.js.map +1 -1
- package/data/build/dist/host/cli-adapters/json-rpc-process.d.ts +22 -6
- package/data/build/dist/host/cli-adapters/json-rpc-process.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/json-rpc-process.js +118 -21
- package/data/build/dist/host/cli-adapters/json-rpc-process.js.map +1 -1
- package/data/build/dist/host/cli-adapters/kimi-driver.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/kimi-driver.js +25 -4
- package/data/build/dist/host/cli-adapters/kimi-driver.js.map +1 -1
- package/data/build/dist/host/cli-adapters/kiro-driver.d.ts +12 -0
- package/data/build/dist/host/cli-adapters/kiro-driver.d.ts.map +1 -0
- package/data/build/dist/host/cli-adapters/kiro-driver.js +18 -0
- package/data/build/dist/host/cli-adapters/kiro-driver.js.map +1 -0
- package/data/build/dist/host/cli-adapters/legacy-session-adapter.d.ts +11 -2
- package/data/build/dist/host/cli-adapters/legacy-session-adapter.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/legacy-session-adapter.js +26 -6
- package/data/build/dist/host/cli-adapters/legacy-session-adapter.js.map +1 -1
- package/data/build/dist/host/cli-adapters/mcode-catalog.d.ts +23 -0
- package/data/build/dist/host/cli-adapters/mcode-catalog.d.ts.map +1 -0
- package/data/build/dist/host/cli-adapters/mcode-catalog.js +120 -0
- package/data/build/dist/host/cli-adapters/mcode-catalog.js.map +1 -0
- package/data/build/dist/host/cli-adapters/mcode-driver.d.ts +56 -0
- package/data/build/dist/host/cli-adapters/mcode-driver.d.ts.map +1 -0
- package/data/build/dist/host/cli-adapters/mcode-driver.js +544 -0
- package/data/build/dist/host/cli-adapters/mcode-driver.js.map +1 -0
- package/data/build/dist/host/cli-adapters/model-catalog.d.ts +35 -5
- package/data/build/dist/host/cli-adapters/model-catalog.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/model-catalog.js +213 -8
- package/data/build/dist/host/cli-adapters/model-catalog.js.map +1 -1
- package/data/build/dist/host/cli-adapters/native-subagent-dispatch.d.ts +81 -0
- package/data/build/dist/host/cli-adapters/native-subagent-dispatch.d.ts.map +1 -0
- package/data/build/dist/host/cli-adapters/native-subagent-dispatch.js +299 -0
- package/data/build/dist/host/cli-adapters/native-subagent-dispatch.js.map +1 -0
- package/data/build/dist/host/cli-adapters/native-subagent-holder.d.ts +4 -0
- package/data/build/dist/host/cli-adapters/native-subagent-holder.d.ts.map +1 -0
- package/data/build/dist/host/cli-adapters/native-subagent-holder.js +4 -0
- package/data/build/dist/host/cli-adapters/native-subagent-holder.js.map +1 -0
- package/data/build/dist/host/cli-adapters/native-team-proxy.js +18 -15
- package/data/build/dist/host/cli-adapters/native-team-proxy.js.map +1 -1
- package/data/build/dist/host/cli-adapters/native-team-subagent.d.ts +57 -0
- package/data/build/dist/host/cli-adapters/native-team-subagent.d.ts.map +1 -0
- package/data/build/dist/host/cli-adapters/native-team-subagent.js +126 -0
- package/data/build/dist/host/cli-adapters/native-team-subagent.js.map +1 -0
- package/data/build/dist/host/cli-adapters/new-api-subscription.d.ts +75 -0
- package/data/build/dist/host/cli-adapters/new-api-subscription.d.ts.map +1 -0
- package/data/build/dist/host/cli-adapters/new-api-subscription.js +959 -0
- package/data/build/dist/host/cli-adapters/new-api-subscription.js.map +1 -0
- package/data/build/dist/host/cli-adapters/opencode-driver.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/opencode-driver.js +22 -10
- package/data/build/dist/host/cli-adapters/opencode-driver.js.map +1 -1
- package/data/build/dist/host/cli-adapters/pi-driver.js +24 -7
- package/data/build/dist/host/cli-adapters/pi-driver.js.map +1 -1
- package/data/build/dist/host/cli-adapters/provider-subscription.d.ts +41 -4
- package/data/build/dist/host/cli-adapters/provider-subscription.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/provider-subscription.js +254 -40
- package/data/build/dist/host/cli-adapters/provider-subscription.js.map +1 -1
- package/data/build/dist/host/cli-adapters/qoder-driver.d.ts +71 -0
- package/data/build/dist/host/cli-adapters/qoder-driver.d.ts.map +1 -0
- package/data/build/dist/host/cli-adapters/qoder-driver.js +553 -0
- package/data/build/dist/host/cli-adapters/qoder-driver.js.map +1 -0
- package/data/build/dist/host/cli-adapters/qoder-subscription.d.ts +31 -0
- package/data/build/dist/host/cli-adapters/qoder-subscription.d.ts.map +1 -0
- package/data/build/dist/host/cli-adapters/qoder-subscription.js +178 -0
- package/data/build/dist/host/cli-adapters/qoder-subscription.js.map +1 -0
- package/data/build/dist/host/cli-adapters/registry-holder.d.ts +4 -0
- package/data/build/dist/host/cli-adapters/registry-holder.d.ts.map +1 -0
- package/data/build/dist/host/cli-adapters/registry-holder.js +9 -0
- package/data/build/dist/host/cli-adapters/registry-holder.js.map +1 -0
- package/data/build/dist/host/cli-adapters/registry.d.ts +54 -2
- package/data/build/dist/host/cli-adapters/registry.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/registry.js +224 -18
- package/data/build/dist/host/cli-adapters/registry.js.map +1 -1
- package/data/build/dist/host/cli-adapters/session-store.d.ts +10 -1
- package/data/build/dist/host/cli-adapters/session-store.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/session-store.js +60 -6
- package/data/build/dist/host/cli-adapters/session-store.js.map +1 -1
- package/data/build/dist/host/cli-adapters/standard-stream-driver.d.ts +16 -1
- package/data/build/dist/host/cli-adapters/standard-stream-driver.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/standard-stream-driver.js +150 -26
- package/data/build/dist/host/cli-adapters/standard-stream-driver.js.map +1 -1
- package/data/build/dist/host/cli-adapters/stream-normalizer.d.ts +10 -1
- package/data/build/dist/host/cli-adapters/stream-normalizer.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/stream-normalizer.js +23 -0
- package/data/build/dist/host/cli-adapters/stream-normalizer.js.map +1 -1
- package/data/build/dist/host/cli-adapters/subagent-tool.d.ts +5 -0
- package/data/build/dist/host/cli-adapters/subagent-tool.d.ts.map +1 -0
- package/data/build/dist/host/cli-adapters/subagent-tool.js +112 -0
- package/data/build/dist/host/cli-adapters/subagent-tool.js.map +1 -0
- package/data/build/dist/host/cli-adapters/zcode-driver.d.ts +57 -0
- package/data/build/dist/host/cli-adapters/zcode-driver.d.ts.map +1 -0
- package/data/build/dist/host/cli-adapters/zcode-driver.js +843 -0
- package/data/build/dist/host/cli-adapters/zcode-driver.js.map +1 -0
- package/data/build/dist/host/cli-adapters/zcode-subscription.d.ts +28 -0
- package/data/build/dist/host/cli-adapters/zcode-subscription.d.ts.map +1 -0
- package/data/build/dist/host/cli-adapters/zcode-subscription.js +212 -0
- package/data/build/dist/host/cli-adapters/zcode-subscription.js.map +1 -0
- package/data/build/dist/host/cli-bridge/bridge-holder.d.ts +27 -0
- package/data/build/dist/host/cli-bridge/bridge-holder.d.ts.map +1 -0
- package/data/build/dist/host/cli-bridge/bridge-holder.js +51 -0
- package/data/build/dist/host/cli-bridge/bridge-holder.js.map +1 -0
- package/data/build/dist/host/cli-bridge/bridge-server.d.ts +39 -0
- package/data/build/dist/host/cli-bridge/bridge-server.d.ts.map +1 -0
- package/data/build/dist/host/cli-bridge/bridge-server.js +140 -0
- package/data/build/dist/host/cli-bridge/bridge-server.js.map +1 -0
- package/data/build/dist/host/cli-bridge/command-code-mod.d.ts +29 -0
- package/data/build/dist/host/cli-bridge/command-code-mod.d.ts.map +1 -0
- package/data/build/dist/host/cli-bridge/command-code-mod.js +91 -0
- package/data/build/dist/host/cli-bridge/command-code-mod.js.map +1 -0
- package/data/build/dist/host/cli-bridge/dispatch.d.ts +19 -0
- package/data/build/dist/host/cli-bridge/dispatch.d.ts.map +1 -0
- package/data/build/dist/host/cli-bridge/dispatch.js +128 -0
- package/data/build/dist/host/cli-bridge/dispatch.js.map +1 -0
- package/data/build/dist/host/cli-bridge/injections.d.ts +28 -0
- package/data/build/dist/host/cli-bridge/injections.d.ts.map +1 -0
- package/data/build/dist/host/cli-bridge/injections.js +131 -0
- package/data/build/dist/host/cli-bridge/injections.js.map +1 -0
- package/data/build/dist/host/cli-bridge/mcp-stdio-entry.d.ts +8 -0
- package/data/build/dist/host/cli-bridge/mcp-stdio-entry.d.ts.map +1 -0
- package/data/build/dist/host/cli-bridge/mcp-stdio-entry.js +170 -0
- package/data/build/dist/host/cli-bridge/mcp-stdio-entry.js.map +1 -0
- package/data/build/dist/host/features/lan-access-dsh.d.ts.map +1 -1
- package/data/build/dist/host/features/lan-access-dsh.js +6 -2
- package/data/build/dist/host/features/lan-access-dsh.js.map +1 -1
- package/data/build/dist/host/features/peer-host.d.ts +21 -1
- package/data/build/dist/host/features/peer-host.d.ts.map +1 -1
- package/data/build/dist/host/features/peer-host.js +176 -31
- package/data/build/dist/host/features/peer-host.js.map +1 -1
- package/data/build/dist/host/features/types.d.ts +2 -0
- package/data/build/dist/host/features/types.d.ts.map +1 -1
- package/data/build/dist/host/index.d.ts +15 -1
- package/data/build/dist/host/index.d.ts.map +1 -1
- package/data/build/dist/host/index.js +48 -2
- package/data/build/dist/host/index.js.map +1 -1
- package/data/build/dist/host/lan-access-dsh.d.ts +7 -1
- package/data/build/dist/host/lan-access-dsh.d.ts.map +1 -1
- package/data/build/dist/host/lan-access-dsh.js +95 -19
- package/data/build/dist/host/lan-access-dsh.js.map +1 -1
- package/data/build/dist/host/login-attempt-guard.d.ts +55 -0
- package/data/build/dist/host/login-attempt-guard.d.ts.map +1 -0
- package/data/build/dist/host/login-attempt-guard.js +201 -0
- package/data/build/dist/host/login-attempt-guard.js.map +1 -0
- package/data/build/dist/host/modules/peer-host/host-api-proxy-service.d.ts +3 -0
- package/data/build/dist/host/modules/peer-host/host-api-proxy-service.d.ts.map +1 -1
- package/data/build/dist/host/modules/peer-host/host-api-proxy-service.js +2 -0
- package/data/build/dist/host/modules/peer-host/host-api-proxy-service.js.map +1 -1
- package/data/build/dist/host/modules/peer-host/peer-host-aggregate-service.d.ts +1 -0
- package/data/build/dist/host/modules/peer-host/peer-host-aggregate-service.d.ts.map +1 -1
- package/data/build/dist/host/modules/peer-host/peer-host-aggregate-service.js +1 -0
- package/data/build/dist/host/modules/peer-host/peer-host-aggregate-service.js.map +1 -1
- package/data/build/dist/host/modules/peer-host/peer-host-native-protocol.d.ts +1 -1
- package/data/build/dist/host/modules/peer-host/peer-host-native-protocol.d.ts.map +1 -1
- package/data/build/dist/host/modules/peer-host/peer-host-native-protocol.js +9 -1
- package/data/build/dist/host/modules/peer-host/peer-host-native-protocol.js.map +1 -1
- package/data/build/dist/host/modules/peer-host/peer-host-native-transport.d.ts +8 -0
- package/data/build/dist/host/modules/peer-host/peer-host-native-transport.d.ts.map +1 -1
- package/data/build/dist/host/modules/peer-host/peer-host-native-transport.js +9 -0
- package/data/build/dist/host/modules/peer-host/peer-host-native-transport.js.map +1 -1
- package/data/build/dist/host/modules/peer-host/peer-host-remote-summary-source.d.ts +7 -0
- package/data/build/dist/host/modules/peer-host/peer-host-remote-summary-source.d.ts.map +1 -1
- package/data/build/dist/host/modules/peer-host/peer-host-remote-summary-source.js +31 -3
- package/data/build/dist/host/modules/peer-host/peer-host-remote-summary-source.js.map +1 -1
- package/data/build/dist/host/modules/peer-host/peer-host-virtual-registry.d.ts +2 -0
- package/data/build/dist/host/modules/peer-host/peer-host-virtual-registry.d.ts.map +1 -1
- package/data/build/dist/host/modules/peer-host/peer-host-virtual-registry.js +26 -7
- package/data/build/dist/host/modules/peer-host/peer-host-virtual-registry.js.map +1 -1
- package/data/build/dist/host/modules/pwa/pwa-assets.d.ts +6 -4
- package/data/build/dist/host/modules/pwa/pwa-assets.d.ts.map +1 -1
- package/data/build/dist/host/modules/pwa/pwa-assets.js +11 -9
- package/data/build/dist/host/modules/pwa/pwa-assets.js.map +1 -1
- package/data/build/dist/host/modules/pwa/pwa-session-notifications.d.ts +3 -0
- package/data/build/dist/host/modules/pwa/pwa-session-notifications.d.ts.map +1 -1
- package/data/build/dist/host/modules/pwa/pwa-session-notifications.js +6 -4
- package/data/build/dist/host/modules/pwa/pwa-session-notifications.js.map +1 -1
- package/data/build/dist/host/native-session-bridge.d.ts.map +1 -1
- package/data/build/dist/host/native-session-bridge.js +106 -59
- package/data/build/dist/host/native-session-bridge.js.map +1 -1
- package/data/build/dist/host/rpc.js +8 -2
- package/data/build/dist/host/rpc.js.map +1 -1
- package/data/build/dist/host/settings.d.ts.map +1 -1
- package/data/build/dist/host/settings.js +14 -4
- package/data/build/dist/host/settings.js.map +1 -1
- package/data/build/dist/host/terminal/backends/local-pty-backend.d.ts.map +1 -1
- package/data/build/dist/host/terminal/backends/local-pty-backend.js +14 -0
- package/data/build/dist/host/terminal/backends/local-pty-backend.js.map +1 -1
- package/data/build/dist/host/terminal/backends/tmux-backend.d.ts +40 -1
- package/data/build/dist/host/terminal/backends/tmux-backend.d.ts.map +1 -1
- package/data/build/dist/host/terminal/backends/tmux-backend.js +291 -18
- package/data/build/dist/host/terminal/backends/tmux-backend.js.map +1 -1
- package/data/build/dist/host/terminal/runtime-adapter.d.ts +32 -0
- package/data/build/dist/host/terminal/runtime-adapter.d.ts.map +1 -1
- package/data/build/dist/host/terminal/runtime-adapter.js +8 -0
- package/data/build/dist/host/terminal/runtime-adapter.js.map +1 -1
- package/data/build/dist/host/terminal/runtime-manager.d.ts +3 -0
- package/data/build/dist/host/terminal/runtime-manager.d.ts.map +1 -1
- package/data/build/dist/host/terminal/runtime-manager.js +92 -4
- package/data/build/dist/host/terminal/runtime-manager.js.map +1 -1
- package/data/build/dist/host/terminal/terminal-controller.d.ts.map +1 -1
- package/data/build/dist/host/terminal/terminal-controller.js +58 -6
- package/data/build/dist/host/terminal/terminal-controller.js.map +1 -1
- package/data/build/dist/host/terminal/terminal-process-service.d.ts.map +1 -1
- package/data/build/dist/host/terminal/terminal-process-service.js +12 -2
- package/data/build/dist/host/terminal/terminal-process-service.js.map +1 -1
- package/data/build/dist/host/terminal/terminal-service.d.ts +41 -4
- package/data/build/dist/host/terminal/terminal-service.d.ts.map +1 -1
- package/data/build/dist/host/terminal/terminal-service.js +551 -61
- package/data/build/dist/host/terminal/terminal-service.js.map +1 -1
- package/data/build/dist/index.d.ts +2 -2
- package/data/build/dist/index.d.ts.map +1 -1
- package/data/build/dist/index.js +2 -2
- package/data/build/dist/index.js.map +1 -1
- package/data/build/dist/shared/contracts/cli-adapter.d.ts +69 -2
- package/data/build/dist/shared/contracts/cli-adapter.d.ts.map +1 -1
- package/data/build/dist/shared/contracts/config.d.ts +63 -10
- package/data/build/dist/shared/contracts/config.d.ts.map +1 -1
- package/data/build/dist/shared/contracts/config.js +57 -5
- package/data/build/dist/shared/contracts/config.js.map +1 -1
- package/data/build/dist/shared/contracts/peer-host.d.ts +2 -0
- package/data/build/dist/shared/contracts/peer-host.d.ts.map +1 -1
- package/data/build/dist/shared/contracts/peer-host.js.map +1 -1
- package/data/build/dist/shared/contracts/subscription.d.ts +20 -1
- package/data/build/dist/shared/contracts/subscription.d.ts.map +1 -1
- package/data/build/dist/shared/contracts/subscription.js.map +1 -1
- package/data/build/dist/shared/contracts/terminal.d.ts +8 -1
- package/data/build/dist/shared/contracts/terminal.d.ts.map +1 -1
- package/data/build/dist/shared/contracts/version.d.ts +1 -1
- package/data/build/dist/shared/contracts/version.d.ts.map +1 -1
- package/data/build/dist/shared/contracts/version.js +1 -1
- package/data/build/dist/shared/contracts/version.js.map +1 -1
- package/data/build/dist/shared/debug.d.ts +8 -0
- package/data/build/dist/shared/debug.d.ts.map +1 -1
- package/data/build/dist/shared/debug.js +43 -7
- package/data/build/dist/shared/debug.js.map +1 -1
- package/data/build/dist/shared/delegation-carrier.d.ts +24 -0
- package/data/build/dist/shared/delegation-carrier.d.ts.map +1 -0
- package/data/build/dist/shared/delegation-carrier.js +111 -0
- package/data/build/dist/shared/delegation-carrier.js.map +1 -0
- package/data/build/dist/shared/index.d.ts +3 -3
- package/data/build/dist/shared/index.d.ts.map +1 -1
- package/data/build/dist/shared/index.js +2 -2
- package/data/build/dist/shared/index.js.map +1 -1
- package/data/build/dist/shared/terminal-input.d.ts +8 -0
- package/data/build/dist/shared/terminal-input.d.ts.map +1 -0
- package/data/build/dist/shared/terminal-input.js +10 -0
- package/data/build/dist/shared/terminal-input.js.map +1 -0
- package/data/build/dist/transport/debug.d.ts +7 -0
- package/data/build/dist/transport/debug.d.ts.map +1 -1
- package/data/build/dist/transport/debug.js +33 -5
- package/data/build/dist/transport/debug.js.map +1 -1
- package/data/build/dist/transport/index.d.ts +1 -1
- package/data/build/dist/transport/index.d.ts.map +1 -1
- package/data/build/dist/transport/index.js +1 -1
- package/data/build/dist/transport/index.js.map +1 -1
- package/data/build/dist/typert.host.js +1 -1
- package/data/build/dist/typert.host.js.map +1 -1
- package/package.json +3 -1
- package/profile/README.md +2 -2
- package/profile/package.json +2 -2
- package/profile/version.json +1 -1
- package/version.json +1 -1
|
@@ -0,0 +1,1721 @@
|
|
|
1
|
+
import { readFileSync, readdirSync, realpathSync, statSync } from 'node:fs';
|
|
2
|
+
import { spawn, spawnSync } from 'node:child_process';
|
|
3
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
4
|
+
import { homedir, tmpdir } from 'node:os';
|
|
5
|
+
import { createConnection } from 'node:net';
|
|
6
|
+
import { basename, dirname, isAbsolute, join } from 'node:path';
|
|
7
|
+
import { JsonRpcProcess, JsonRpcRequestError } from './json-rpc-process.js';
|
|
8
|
+
import { emptyCatalog, isRecord, streamRpcRequest, textValue, usageChunk } from './rpc-driver-utils.js';
|
|
9
|
+
import { probeStoredSession, readFirstJsonRecord } from './session-probe.js';
|
|
10
|
+
import { firstToolText, isToolRecord, normalizeToolStatus, serializeToolValue } from './tool-observation.js';
|
|
11
|
+
import { buildAcpPromptBlocks } from './attachment-utils.js';
|
|
12
|
+
import { acpBridgeMcpServers } from '../cli-bridge/injections.js';
|
|
13
|
+
import { advanceCodingNsSegment, createCodingNsSegmentState, decorateCodingNsSegmentEvent } from './stream-normalizer.js';
|
|
14
|
+
import { commandEnvironment, resolveCommandPath, WINDOWS } from './process-utils.js';
|
|
15
|
+
import { isProviderDefaultModel } from './model-catalog.js';
|
|
16
|
+
/** WorkBuddy 将 Auto 拆成三个模型 ID,但它们实际是 Auto 的思考档位。 */
|
|
17
|
+
const WORKBUDDY_AUTO_TIER_MODELS = [
|
|
18
|
+
{ id: 'fast-model', name: '快速' },
|
|
19
|
+
{ id: 'balanced-model', name: '均衡' },
|
|
20
|
+
{ id: 'deep-model', name: '极致' },
|
|
21
|
+
];
|
|
22
|
+
const WORKBUDDY_AUTO_TIER_IDS = new Set(WORKBUDDY_AUTO_TIER_MODELS.map((model) => model.id));
|
|
23
|
+
/** `auto` 是 WorkBuddy 的默认路由占位符,不应作为独立模型渲染。 */
|
|
24
|
+
const WORKBUDDY_AUTO_MODEL_ID = 'auto';
|
|
25
|
+
const CODEBUDDY_HOME = join(homedir(), '.codebuddy');
|
|
26
|
+
const CODEBUDDY_INTERNATIONAL_AUTH_HOME = join(homedir(), '.codebuddy-international-home');
|
|
27
|
+
// 官方 FileAuthenticationStorage 不读取 CODEBUDDY_CONFIG_DIR,而是按 HOME
|
|
28
|
+
// 计算 ~/Library/Application Support/CodeBuddyExtension(或平台等价目录)。
|
|
29
|
+
// CN 在全局认证不是 CN 账号时使用独立 HOME,避免登录时覆盖国际版的认证文件。
|
|
30
|
+
const CODEBUDDY_CN_AUTH_HOME = join(homedir(), '.codebuddy-cn-home');
|
|
31
|
+
const INTERNAL_AUTH_DOMAINS = new Set([
|
|
32
|
+
'copilot.tencent.com', 'staging-copilot.tencent.com', 'www.codebuddy.cn', 'staging.codebuddy.cn',
|
|
33
|
+
'www.workbuddy.cn', 'staging.workbuddy.cn',
|
|
34
|
+
]);
|
|
35
|
+
const EXTERNAL_AUTH_DOMAINS = new Set(['www.codebuddy.ai', 'staging-codebuddy.tencent.com']);
|
|
36
|
+
// WorkBuddy 产品配置中的 dataFolderName 为 `.workbuddy`;必须复用桌面应用
|
|
37
|
+
// 的认证存储,不能另造 `.workbuddy-ai` 隔离目录,否则 ACP 会报 Authentication required。
|
|
38
|
+
const WORKBUDDY_HOME = join(homedir(), '.workbuddy');
|
|
39
|
+
/** CodeBuddy 独立 CLI 的产品身份。 */
|
|
40
|
+
export const CODEBUDDY_PROFILE = {
|
|
41
|
+
adapterId: 'codebuddy',
|
|
42
|
+
displayName: 'CodeBuddy',
|
|
43
|
+
// CN 与国际版是同一个 CLI;兼容旧版 CN profile 的专用配置变量,统一归入
|
|
44
|
+
// 当前适配器的配置根,不再注册第二个 Agent。
|
|
45
|
+
configRootEnvVars: ['CODEBUDDY_CONFIG_DIR', 'CODEBUDDY_CN_CONFIG_DIR'],
|
|
46
|
+
defaultConfigRoot: CODEBUDDY_HOME,
|
|
47
|
+
historyDirRule: 'lowercase-slug',
|
|
48
|
+
platforms: ['darwin', 'win32', 'linux'],
|
|
49
|
+
commandEnvOverride: 'CODEBUDDY_CLI_PATH',
|
|
50
|
+
// 官方 npm 包同时提供 codebuddy、codebuddy-code 和 cbc 三个入口。
|
|
51
|
+
binaries: ['codebuddy', 'codebuddy-code', 'cbc', 'codebuddy.cmd', 'codebuddy-code.cmd', 'cbc.cmd'],
|
|
52
|
+
bundledInApp: false,
|
|
53
|
+
// 没有匹配的全局国际账号时,使用独立认证根,避免读到 CN 会话。
|
|
54
|
+
isolatedHome: CODEBUDDY_INTERNATIONAL_AUTH_HOME,
|
|
55
|
+
// 区域变量在 runtimeEnvironment 中根据认证域名动态设置。
|
|
56
|
+
};
|
|
57
|
+
/** 旧版 CN profile 的兼容导出;运行时不再把它注册为独立 Agent。 */
|
|
58
|
+
export const CODEBUDDY_CN_PROFILE = CODEBUDDY_PROFILE;
|
|
59
|
+
/** WorkBuddy 桌面应用内置 CLI 的产品身份。 */
|
|
60
|
+
export const WORKBUDDY_PROFILE = {
|
|
61
|
+
adapterId: 'workbuddy',
|
|
62
|
+
displayName: 'WorkBuddy',
|
|
63
|
+
configRootEnvVars: ['WORKBUDDY_CONFIG_DIR', 'CODEBUDDY_CONFIG_DIR'],
|
|
64
|
+
defaultConfigRoot: WORKBUDDY_HOME,
|
|
65
|
+
historyDirRule: 'preserve-case',
|
|
66
|
+
platforms: ['darwin', 'win32'],
|
|
67
|
+
commandEnvOverride: 'WORKBUDDY_CLI_PATH',
|
|
68
|
+
// WorkBuddy 的入口文件仍叫 codebuddy,但只能使用桌面应用内置副本。
|
|
69
|
+
binaries: ['codebuddy'],
|
|
70
|
+
bundledInApp: true,
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* ACP 驱动的已验收公共能力。
|
|
74
|
+
*
|
|
75
|
+
* CodeBuddy 的 ACP 上下文事件与本地 JSONL 用量补偿已经完成验收;账户套餐
|
|
76
|
+
* 仍由独立订阅读取器负责,不混入 ACP 能力声明。
|
|
77
|
+
*/
|
|
78
|
+
const CONSERVATIVE_CAPABILITIES = [
|
|
79
|
+
'models', 'stream', 'resume', 'interrupt', 'tool-events', 'reasoning', 'usage',
|
|
80
|
+
];
|
|
81
|
+
/** 无副作用模型探测的最低回退项;登记层可以传入更完整的真实目录覆盖它。 */
|
|
82
|
+
export const CODEBUDDY_DEFAULT_CATALOG = {
|
|
83
|
+
groups: [{ id: 'codebuddy', name: 'CodeBuddy', models: [{ id: 'provider-default', name: '跟随 CodeBuddy 默认模型', efforts: [] }] }],
|
|
84
|
+
currentModel: null,
|
|
85
|
+
currentEffort: null,
|
|
86
|
+
// 只有默认入口时表示产品目录尚未读取成功,Registry 必须按短周期重试。
|
|
87
|
+
fallback: true,
|
|
88
|
+
};
|
|
89
|
+
export const CODEBUDDY_CN_DEFAULT_CATALOG = CODEBUDDY_DEFAULT_CATALOG;
|
|
90
|
+
export const WORKBUDDY_DEFAULT_CATALOG = {
|
|
91
|
+
groups: [{ id: 'workbuddy', name: 'WorkBuddy', models: [{ id: 'provider-default', name: '跟随 WorkBuddy 默认模型', efforts: [] }] }],
|
|
92
|
+
currentModel: null,
|
|
93
|
+
currentEffort: null,
|
|
94
|
+
// 只有默认入口时表示产品目录尚未读取成功,Registry 必须按短周期重试。
|
|
95
|
+
fallback: true,
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* CodeBuddy 与 WorkBuddy 共用的 ACP stdio 驱动。
|
|
99
|
+
*
|
|
100
|
+
* 每轮启动一个短生命周期 ACP 进程,继续会话时通过 `session/load` 恢复。
|
|
101
|
+
* 这样取消不会把残留取消态带到下一轮,且不需要复制厂商 SDK 或桌面应用登录态。
|
|
102
|
+
*/
|
|
103
|
+
export class CodeBuddyCliDriver {
|
|
104
|
+
descriptor;
|
|
105
|
+
/** CodeBuddy/WorkBuddy 的 ACP 流可由 Registry 在工具完成后暂停并续读。 */
|
|
106
|
+
supportsToolStepSplitting = true;
|
|
107
|
+
profile;
|
|
108
|
+
runSpawnSync;
|
|
109
|
+
runSpawn;
|
|
110
|
+
environment;
|
|
111
|
+
configRoot;
|
|
112
|
+
sessionRoots;
|
|
113
|
+
fallbackCatalog;
|
|
114
|
+
platform;
|
|
115
|
+
binaries;
|
|
116
|
+
binariesOverride;
|
|
117
|
+
modelCatalogPaths;
|
|
118
|
+
readModelCatalogFile;
|
|
119
|
+
commandPath;
|
|
120
|
+
useSidecar;
|
|
121
|
+
sidecarSocketPath;
|
|
122
|
+
workbuddyElectronPath;
|
|
123
|
+
cachedCommand = null;
|
|
124
|
+
lastVersion = null;
|
|
125
|
+
detectionDiagnostic;
|
|
126
|
+
processes = new Map();
|
|
127
|
+
sidecarProcesses = new Map();
|
|
128
|
+
constructor(options = {}) {
|
|
129
|
+
this.profile = options.profile ?? CODEBUDDY_PROFILE;
|
|
130
|
+
this.runSpawnSync = options.spawnSync ?? spawnSync;
|
|
131
|
+
this.runSpawn = options.spawn ?? spawn;
|
|
132
|
+
this.platform = options.platform ?? process.platform;
|
|
133
|
+
this.binariesOverride = options.binaries !== undefined;
|
|
134
|
+
this.binaries = options.binaries ?? this.profile.binaries;
|
|
135
|
+
this.commandPath = options.commandPath?.trim() || undefined;
|
|
136
|
+
this.environment = { ...process.env, ...(options.environment ?? options.env ?? {}) };
|
|
137
|
+
this.configRoot = resolveConfigRoot(this.profile, options.configRoot, this.environment);
|
|
138
|
+
this.modelCatalogPaths = options.modelCatalogPaths;
|
|
139
|
+
this.readModelCatalogFile = options.readFileSync ?? readFileSync;
|
|
140
|
+
this.sessionRoots = options.sessionRoots ?? [join(this.configRoot, 'projects'), join(this.configRoot, 'sessions')];
|
|
141
|
+
this.fallbackCatalog = options.fallbackCatalog ?? defaultCatalogForProfile(this.profile);
|
|
142
|
+
this.useSidecar = options.useSidecar ?? (this.profile.adapterId === WORKBUDDY_PROFILE.adapterId && options.spawn === undefined);
|
|
143
|
+
this.sidecarSocketPath = options.sidecarSocketPath?.trim() || undefined;
|
|
144
|
+
this.workbuddyElectronPath = options.workbuddyElectronPath?.trim() || undefined;
|
|
145
|
+
this.descriptor = {
|
|
146
|
+
id: this.profile.adapterId,
|
|
147
|
+
name: this.profile.displayName,
|
|
148
|
+
protocol: 'acp',
|
|
149
|
+
capabilities: CONSERVATIVE_CAPABILITIES,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
/** 当前使用的产品身份配置根,供测试和诊断使用;不包含凭据。 */
|
|
153
|
+
get configDirectory() { return this.configRoot; }
|
|
154
|
+
/** 最近一次探测失败的脱敏诊断。 */
|
|
155
|
+
get discoveryError() { return this.detectionDiagnostic; }
|
|
156
|
+
getDiscoveryDiagnostic() { return this.detectionDiagnostic; }
|
|
157
|
+
async detect() {
|
|
158
|
+
this.detectionDiagnostic = undefined;
|
|
159
|
+
if (!this.profile.platforms.includes(this.platform)) {
|
|
160
|
+
this.detectionDiagnostic = `${this.profile.displayName} 官方未提供 ${this.platform} 平台版本`;
|
|
161
|
+
return { installed: false, version: null, command: null };
|
|
162
|
+
}
|
|
163
|
+
const explicit = this.explicitCommand();
|
|
164
|
+
if (explicit !== undefined) {
|
|
165
|
+
const result = this.detectCommand(explicit);
|
|
166
|
+
if (result !== null)
|
|
167
|
+
return result;
|
|
168
|
+
if (this.detectionDiagnostic === undefined)
|
|
169
|
+
this.detectionDiagnostic = `${this.profile.displayName} 显式指定的 CLI 路径无效`;
|
|
170
|
+
return { installed: false, version: null, command: null };
|
|
171
|
+
}
|
|
172
|
+
let firstDiagnostic;
|
|
173
|
+
for (const command of this.candidateCommands()) {
|
|
174
|
+
const result = this.detectCommand(command);
|
|
175
|
+
if (result !== null)
|
|
176
|
+
return result;
|
|
177
|
+
if (firstDiagnostic === undefined && this.detectionDiagnostic !== undefined)
|
|
178
|
+
firstDiagnostic = this.detectionDiagnostic;
|
|
179
|
+
}
|
|
180
|
+
this.detectionDiagnostic = firstDiagnostic ?? `${this.profile.displayName} CLI 未安装`;
|
|
181
|
+
return { installed: false, version: null, command: null };
|
|
182
|
+
}
|
|
183
|
+
/** 从产品快照读取模型目录;ACP 没有标准只读目录接口,不能创建会话探测模型。 */
|
|
184
|
+
async listModels() {
|
|
185
|
+
const detection = await this.detect();
|
|
186
|
+
if (!detection.installed)
|
|
187
|
+
return emptyCatalog();
|
|
188
|
+
// PATH 中的 npm shim 可能只是相对命令名;模型目录位于真实 CLI 包根目录,
|
|
189
|
+
// 因此这里补一次只读路径解析,避免把工作目录误当成产品目录。
|
|
190
|
+
const command = detection.command !== null && !isAbsolute(detection.command)
|
|
191
|
+
? resolveCommandPath(detection.command, this.runSpawnSync) ?? detection.command
|
|
192
|
+
: detection.command;
|
|
193
|
+
const modelCatalogPaths = this.modelCatalogPaths ?? defaultModelCatalogPaths(this.profile, this.configRoot, this.environment);
|
|
194
|
+
return readProductCatalog(modelCatalogPaths, this.readModelCatalogFile, command, this.profile, detectCodeBuddyRegion(this.environment)) ?? this.fallbackCatalog;
|
|
195
|
+
}
|
|
196
|
+
/** 只读扫描产品自己的历史根;不会调用 ACP load/prompt。 */
|
|
197
|
+
async probeSession(input) {
|
|
198
|
+
return probeStoredSession(input, {
|
|
199
|
+
roots: this.sessionRoots,
|
|
200
|
+
matches: (path, entry, id) => entry.isFile() && (path.endsWith(`${id}.jsonl`) || basename(path).includes(id)),
|
|
201
|
+
validate: async (path, id) => {
|
|
202
|
+
const record = await readFirstJsonRecord(path);
|
|
203
|
+
const value = record?.sessionId ?? record?.session_id ?? record?.id;
|
|
204
|
+
return value === id;
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
async *executeTurn(input) {
|
|
209
|
+
const command = this.cachedCommand ?? (await this.detect()).command;
|
|
210
|
+
if (command === null)
|
|
211
|
+
throw new Error(`${this.profile.displayName} CLI 未安装`);
|
|
212
|
+
if (this.useSidecar && this.profile.adapterId === WORKBUDDY_PROFILE.adapterId) {
|
|
213
|
+
yield* this.executeWorkBuddySidecarTurn(input, command);
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
const rpc = new JsonRpcProcess({
|
|
217
|
+
command,
|
|
218
|
+
args: ['--acp'],
|
|
219
|
+
cwd: input.cwd,
|
|
220
|
+
env: this.runtimeEnvironment(command),
|
|
221
|
+
spawn: this.runSpawn,
|
|
222
|
+
});
|
|
223
|
+
const processState = { rpc, providerSessionId: '' };
|
|
224
|
+
this.processes.set(input.sessionId, processState);
|
|
225
|
+
// 未声明 permission/questions 时,私有交互必须快速拒绝,不能让一轮永久等待。
|
|
226
|
+
rpc.setServerRequestHandler(() => ({ outcome: { outcome: 'cancelled' } }));
|
|
227
|
+
try {
|
|
228
|
+
await rpc.request('initialize', {
|
|
229
|
+
protocolVersion: 1,
|
|
230
|
+
clientInfo: { name: 'codingns4dsh', version: '0.1.1' },
|
|
231
|
+
clientCapabilities: {},
|
|
232
|
+
}, sidecarSignalOptions(input.signal));
|
|
233
|
+
rpc.notify('initialized', {});
|
|
234
|
+
const session = input.providerSessionId
|
|
235
|
+
? await rpc.request('session/load', {
|
|
236
|
+
sessionId: input.providerSessionId,
|
|
237
|
+
cwd: input.cwd ?? process.cwd(),
|
|
238
|
+
mcpServers: acpBridgeMcpServers(input.sessionId, this.profile.adapterId),
|
|
239
|
+
}, sidecarSignalOptions(input.signal))
|
|
240
|
+
: await rpc.request('session/new', {
|
|
241
|
+
cwd: input.cwd ?? process.cwd(),
|
|
242
|
+
mcpServers: acpBridgeMcpServers(input.sessionId, this.profile.adapterId),
|
|
243
|
+
}, sidecarSignalOptions(input.signal));
|
|
244
|
+
const providerSessionId = readSessionId(session) ?? input.providerSessionId ?? input.sessionId;
|
|
245
|
+
processState.providerSessionId = providerSessionId;
|
|
246
|
+
yield { type: 'session-binding', providerSessionId };
|
|
247
|
+
// ACP 版本间的模型设置方法并不完全一致;失败时沿用 Provider 默认模型,不能阻断本轮。
|
|
248
|
+
const modelId = resolveCodeBuddyModelId(this.profile, input.modelId, input.effortId);
|
|
249
|
+
if (!isProviderDefaultModel(modelId)) {
|
|
250
|
+
await rpc.request('session/set_model', { sessionId: providerSessionId, modelId }, { signal: input.signal, killOnAbort: false }).catch(() => undefined);
|
|
251
|
+
}
|
|
252
|
+
const stream = streamRpcRequest(rpc, 'session/prompt', {
|
|
253
|
+
sessionId: providerSessionId,
|
|
254
|
+
prompt: await buildAcpPromptBlocks(input.prompt, input.attachments ?? []),
|
|
255
|
+
}, input.signal, { dispose: false, killOnAbort: false });
|
|
256
|
+
let meaningful = false;
|
|
257
|
+
let richUsageSeen = false;
|
|
258
|
+
let reportedContextWindow;
|
|
259
|
+
let terminal;
|
|
260
|
+
const segmentState = createCodingNsSegmentState();
|
|
261
|
+
let promptResponse;
|
|
262
|
+
while (true) {
|
|
263
|
+
const item = await stream.next();
|
|
264
|
+
if (item.done) {
|
|
265
|
+
promptResponse = item.value;
|
|
266
|
+
break;
|
|
267
|
+
}
|
|
268
|
+
const rawChunk = codeBuddyMessageToChunk(item.value);
|
|
269
|
+
const chunk = rawChunk === null ? null : decorateCodingNsSegmentEvent(rawChunk, input, segmentState, this.descriptor.id);
|
|
270
|
+
if (chunk?.type === 'finish') {
|
|
271
|
+
// ACP 可能先发一个 session/update 终态,再在 prompt 响应里携带真正的
|
|
272
|
+
// stopReason。先暂存终态,等正文和 prompt 响应都到齐后再决定成功或失败。
|
|
273
|
+
terminal = chunk;
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
if (chunk !== null) {
|
|
277
|
+
meaningful = true;
|
|
278
|
+
if (chunk.type === 'usage') {
|
|
279
|
+
if (chunk.contextWindow !== undefined)
|
|
280
|
+
reportedContextWindow = chunk.contextWindow;
|
|
281
|
+
if (hasTokenUsage(chunk))
|
|
282
|
+
richUsageSeen = true;
|
|
283
|
+
}
|
|
284
|
+
yield chunk;
|
|
285
|
+
advanceCodingNsSegment(chunk, segmentState);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
// CodeBuddy ACP 当前只发送正文与 stopReason;完整 Token/缓存数据会在
|
|
289
|
+
// prompt 完成后写入本地 JSONL。只在 ACP 没有提供完整 usage 时补读,避免
|
|
290
|
+
// 同一轮产生两套互相冲突的 token 口径。
|
|
291
|
+
if (!richUsageSeen) {
|
|
292
|
+
const historyUsage = await this.readHistoryUsage(input, providerSessionId);
|
|
293
|
+
if (historyUsage !== null)
|
|
294
|
+
yield enrichCodeBuddyUsageContext(historyUsage, reportedContextWindow);
|
|
295
|
+
}
|
|
296
|
+
const responseHasStopReason = isRecord(promptResponse) && typeof promptResponse.stopReason === 'string';
|
|
297
|
+
if (responseHasStopReason) {
|
|
298
|
+
// prompt 响应是 ACP 对本轮的最终判定,必须覆盖通知中可能提前发送的
|
|
299
|
+
// `finish(stop)`;否则 refusal 会被投影成正常结束,留下空 assistant。
|
|
300
|
+
yield promptFinish(promptResponse, input.signal);
|
|
301
|
+
}
|
|
302
|
+
else if (terminal !== undefined) {
|
|
303
|
+
if (meaningful) {
|
|
304
|
+
yield terminal;
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
yield emptyResponseFinish(terminal.failure ?? promptFailure(promptResponse));
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
yield emptyResponseFinish(promptFailure(promptResponse));
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
finally {
|
|
315
|
+
if (this.processes.get(input.sessionId) === processState)
|
|
316
|
+
this.processes.delete(input.sessionId);
|
|
317
|
+
await rpc.disposeAndWait();
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
/** WorkBuddy 桌面版必须由 sidecar 启动,普通 Node ACP 不会收到字段加密密钥。 */
|
|
321
|
+
async *executeWorkBuddySidecarTurn(input, command) {
|
|
322
|
+
const socketPath = this.sidecarSocketPath ?? discoverWorkBuddySidecarSocket(this.configRoot, this.platform, this.environment);
|
|
323
|
+
if (socketPath === null) {
|
|
324
|
+
throw new Error('WorkBuddy Hosted CLI sidecar 不可用(未找到 sidecar.pid 或 sidecar-<uuid>.sock)。当前版本按需创建 Hosted CLI runtime,普通应用启动或登录不会生成该通道;DSH 无法代替桌面 daemon 注入认证凭据。请先在 WorkBuddy 中启动一次 Hosted CLI 会话,或设置 WORKBUDDY_SIDECAR_SOCKET');
|
|
325
|
+
}
|
|
326
|
+
const sidecar = new WorkBuddySidecarClient(socketPath);
|
|
327
|
+
let sidecarSessionId = '';
|
|
328
|
+
let ownsSidecarSession = false;
|
|
329
|
+
const launch = workBuddyLaunchSpec(command, this.workbuddyElectronPath, this.platform, this.environment);
|
|
330
|
+
let client;
|
|
331
|
+
let providerSessionId = '';
|
|
332
|
+
let disposed = false;
|
|
333
|
+
const cleanup = async () => {
|
|
334
|
+
if (disposed)
|
|
335
|
+
return;
|
|
336
|
+
disposed = true;
|
|
337
|
+
this.sidecarProcesses.delete(input.sessionId);
|
|
338
|
+
await client?.dispose().catch(() => undefined);
|
|
339
|
+
if (ownsSidecarSession && sidecarSessionId !== '') {
|
|
340
|
+
await sidecar.request('session.kill', { sessionId: sidecarSessionId }, { timeoutMs: 5_000 }).catch(() => undefined);
|
|
341
|
+
}
|
|
342
|
+
sidecar.dispose();
|
|
343
|
+
};
|
|
344
|
+
try {
|
|
345
|
+
const endpoint = await resolveWorkBuddyAcpEndpoint(sidecar, input, launch, this.configRoot, this.runtimeEnvironment(command), this.platform, this.environment, (value) => {
|
|
346
|
+
sidecarSessionId = value;
|
|
347
|
+
ownsSidecarSession = true;
|
|
348
|
+
});
|
|
349
|
+
if (endpoint === '')
|
|
350
|
+
throw new Error('WorkBuddy sidecar 未返回 ACP 地址');
|
|
351
|
+
client = new WorkBuddyHttpAcpClient(endpoint);
|
|
352
|
+
await client.connect(input.signal);
|
|
353
|
+
const state = {
|
|
354
|
+
client,
|
|
355
|
+
sidecar,
|
|
356
|
+
sidecarSessionId,
|
|
357
|
+
get providerSessionId() { return providerSessionId; },
|
|
358
|
+
dispose: cleanup,
|
|
359
|
+
};
|
|
360
|
+
this.sidecarProcesses.set(input.sessionId, state);
|
|
361
|
+
await client.request('initialize', {
|
|
362
|
+
protocolVersion: 1,
|
|
363
|
+
clientInfo: { name: 'codingns4dsh', version: '0.1.1' },
|
|
364
|
+
clientCapabilities: {},
|
|
365
|
+
}, sidecarSignalOptions(input.signal));
|
|
366
|
+
await client.notify('initialized', {}, input.signal);
|
|
367
|
+
const session = input.providerSessionId
|
|
368
|
+
? await client.request('session/load', {
|
|
369
|
+
sessionId: input.providerSessionId,
|
|
370
|
+
cwd: input.cwd ?? process.cwd(),
|
|
371
|
+
mcpServers: acpBridgeMcpServers(input.sessionId, this.profile.adapterId),
|
|
372
|
+
}, sidecarSignalOptions(input.signal))
|
|
373
|
+
: await client.request('session/new', {
|
|
374
|
+
cwd: input.cwd ?? process.cwd(),
|
|
375
|
+
mcpServers: acpBridgeMcpServers(input.sessionId, this.profile.adapterId),
|
|
376
|
+
}, sidecarSignalOptions(input.signal));
|
|
377
|
+
providerSessionId = readSessionId(session) ?? input.providerSessionId ?? input.sessionId;
|
|
378
|
+
yield { type: 'session-binding', providerSessionId };
|
|
379
|
+
const modelId = resolveCodeBuddyModelId(this.profile, input.modelId, input.effortId);
|
|
380
|
+
if (!isProviderDefaultModel(modelId)) {
|
|
381
|
+
await client.request('session/set_model', { sessionId: providerSessionId, modelId }, sidecarSignalOptions(input.signal, false)).catch(() => undefined);
|
|
382
|
+
}
|
|
383
|
+
const stream = client.streamRequest('session/prompt', {
|
|
384
|
+
sessionId: providerSessionId,
|
|
385
|
+
prompt: await buildAcpPromptBlocks(input.prompt, input.attachments ?? []),
|
|
386
|
+
}, input.signal);
|
|
387
|
+
let meaningful = false;
|
|
388
|
+
let richUsageSeen = false;
|
|
389
|
+
let reportedContextWindow;
|
|
390
|
+
let terminal;
|
|
391
|
+
const segmentState = createCodingNsSegmentState();
|
|
392
|
+
let promptResponse;
|
|
393
|
+
while (true) {
|
|
394
|
+
const item = await stream.next();
|
|
395
|
+
if (item.done) {
|
|
396
|
+
promptResponse = item.value;
|
|
397
|
+
break;
|
|
398
|
+
}
|
|
399
|
+
const rawChunk = codeBuddyMessageToChunk(item.value);
|
|
400
|
+
const chunk = rawChunk === null ? null : decorateCodingNsSegmentEvent(rawChunk, input, segmentState, this.descriptor.id);
|
|
401
|
+
if (chunk?.type === 'finish') {
|
|
402
|
+
terminal = chunk;
|
|
403
|
+
continue;
|
|
404
|
+
}
|
|
405
|
+
if (chunk !== null) {
|
|
406
|
+
meaningful = true;
|
|
407
|
+
if (chunk.type === 'usage') {
|
|
408
|
+
if (chunk.contextWindow !== undefined)
|
|
409
|
+
reportedContextWindow = chunk.contextWindow;
|
|
410
|
+
if (hasTokenUsage(chunk))
|
|
411
|
+
richUsageSeen = true;
|
|
412
|
+
}
|
|
413
|
+
yield chunk;
|
|
414
|
+
advanceCodingNsSegment(chunk, segmentState);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
if (!richUsageSeen) {
|
|
418
|
+
const historyUsage = await this.readHistoryUsage(input, providerSessionId);
|
|
419
|
+
if (historyUsage !== null)
|
|
420
|
+
yield enrichCodeBuddyUsageContext(historyUsage, reportedContextWindow);
|
|
421
|
+
}
|
|
422
|
+
const responseHasStopReason = isRecord(promptResponse) && typeof promptResponse.stopReason === 'string';
|
|
423
|
+
if (responseHasStopReason) {
|
|
424
|
+
yield promptFinish(promptResponse, input.signal);
|
|
425
|
+
}
|
|
426
|
+
else if (terminal !== undefined) {
|
|
427
|
+
if (meaningful)
|
|
428
|
+
yield terminal;
|
|
429
|
+
else
|
|
430
|
+
yield emptyResponseFinish(terminal.failure ?? promptFailure(promptResponse));
|
|
431
|
+
}
|
|
432
|
+
else {
|
|
433
|
+
yield emptyResponseFinish(promptFailure(promptResponse));
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
finally {
|
|
437
|
+
await cleanup();
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
/** ACP 未提供 usage 时,从 CodeBuddy 已落盘的最新 assistant 记录补齐用量。 */
|
|
441
|
+
async readHistoryUsage(input, providerSessionId) {
|
|
442
|
+
let rawStoreRef = input.rawStoreRef?.trim() || undefined;
|
|
443
|
+
// CLI 写 JSONL 与结束响应存在极短竞态,最多等待 100ms;失败时保持原有
|
|
444
|
+
// ACP 行为,不把“读不到历史”误报成 Provider 错误。
|
|
445
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
446
|
+
if (rawStoreRef === undefined) {
|
|
447
|
+
const probe = await this.probeSession({
|
|
448
|
+
providerSessionId,
|
|
449
|
+
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
450
|
+
}).catch(() => undefined);
|
|
451
|
+
rawStoreRef = probe?.rawStoreRef;
|
|
452
|
+
}
|
|
453
|
+
if (rawStoreRef !== undefined) {
|
|
454
|
+
const usage = readCodeBuddyHistoryUsage(rawStoreRef);
|
|
455
|
+
if (usage !== null) {
|
|
456
|
+
const modelId = readCodeBuddyHistoryModel(rawStoreRef);
|
|
457
|
+
const configuredWindow = modelId === null
|
|
458
|
+
? undefined
|
|
459
|
+
: readCodeBuddyModelContextWindow(modelId, this.configRoot, this.environment, this.readModelCatalogFile);
|
|
460
|
+
return enrichCodeBuddyUsageContext(usage, configuredWindow);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
if (attempt < 2)
|
|
464
|
+
await delay(50);
|
|
465
|
+
}
|
|
466
|
+
return null;
|
|
467
|
+
}
|
|
468
|
+
/** 中断当前 ACP 进程;服务端取消失败时仍关闭本轮进程。 */
|
|
469
|
+
async interrupt(sessionId) {
|
|
470
|
+
const sidecarState = this.sidecarProcesses.get(sessionId);
|
|
471
|
+
if (sidecarState !== undefined) {
|
|
472
|
+
await sidecarState.client.request('session/cancel', { sessionId: sidecarState.providerSessionId || sessionId }, { killOnAbort: false }).catch(() => undefined);
|
|
473
|
+
await sidecarState.dispose().catch(() => undefined);
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
const state = this.processes.get(sessionId);
|
|
477
|
+
if (state === undefined)
|
|
478
|
+
return;
|
|
479
|
+
try {
|
|
480
|
+
await state.rpc.request('session/cancel', { sessionId: state.providerSessionId || sessionId }, { killOnAbort: false });
|
|
481
|
+
}
|
|
482
|
+
catch { /* 进程关闭兜底 */ }
|
|
483
|
+
state.rpc.dispose();
|
|
484
|
+
}
|
|
485
|
+
dispose() {
|
|
486
|
+
for (const state of this.processes.values())
|
|
487
|
+
state.rpc.dispose();
|
|
488
|
+
this.processes.clear();
|
|
489
|
+
for (const state of this.sidecarProcesses.values())
|
|
490
|
+
void state.dispose().catch(() => undefined);
|
|
491
|
+
this.sidecarProcesses.clear();
|
|
492
|
+
this.cachedCommand = null;
|
|
493
|
+
this.lastVersion = null;
|
|
494
|
+
}
|
|
495
|
+
explicitCommand() {
|
|
496
|
+
if (this.commandPath !== undefined)
|
|
497
|
+
return this.commandPath;
|
|
498
|
+
const value = this.profile.commandEnvOverride === ''
|
|
499
|
+
? undefined
|
|
500
|
+
: this.environment[this.profile.commandEnvOverride]
|
|
501
|
+
?? (this.profile.adapterId === CODEBUDDY_PROFILE.adapterId ? this.environment.CODEBUDDY_CN_CLI_PATH : undefined);
|
|
502
|
+
return value?.trim() || undefined;
|
|
503
|
+
}
|
|
504
|
+
candidateCommands() {
|
|
505
|
+
if (this.profile.bundledInApp && !this.binariesOverride)
|
|
506
|
+
return bundledWorkBuddyCommands(this.platform, this.environment);
|
|
507
|
+
return this.binaries;
|
|
508
|
+
}
|
|
509
|
+
detectCommand(command) {
|
|
510
|
+
try {
|
|
511
|
+
const options = {
|
|
512
|
+
encoding: 'utf8', timeout: 5_000, windowsHide: true, shell: WINDOWS,
|
|
513
|
+
env: this.runtimeEnvironment(command),
|
|
514
|
+
};
|
|
515
|
+
const result = this.runSpawnSync(command, ['--version'], options);
|
|
516
|
+
const output = `${result.stdout ?? ''}\n${result.stderr ?? ''}`;
|
|
517
|
+
const version = output.match(/\b\d+\.\d+(?:\.\d+)?(?:[-+][0-9A-Za-z.-]+)?\b/u)?.[0];
|
|
518
|
+
if (result.status === 0 && version !== undefined) {
|
|
519
|
+
const help = this.runSpawnSync(command, ['--help'], options);
|
|
520
|
+
const helpOutput = `${help.stdout ?? ''}\n${help.stderr ?? ''}`;
|
|
521
|
+
if (!/--acp(?:[\s=]|$)/u.test(helpOutput)) {
|
|
522
|
+
this.detectionDiagnostic = help.status === 0
|
|
523
|
+
? `${this.profile.displayName} CLI 不支持 ACP 协议`
|
|
524
|
+
: summarizeCliFailure(this.profile.displayName, helpOutput);
|
|
525
|
+
return null;
|
|
526
|
+
}
|
|
527
|
+
this.cachedCommand = command;
|
|
528
|
+
this.lastVersion = version;
|
|
529
|
+
this.detectionDiagnostic = undefined;
|
|
530
|
+
return { installed: true, version, command };
|
|
531
|
+
}
|
|
532
|
+
const failure = summarizeCliFailure(this.profile.displayName, output);
|
|
533
|
+
if (failure !== undefined)
|
|
534
|
+
this.detectionDiagnostic = failure;
|
|
535
|
+
// CodeBuddy 的 PATH shim 可能在 GUI 环境中不在当前 PATH,失败后只对非内置产品做一次登录 Shell 查找。
|
|
536
|
+
if (!this.profile.bundledInApp && !isAbsolute(command) && result.status === null) {
|
|
537
|
+
const resolved = resolveCommandPath(command, this.runSpawnSync);
|
|
538
|
+
if (resolved !== null && resolved !== command)
|
|
539
|
+
return this.detectCommand(resolved);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
catch {
|
|
543
|
+
// 继续尝试下一个官方候选;错误只保留脱敏诊断。
|
|
544
|
+
}
|
|
545
|
+
return null;
|
|
546
|
+
}
|
|
547
|
+
runtimeEnvironment(command) {
|
|
548
|
+
const env = { ...commandEnvironment(command), ...this.environment, ...(this.profile.environmentOverrides ?? {}) };
|
|
549
|
+
if (this.profile.adapterId === CODEBUDDY_PROFILE.adapterId) {
|
|
550
|
+
const region = detectCodeBuddyRegion(env);
|
|
551
|
+
if (region === 'cn') {
|
|
552
|
+
env.CODEBUDDY_INTERNET_ENVIRONMENT = 'internal';
|
|
553
|
+
env.CODEBUDDY_COPILOT_INTERNET_ENVIRONMENT = 'internal';
|
|
554
|
+
}
|
|
555
|
+
else {
|
|
556
|
+
delete env.CODEBUDDY_INTERNET_ENVIRONMENT;
|
|
557
|
+
delete env.CODEBUDDY_COPILOT_INTERNET_ENVIRONMENT;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
const authenticationHome = resolveAuthenticationHome(this.profile, this.platform, env);
|
|
561
|
+
if (authenticationHome !== undefined) {
|
|
562
|
+
env.HOME = authenticationHome;
|
|
563
|
+
if (this.platform === 'win32')
|
|
564
|
+
env.USERPROFILE = authenticationHome;
|
|
565
|
+
}
|
|
566
|
+
for (const variable of this.profile.configRootEnvVars)
|
|
567
|
+
env[variable] = this.configRoot;
|
|
568
|
+
return env;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
/** WorkBuddy 只允许应用内置 codebuddy,不会误启动 PATH 上的 CodeBuddy。 */
|
|
572
|
+
export class WorkBuddyCliDriver extends CodeBuddyCliDriver {
|
|
573
|
+
constructor(options = {}) {
|
|
574
|
+
super({ ...options, profile: WORKBUDDY_PROFILE });
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
/** 旧版 CN 类名兼容导出;CN/国际版现在统一使用 codebuddy 适配器。 */
|
|
578
|
+
export class CodeBuddyCnCliDriver extends CodeBuddyCliDriver {
|
|
579
|
+
constructor(options = {}) {
|
|
580
|
+
super(options);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
export { CodeBuddyCliDriver as CodeBuddyDriver, CodeBuddyCnCliDriver as CodeBuddyCnDriver };
|
|
584
|
+
function sidecarSignalOptions(signal, killOnAbort) {
|
|
585
|
+
return {
|
|
586
|
+
...(signal === undefined ? {} : { signal }),
|
|
587
|
+
...(killOnAbort === undefined ? {} : { killOnAbort }),
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
/** WorkBuddy sidecar 的逐行 JSON-RPC 控制客户端;socket 本身已由桌面应用设为 0700。 */
|
|
591
|
+
class WorkBuddySidecarClient {
|
|
592
|
+
socketPath;
|
|
593
|
+
closed = false;
|
|
594
|
+
nextId = 1;
|
|
595
|
+
constructor(socketPath) {
|
|
596
|
+
this.socketPath = socketPath;
|
|
597
|
+
}
|
|
598
|
+
async request(method, params = {}, options = {}) {
|
|
599
|
+
if (this.closed)
|
|
600
|
+
throw new Error('WorkBuddy sidecar 已关闭');
|
|
601
|
+
const id = this.nextId++;
|
|
602
|
+
return await new Promise((resolve, reject) => {
|
|
603
|
+
const socket = createConnection(this.socketPath);
|
|
604
|
+
let buffer = '';
|
|
605
|
+
let settled = false;
|
|
606
|
+
const timeout = setTimeout(() => finish(new Error(`WorkBuddy sidecar 请求超时:${method}`)), options.timeoutMs ?? 30_000);
|
|
607
|
+
timeout.unref?.();
|
|
608
|
+
const abort = () => finish(new Error('WorkBuddy sidecar 请求已取消'));
|
|
609
|
+
const finish = (error, value) => {
|
|
610
|
+
if (settled)
|
|
611
|
+
return;
|
|
612
|
+
settled = true;
|
|
613
|
+
clearTimeout(timeout);
|
|
614
|
+
options.signal?.removeEventListener('abort', abort);
|
|
615
|
+
socket.destroy();
|
|
616
|
+
if (error === undefined)
|
|
617
|
+
resolve(value);
|
|
618
|
+
else
|
|
619
|
+
reject(error);
|
|
620
|
+
};
|
|
621
|
+
const onLine = (line) => {
|
|
622
|
+
if (line.trim() === '')
|
|
623
|
+
return;
|
|
624
|
+
let message;
|
|
625
|
+
try {
|
|
626
|
+
message = JSON.parse(line);
|
|
627
|
+
}
|
|
628
|
+
catch {
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
if (message.id !== id)
|
|
632
|
+
return;
|
|
633
|
+
if (message.error !== undefined) {
|
|
634
|
+
finish(new JsonRpcRequestError(message.error.code, message.error.data, message.error.message ?? 'WorkBuddy sidecar 请求失败'));
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
finish(undefined, message.result);
|
|
638
|
+
};
|
|
639
|
+
socket.on('connect', () => {
|
|
640
|
+
socket.write(`${JSON.stringify({ jsonrpc: '2.0', id, method, params })}\n`);
|
|
641
|
+
});
|
|
642
|
+
socket.on('data', (chunk) => {
|
|
643
|
+
buffer += chunk.toString();
|
|
644
|
+
let index = buffer.indexOf('\n');
|
|
645
|
+
while (index >= 0) {
|
|
646
|
+
const line = buffer.slice(0, index);
|
|
647
|
+
buffer = buffer.slice(index + 1);
|
|
648
|
+
onLine(line);
|
|
649
|
+
index = buffer.indexOf('\n');
|
|
650
|
+
}
|
|
651
|
+
});
|
|
652
|
+
socket.once('error', (error) => finish(new Error(`WorkBuddy sidecar 连接失败:${error.message}`)));
|
|
653
|
+
socket.once('close', () => finish(new Error('WorkBuddy sidecar 连接已关闭')));
|
|
654
|
+
options.signal?.addEventListener('abort', abort, { once: true });
|
|
655
|
+
if (options.signal?.aborted)
|
|
656
|
+
abort();
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
dispose() {
|
|
660
|
+
this.closed = true;
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
/** WorkBuddy ACP 的 streamable HTTP 客户端。每个 POST 响应都是一段 SSE 消息流。 */
|
|
664
|
+
class WorkBuddyHttpAcpClient {
|
|
665
|
+
endpoint;
|
|
666
|
+
connectionId = '';
|
|
667
|
+
sessionToken = '';
|
|
668
|
+
nextId = 1;
|
|
669
|
+
closed = false;
|
|
670
|
+
activeControllers = new Set();
|
|
671
|
+
responseReleases = new WeakMap();
|
|
672
|
+
constructor(endpoint) {
|
|
673
|
+
this.endpoint = endpoint;
|
|
674
|
+
}
|
|
675
|
+
async connect(signal) {
|
|
676
|
+
const response = await fetch(`${this.endpoint}/connect`, {
|
|
677
|
+
method: 'POST',
|
|
678
|
+
headers: { accept: 'application/json', 'x-codebuddy-request': '1' },
|
|
679
|
+
...(signal === undefined ? {} : { signal }),
|
|
680
|
+
});
|
|
681
|
+
if (!response.ok)
|
|
682
|
+
throw new Error(`WorkBuddy ACP 连接失败(HTTP ${response.status})`);
|
|
683
|
+
const value = await response.json();
|
|
684
|
+
const connectionId = typeof value.connectionId === 'string' ? value.connectionId : '';
|
|
685
|
+
if (connectionId === '')
|
|
686
|
+
throw new Error('WorkBuddy ACP 未返回连接标识');
|
|
687
|
+
this.connectionId = connectionId;
|
|
688
|
+
this.sessionToken = typeof value.sessionToken === 'string' ? value.sessionToken : '';
|
|
689
|
+
}
|
|
690
|
+
async request(method, params = {}, options = {}) {
|
|
691
|
+
const stream = this.streamRequest(method, params, options.signal, options.killOnAbort);
|
|
692
|
+
let item = await stream.next();
|
|
693
|
+
while (!item.done)
|
|
694
|
+
item = await stream.next();
|
|
695
|
+
return item.value;
|
|
696
|
+
}
|
|
697
|
+
async notify(method, params = {}, signal) {
|
|
698
|
+
const response = await this.post({ jsonrpc: '2.0', method, params }, signal);
|
|
699
|
+
try {
|
|
700
|
+
// JSON-RPC 通知仍可能返回 `:ok` 或一段 SSE;必须消费响应体,
|
|
701
|
+
// 否则 undici 会保留连接,下一轮结束时留下悬挂 socket。
|
|
702
|
+
for await (const _message of readAcpSseMessages(response)) { /* 只排空通知响应。 */ }
|
|
703
|
+
}
|
|
704
|
+
finally {
|
|
705
|
+
this.releaseResponse(response);
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
async *streamRequest(method, params = {}, signal, _killOnAbort = true) {
|
|
709
|
+
const id = this.nextId++;
|
|
710
|
+
const response = await this.post({ jsonrpc: '2.0', id, method, params }, signal);
|
|
711
|
+
try {
|
|
712
|
+
let final;
|
|
713
|
+
for await (const message of readAcpSseMessages(response)) {
|
|
714
|
+
if (message.id === id) {
|
|
715
|
+
final = message;
|
|
716
|
+
continue;
|
|
717
|
+
}
|
|
718
|
+
if (typeof message.method === 'string' && message.id !== undefined && message.id !== null) {
|
|
719
|
+
void this.respond(message.id, { outcome: { outcome: 'cancelled' } }, signal);
|
|
720
|
+
continue;
|
|
721
|
+
}
|
|
722
|
+
yield message;
|
|
723
|
+
}
|
|
724
|
+
if (final === undefined)
|
|
725
|
+
throw new Error(`WorkBuddy ACP 未返回 ${method} 的响应`);
|
|
726
|
+
if (final.error !== undefined) {
|
|
727
|
+
throw new JsonRpcRequestError(final.error.code, final.error.data, final.error.message ?? 'WorkBuddy ACP 请求失败');
|
|
728
|
+
}
|
|
729
|
+
return final.result;
|
|
730
|
+
}
|
|
731
|
+
finally {
|
|
732
|
+
this.releaseResponse(response);
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
async dispose() {
|
|
736
|
+
if (this.closed)
|
|
737
|
+
return;
|
|
738
|
+
this.closed = true;
|
|
739
|
+
for (const controller of this.activeControllers)
|
|
740
|
+
controller.abort();
|
|
741
|
+
this.activeControllers.clear();
|
|
742
|
+
if (this.connectionId === '')
|
|
743
|
+
return;
|
|
744
|
+
try {
|
|
745
|
+
await fetch(this.endpoint, {
|
|
746
|
+
method: 'DELETE',
|
|
747
|
+
headers: this.headers(),
|
|
748
|
+
signal: AbortSignal.timeout(2_000),
|
|
749
|
+
});
|
|
750
|
+
}
|
|
751
|
+
catch {
|
|
752
|
+
// sidecar.session.kill 负责最终清理;HTTP DELETE 失败不应掩盖本轮结果。
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
async respond(id, result, signal) {
|
|
756
|
+
try {
|
|
757
|
+
const response = await this.post({ jsonrpc: '2.0', id, result }, signal);
|
|
758
|
+
try {
|
|
759
|
+
for await (const _message of readAcpSseMessages(response)) { /* 排空响应后再释放请求控制器。 */ }
|
|
760
|
+
}
|
|
761
|
+
finally {
|
|
762
|
+
this.releaseResponse(response);
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
catch { /* 取消态无需再上抛 */ }
|
|
766
|
+
}
|
|
767
|
+
async post(body, signal) {
|
|
768
|
+
if (this.closed)
|
|
769
|
+
throw new Error('WorkBuddy ACP 已关闭');
|
|
770
|
+
const controller = new AbortController();
|
|
771
|
+
this.activeControllers.add(controller);
|
|
772
|
+
const onAbort = () => controller.abort();
|
|
773
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
774
|
+
try {
|
|
775
|
+
const response = await fetch(this.endpoint, {
|
|
776
|
+
method: 'POST',
|
|
777
|
+
headers: { ...this.headers(), accept: 'application/json, text/event-stream', 'content-type': 'application/json', 'x-codebuddy-request': '1' },
|
|
778
|
+
body: JSON.stringify(body),
|
|
779
|
+
signal: controller.signal,
|
|
780
|
+
});
|
|
781
|
+
if (!response.ok) {
|
|
782
|
+
this.activeControllers.delete(controller);
|
|
783
|
+
throw new Error(`WorkBuddy ACP 请求失败(HTTP ${response.status})`);
|
|
784
|
+
}
|
|
785
|
+
this.responseReleases.set(response, () => {
|
|
786
|
+
signal?.removeEventListener('abort', onAbort);
|
|
787
|
+
this.activeControllers.delete(controller);
|
|
788
|
+
});
|
|
789
|
+
return response;
|
|
790
|
+
}
|
|
791
|
+
catch (error) {
|
|
792
|
+
signal?.removeEventListener('abort', onAbort);
|
|
793
|
+
this.activeControllers.delete(controller);
|
|
794
|
+
throw error;
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
releaseResponse(response) {
|
|
798
|
+
this.responseReleases.get(response)?.();
|
|
799
|
+
this.responseReleases.delete(response);
|
|
800
|
+
}
|
|
801
|
+
headers() {
|
|
802
|
+
return {
|
|
803
|
+
'acp-connection-id': this.connectionId,
|
|
804
|
+
...(this.sessionToken === '' ? {} : { 'acp-session-token': this.sessionToken }),
|
|
805
|
+
};
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
async function* readAcpSseMessages(response) {
|
|
809
|
+
const contentType = response.headers.get('content-type')?.toLowerCase() ?? '';
|
|
810
|
+
if (!contentType.includes('text/event-stream')) {
|
|
811
|
+
const text = await response.text();
|
|
812
|
+
if (text.trim() !== '') {
|
|
813
|
+
try {
|
|
814
|
+
const value = JSON.parse(text);
|
|
815
|
+
yield value;
|
|
816
|
+
}
|
|
817
|
+
catch { /* 非 JSON 的 :ok 响应没有协议消息。 */ }
|
|
818
|
+
}
|
|
819
|
+
return;
|
|
820
|
+
}
|
|
821
|
+
const body = response.body;
|
|
822
|
+
if (body === null)
|
|
823
|
+
return;
|
|
824
|
+
const reader = body.getReader();
|
|
825
|
+
const decoder = new TextDecoder();
|
|
826
|
+
let buffer = '';
|
|
827
|
+
let dataLines = [];
|
|
828
|
+
const emit = async function* () {
|
|
829
|
+
if (dataLines.length === 0)
|
|
830
|
+
return;
|
|
831
|
+
const data = dataLines.join('\n');
|
|
832
|
+
dataLines = [];
|
|
833
|
+
if (data.trim() === '')
|
|
834
|
+
return;
|
|
835
|
+
try {
|
|
836
|
+
const value = JSON.parse(data);
|
|
837
|
+
yield value;
|
|
838
|
+
}
|
|
839
|
+
catch { /* 忽略 SSE 心跳或厂商非 JSON 扩展。 */ }
|
|
840
|
+
};
|
|
841
|
+
while (true) {
|
|
842
|
+
const chunk = await reader.read();
|
|
843
|
+
buffer += decoder.decode(chunk.value ?? new Uint8Array(), { stream: !chunk.done });
|
|
844
|
+
let newline = buffer.indexOf('\n');
|
|
845
|
+
while (newline >= 0) {
|
|
846
|
+
const line = buffer.slice(0, newline).replace(/\r$/u, '');
|
|
847
|
+
buffer = buffer.slice(newline + 1);
|
|
848
|
+
if (line === '') {
|
|
849
|
+
for await (const message of emit())
|
|
850
|
+
yield message;
|
|
851
|
+
}
|
|
852
|
+
else if (line.startsWith('data:')) {
|
|
853
|
+
dataLines.push(line.slice(5).trimStart());
|
|
854
|
+
}
|
|
855
|
+
newline = buffer.indexOf('\n');
|
|
856
|
+
}
|
|
857
|
+
if (chunk.done)
|
|
858
|
+
break;
|
|
859
|
+
}
|
|
860
|
+
if (dataLines.length > 0)
|
|
861
|
+
for await (const message of emit())
|
|
862
|
+
yield message;
|
|
863
|
+
}
|
|
864
|
+
function discoverWorkBuddySidecarSocket(configRoot, platform, environment) {
|
|
865
|
+
const explicit = environment.WORKBUDDY_SIDECAR_SOCKET?.trim();
|
|
866
|
+
if (explicit)
|
|
867
|
+
return explicit;
|
|
868
|
+
const uid = typeof process.getuid === 'function' ? String(process.getuid()) : 'unknown';
|
|
869
|
+
const runtimeRoot = platform === 'win32'
|
|
870
|
+
? join(tmpdir(), 'wb', sha1Token(configRoot, 12))
|
|
871
|
+
: join(environment.TMPDIR?.trim() || tmpdir(), `wb-${sha1Token(uid, 6)}`, sha1Token(configRoot, 12));
|
|
872
|
+
const pidPath = join(runtimeRoot, 'sidecar.pid');
|
|
873
|
+
try {
|
|
874
|
+
const record = JSON.parse(readFileSync(pidPath, 'utf8'));
|
|
875
|
+
if (typeof record.controlPipeUuid !== 'string' || !/^[a-f0-9]+$/u.test(record.controlPipeUuid))
|
|
876
|
+
throw new Error('invalid sidecar pid record');
|
|
877
|
+
if (platform === 'win32')
|
|
878
|
+
return `\\\\.\\pipe\\workbuddy-${sha1Token(configRoot, 12)}-sidecar-${record.controlPipeUuid}`;
|
|
879
|
+
const socket = join(runtimeRoot, `sidecar-${record.controlPipeUuid}.sock`);
|
|
880
|
+
try {
|
|
881
|
+
statSync(socket);
|
|
882
|
+
return socket;
|
|
883
|
+
}
|
|
884
|
+
catch {
|
|
885
|
+
throw new Error('sidecar socket missing');
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
catch {
|
|
889
|
+
// 5.7.3 的 sidecar 先监听带随机 UUID 的 socket,再写 sidecar.pid。
|
|
890
|
+
// PID 文件可能因异常退出或文件清理丢失,但存活 sidecar 仍可被复用;
|
|
891
|
+
// 只扫描严格匹配的 control socket,避免把会话 data socket 当成控制端点。
|
|
892
|
+
if (platform === 'win32')
|
|
893
|
+
return null;
|
|
894
|
+
try {
|
|
895
|
+
const candidates = readdirSync(runtimeRoot, { withFileTypes: true })
|
|
896
|
+
.filter((entry) => (entry.isFile() || entry.isSocket()) && /^sidecar-[a-f0-9]+\.sock$/u.test(entry.name))
|
|
897
|
+
.flatMap((entry) => {
|
|
898
|
+
const socket = join(runtimeRoot, entry.name);
|
|
899
|
+
try {
|
|
900
|
+
return [{ socket, modifiedAt: statSync(socket).mtimeMs }];
|
|
901
|
+
}
|
|
902
|
+
catch {
|
|
903
|
+
return [];
|
|
904
|
+
}
|
|
905
|
+
})
|
|
906
|
+
.sort((left, right) => right.modifiedAt - left.modifiedAt);
|
|
907
|
+
return candidates[0]?.socket ?? null;
|
|
908
|
+
}
|
|
909
|
+
catch {
|
|
910
|
+
return null;
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
async function resolveWorkBuddyAcpEndpoint(sidecar, input, launch, configRoot, baseEnvironment, platform, environment, onCreated) {
|
|
915
|
+
const sessions = await sidecar.request('session.list', {}, { timeoutMs: 5_000 }).catch(() => []);
|
|
916
|
+
if (Array.isArray(sessions)) {
|
|
917
|
+
const candidates = sessions.filter((entry) => isRecord(entry) && typeof entry.acpEndpoint === 'string');
|
|
918
|
+
const host = candidates.find((entry) => typeof entry.sessionId === 'string' && entry.sessionId.includes('__workbuddy_cli_host__'));
|
|
919
|
+
const endpoint = host?.acpEndpoint ?? candidates[0]?.acpEndpoint;
|
|
920
|
+
if (typeof endpoint === 'string' && endpoint.trim() !== '')
|
|
921
|
+
return endpoint;
|
|
922
|
+
}
|
|
923
|
+
const sessionId = `codingns-${input.sessionId}-${randomUUID()}`;
|
|
924
|
+
const created = await sidecar.request('session.create', {
|
|
925
|
+
sessionId,
|
|
926
|
+
command: launch.command,
|
|
927
|
+
args: launch.args,
|
|
928
|
+
cwd: input.cwd ?? process.cwd(),
|
|
929
|
+
port: 0,
|
|
930
|
+
env: workBuddySidecarEnvironment(baseEnvironment, configRoot, launch.command, platform, environment),
|
|
931
|
+
}, { timeoutMs: 195_000 });
|
|
932
|
+
const endpoint = isRecord(created) && typeof created.acpEndpoint === 'string' ? created.acpEndpoint.trim() : '';
|
|
933
|
+
if (endpoint === '')
|
|
934
|
+
throw new Error('WorkBuddy sidecar 未返回 ACP 地址');
|
|
935
|
+
onCreated(sessionId);
|
|
936
|
+
return endpoint;
|
|
937
|
+
}
|
|
938
|
+
function sha1Token(value, length) {
|
|
939
|
+
return createHash('sha1').update(value).digest('hex').slice(0, length);
|
|
940
|
+
}
|
|
941
|
+
function workBuddyLaunchSpec(cliCommand, electronOverride, platform, environment) {
|
|
942
|
+
const cliPath = cliCommand;
|
|
943
|
+
if (platform === 'darwin') {
|
|
944
|
+
const appRoot = cliPath.match(/^(.*\.app)\/Contents\/Resources\//u)?.[1];
|
|
945
|
+
const electron = electronOverride
|
|
946
|
+
|| (appRoot === undefined ? undefined : join(appRoot, 'Contents/MacOS/Electron'))
|
|
947
|
+
|| '/Applications/WorkBuddy.app/Contents/MacOS/Electron';
|
|
948
|
+
return {
|
|
949
|
+
command: electron,
|
|
950
|
+
args: [cliPath, '--serve', '--no-session-persistence', '--setting-sources', 'user', '--strict-mcp-config'],
|
|
951
|
+
};
|
|
952
|
+
}
|
|
953
|
+
const electron = electronOverride || environment.WORKBUDDY_ELECTRON_PATH || cliPath;
|
|
954
|
+
return {
|
|
955
|
+
command: electron,
|
|
956
|
+
args: [cliPath, '--serve', '--no-session-persistence', '--setting-sources', 'user', '--strict-mcp-config'],
|
|
957
|
+
};
|
|
958
|
+
}
|
|
959
|
+
function workBuddySidecarEnvironment(base, configRoot, launchCommand, platform, environment) {
|
|
960
|
+
const appRoot = platform === 'darwin'
|
|
961
|
+
? launchCommand.match(/^(.*\.app)\/Contents\/MacOS\/Electron$/u)?.[1]
|
|
962
|
+
: undefined;
|
|
963
|
+
const appPath = base.WORKBUDDY_APP_PATH || (appRoot === undefined ? '/Applications/WorkBuddy.app/Contents/Resources/app.asar' : join(appRoot, 'Contents/Resources/app.asar'));
|
|
964
|
+
const resourcesPath = base.WORKBUDDY_RESOURCES_PATH || dirname(appPath);
|
|
965
|
+
const env = {
|
|
966
|
+
...base,
|
|
967
|
+
ELECTRON_RUN_AS_NODE: '1',
|
|
968
|
+
WORKBUDDY_IS_PACKAGED: '1',
|
|
969
|
+
WORKBUDDY_APP_PATH: base.WORKBUDDY_APP_PATH || appPath,
|
|
970
|
+
WORKBUDDY_CONFIG_DIR: configRoot,
|
|
971
|
+
CODEBUDDY_CONFIG_DIR: configRoot,
|
|
972
|
+
WORKBUDDY_DATA_FOLDER_NAME: '.workbuddy',
|
|
973
|
+
WORKBUDDY_APP_NAME: 'WorkBuddy',
|
|
974
|
+
WORKBUDDY_APPLICATION_NAME: 'WorkBuddy',
|
|
975
|
+
WORKBUDDY_PRODUCT_NAME: 'WorkBuddy',
|
|
976
|
+
WORKBUDDY_RESOURCES_PATH: resourcesPath,
|
|
977
|
+
WORKBUDDY_USER_DATA_DIR: base.WORKBUDDY_USER_DATA_DIR || join(configRoot, 'app'),
|
|
978
|
+
WORKBUDDY_NODE_ENV: 'production',
|
|
979
|
+
ACC_PRODUCT_CONFIG_PATH: base.ACC_PRODUCT_CONFIG_PATH || join(configRoot, 'cache/acc-product-config-v3.json'),
|
|
980
|
+
CODEBUDDY_DISABLE_IDE: '1',
|
|
981
|
+
CODEBUDDY_FORCE_HEADLESS_BUNDLE: '1',
|
|
982
|
+
CODEBUDDY_GATEWAY_AUTH: base.CODEBUDDY_GATEWAY_AUTH || 'password',
|
|
983
|
+
CODEBUDDY_GATEWAY_PASSWORD: base.CODEBUDDY_GATEWAY_PASSWORD || randomUUID(),
|
|
984
|
+
CODEBUDDY_HOST: base.CODEBUDDY_HOST || 'workbuddy-desktop',
|
|
985
|
+
CODEBUDDY_INTERNET_ENVIRONMENT: base.CODEBUDDY_INTERNET_ENVIRONMENT || 'internal',
|
|
986
|
+
CODEBUDDY_COPILOT_INTERNET_ENVIRONMENT: base.CODEBUDDY_COPILOT_INTERNET_ENVIRONMENT || 'internal',
|
|
987
|
+
SERVER__PORT: '0',
|
|
988
|
+
SERVER__HOST: '127.0.0.1',
|
|
989
|
+
...(environment.WORKBUDDY_EXTRA_PATHS === undefined ? {} : { WORKBUDDY_EXTRA_PATHS: environment.WORKBUDDY_EXTRA_PATHS }),
|
|
990
|
+
};
|
|
991
|
+
return env;
|
|
992
|
+
}
|
|
993
|
+
function resolveConfigRoot(profile, explicit, environment) {
|
|
994
|
+
const lookupEnvVars = profile.configRootLookupEnvVars ?? profile.configRootEnvVars;
|
|
995
|
+
const candidate = explicit?.trim()
|
|
996
|
+
|| lookupEnvVars.map((name) => environment[name]?.trim()).find((value) => Boolean(value))
|
|
997
|
+
|| profile.defaultConfigRoot;
|
|
998
|
+
return candidate;
|
|
999
|
+
}
|
|
1000
|
+
function bundledWorkBuddyCommands(platform, environment) {
|
|
1001
|
+
if (platform === 'darwin') {
|
|
1002
|
+
const roots = [
|
|
1003
|
+
'/Applications/WorkBuddy AI.app',
|
|
1004
|
+
'/Applications/WorkBuddy.app',
|
|
1005
|
+
join(homedir(), 'Applications/WorkBuddy AI.app'),
|
|
1006
|
+
join(homedir(), 'Applications/WorkBuddy.app'),
|
|
1007
|
+
];
|
|
1008
|
+
return roots.map((root) => join(root, 'Contents/Resources/app.asar.unpacked/cli/bin/codebuddy'));
|
|
1009
|
+
}
|
|
1010
|
+
if (platform === 'win32') {
|
|
1011
|
+
const local = environment.LOCALAPPDATA ?? join(homedir(), 'AppData/Local');
|
|
1012
|
+
const programFiles = environment.PROGRAMFILES ?? 'C:\\Program Files';
|
|
1013
|
+
const roots = [
|
|
1014
|
+
join(local, 'Programs/WorkBuddy AI'),
|
|
1015
|
+
join(local, 'Programs/WorkBuddy'),
|
|
1016
|
+
join(programFiles, 'WorkBuddy AI'),
|
|
1017
|
+
join(programFiles, 'WorkBuddy'),
|
|
1018
|
+
];
|
|
1019
|
+
return roots.flatMap((root) => [
|
|
1020
|
+
join(root, 'resources/app.asar.unpacked/cli/bin/codebuddy.exe'),
|
|
1021
|
+
join(root, 'resources/app.asar.unpacked/cli/bin/codebuddy.cmd'),
|
|
1022
|
+
join(root, 'resources/app.asar.unpacked/cli/bin/codebuddy'),
|
|
1023
|
+
]);
|
|
1024
|
+
}
|
|
1025
|
+
return [];
|
|
1026
|
+
}
|
|
1027
|
+
function uniqueCommands(commands) {
|
|
1028
|
+
return [...new Set(commands.filter((command) => command.trim().length > 0))];
|
|
1029
|
+
}
|
|
1030
|
+
/**
|
|
1031
|
+
* 官方认证文件不按 CODEBUDDY_CONFIG_DIR 分片,而是按 HOME 下的
|
|
1032
|
+
* CodeBuddyExtension/Data/Public 分片。若全局文件正好属于当前区域,复用它
|
|
1033
|
+
* 保持现有登录态;若属于另一区域或不存在,切换到适配器专用 HOME。
|
|
1034
|
+
*/
|
|
1035
|
+
function resolveAuthenticationHome(profile, platform, environment) {
|
|
1036
|
+
if (profile.isolatedHome === undefined)
|
|
1037
|
+
return undefined;
|
|
1038
|
+
const adapterRegion = profile.adapterId === CODEBUDDY_PROFILE.adapterId ? detectCodeBuddyRegion(environment) : undefined;
|
|
1039
|
+
if (adapterRegion === undefined)
|
|
1040
|
+
return profile.isolatedHome;
|
|
1041
|
+
const baseHome = platform === 'win32'
|
|
1042
|
+
? environment.USERPROFILE?.trim() || environment.HOME?.trim() || homedir()
|
|
1043
|
+
: environment.HOME?.trim() || homedir();
|
|
1044
|
+
const authPath = platform === 'darwin'
|
|
1045
|
+
? join(baseHome, 'Library/Application Support/CodeBuddyExtension/Data/Public/auth/Tencent-Cloud.coding-copilot.info')
|
|
1046
|
+
: platform === 'win32'
|
|
1047
|
+
? join(baseHome, 'AppData/Local/CodeBuddyExtension/Data/Public/auth/Tencent-Cloud.coding-copilot.info')
|
|
1048
|
+
: join(baseHome, '.local/share/CodeBuddyExtension/Data/Public/auth/Tencent-Cloud.coding-copilot.info');
|
|
1049
|
+
let domain;
|
|
1050
|
+
try {
|
|
1051
|
+
const parsed = JSON.parse(readFileSync(authPath, 'utf8'));
|
|
1052
|
+
if (typeof parsed.auth?.domain === 'string')
|
|
1053
|
+
domain = parsed.auth.domain.trim().toLowerCase();
|
|
1054
|
+
}
|
|
1055
|
+
catch {
|
|
1056
|
+
// 没有全局认证文件或文件损坏时,直接使用适配器隔离目录。
|
|
1057
|
+
}
|
|
1058
|
+
const matches = adapterRegion === 'cn' ? INTERNAL_AUTH_DOMAINS.has(domain ?? '') : EXTERNAL_AUTH_DOMAINS.has(domain ?? '');
|
|
1059
|
+
if (matches)
|
|
1060
|
+
return undefined;
|
|
1061
|
+
return adapterRegion === 'cn' ? CODEBUDDY_CN_AUTH_HOME : CODEBUDDY_INTERNATIONAL_AUTH_HOME;
|
|
1062
|
+
}
|
|
1063
|
+
/**
|
|
1064
|
+
* 从显式环境、认证域名和产品快照推断 CodeBuddy 区域。
|
|
1065
|
+
* 官方 CLI 只有一个二进制,区域必须在启动前确定,否则模型和请求端点会串线。
|
|
1066
|
+
*/
|
|
1067
|
+
function detectCodeBuddyRegion(environment) {
|
|
1068
|
+
const configured = `${environment.CODEBUDDY_INTERNET_ENVIRONMENT ?? environment.CODEBUDDY_COPILOT_INTERNET_ENVIRONMENT ?? ''}`.trim().toLowerCase();
|
|
1069
|
+
if (configured === 'internal' || configured === 'cn' || configured === 'china' || configured === 'ioa')
|
|
1070
|
+
return 'cn';
|
|
1071
|
+
if (configured === 'external' || configured === 'international' || configured === 'overseas')
|
|
1072
|
+
return 'international';
|
|
1073
|
+
const homes = uniquePaths([
|
|
1074
|
+
environment.HOME?.trim() || homedir(),
|
|
1075
|
+
CODEBUDDY_CN_AUTH_HOME,
|
|
1076
|
+
CODEBUDDY_INTERNATIONAL_AUTH_HOME,
|
|
1077
|
+
]);
|
|
1078
|
+
for (const home of homes) {
|
|
1079
|
+
const domain = readCodeBuddyAuthDomain(home, environment);
|
|
1080
|
+
if (domain === undefined)
|
|
1081
|
+
continue;
|
|
1082
|
+
if (INTERNAL_AUTH_DOMAINS.has(domain))
|
|
1083
|
+
return 'cn';
|
|
1084
|
+
if (EXTERNAL_AUTH_DOMAINS.has(domain))
|
|
1085
|
+
return 'international';
|
|
1086
|
+
}
|
|
1087
|
+
const productConfig = environment.ACC_PRODUCT_CONFIG_PATH?.trim();
|
|
1088
|
+
if (productConfig !== undefined && productConfig !== '') {
|
|
1089
|
+
try {
|
|
1090
|
+
const value = JSON.parse(readFileSync(productConfig, 'utf8'));
|
|
1091
|
+
const endpoint = typeof value.endpoint === 'string' ? value.endpoint.toLowerCase() : '';
|
|
1092
|
+
if (endpoint.includes('codebuddy.ai'))
|
|
1093
|
+
return 'international';
|
|
1094
|
+
if (endpoint.includes('copilot.tencent.com') || endpoint.includes('codebuddy.cn'))
|
|
1095
|
+
return 'cn';
|
|
1096
|
+
}
|
|
1097
|
+
catch {
|
|
1098
|
+
// 产品快照损坏时回退到官方默认国际环境。
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
return 'international';
|
|
1102
|
+
}
|
|
1103
|
+
function readCodeBuddyAuthDomain(home, environment) {
|
|
1104
|
+
const authPath = environment.CODEBUDDY_AUTH_FILE?.trim()
|
|
1105
|
+
|| (process.platform === 'darwin'
|
|
1106
|
+
? join(home, 'Library/Application Support/CodeBuddyExtension/Data/Public/auth/Tencent-Cloud.coding-copilot.info')
|
|
1107
|
+
: process.platform === 'win32'
|
|
1108
|
+
? join(home, 'AppData/Local/CodeBuddyExtension/Data/Public/auth/Tencent-Cloud.coding-copilot.info')
|
|
1109
|
+
: join(home, '.local/share/CodeBuddyExtension/Data/Public/auth/Tencent-Cloud.coding-copilot.info'));
|
|
1110
|
+
try {
|
|
1111
|
+
const value = JSON.parse(readFileSync(authPath, 'utf8'));
|
|
1112
|
+
return typeof value.auth?.domain === 'string' ? value.auth.domain.trim().toLowerCase() : undefined;
|
|
1113
|
+
}
|
|
1114
|
+
catch {
|
|
1115
|
+
return undefined;
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
function summarizeCliFailure(displayName, output) {
|
|
1119
|
+
if (/Cannot find module ['"]esbuild['"]/u.test(output)) {
|
|
1120
|
+
return `${displayName} CLI 启动失败:缺少依赖 esbuild,请重新安装 CLI`;
|
|
1121
|
+
}
|
|
1122
|
+
if (/Cannot find module/u.test(output)) {
|
|
1123
|
+
return `${displayName} CLI 启动失败:缺少 Node.js 依赖,请重新安装 CLI`;
|
|
1124
|
+
}
|
|
1125
|
+
if (output.trim().length === 0)
|
|
1126
|
+
return undefined;
|
|
1127
|
+
return `${displayName} CLI 启动失败,请检查命令输出`;
|
|
1128
|
+
}
|
|
1129
|
+
function defaultModelCatalogPaths(profile, configRoot, environment) {
|
|
1130
|
+
const region = profile.adapterId === CODEBUDDY_PROFILE.adapterId ? detectCodeBuddyRegion(environment) : undefined;
|
|
1131
|
+
const configuredNames = profile.adapterId === CODEBUDDY_PROFILE.adapterId
|
|
1132
|
+
? region === 'cn' ? ['CODEBUDDY_CN_MODEL_CATALOG', 'CODEBUDDY_MODEL_CATALOG'] : ['CODEBUDDY_MODEL_CATALOG', 'CODEBUDDY_CN_MODEL_CATALOG']
|
|
1133
|
+
: [profile.modelCatalogEnvVar ?? `${profile.adapterId.toUpperCase().replace(/[^A-Z0-9]+/gu, '_')}_MODEL_CATALOG`];
|
|
1134
|
+
const configured = configuredNames.flatMap((name) => {
|
|
1135
|
+
const value = environment[name]?.trim();
|
|
1136
|
+
return value === undefined || value === '' ? [] : [value];
|
|
1137
|
+
});
|
|
1138
|
+
const paths = [...configured];
|
|
1139
|
+
if (profile.adapterId === WORKBUDDY_PROFILE.adapterId) {
|
|
1140
|
+
const userDataRoot = join(homedir(), '.workbuddy');
|
|
1141
|
+
const configSiblingRoot = join(dirname(configRoot), '.workbuddy');
|
|
1142
|
+
// WorkBuddy 桌面端把最新云端产品快照写在 local_storage envelope 中;
|
|
1143
|
+
// cache/acc-product-config-v3.json 可能仍是旧端点的目录,必须优先读取前者。
|
|
1144
|
+
const localStorageRoots = uniquePaths([
|
|
1145
|
+
join(userDataRoot, 'local_storage'),
|
|
1146
|
+
join(configSiblingRoot, 'local_storage'),
|
|
1147
|
+
]);
|
|
1148
|
+
for (const root of localStorageRoots)
|
|
1149
|
+
paths.push(...workBuddyLocalStorageCatalogPaths(root));
|
|
1150
|
+
paths.push(join(userDataRoot, 'cache/acc-product-config-v3.json'));
|
|
1151
|
+
if (configSiblingRoot !== userDataRoot)
|
|
1152
|
+
paths.push(join(configSiblingRoot, 'cache/acc-product-config-v3.json'));
|
|
1153
|
+
}
|
|
1154
|
+
if (profile.adapterId === CODEBUDDY_PROFILE.adapterId) {
|
|
1155
|
+
if (region === 'cn')
|
|
1156
|
+
paths.push(join(configRoot, 'product.internal.json'));
|
|
1157
|
+
paths.push(join(configRoot, 'product.json'));
|
|
1158
|
+
if (region !== 'cn')
|
|
1159
|
+
paths.push(join(configRoot, 'product.internal.json'));
|
|
1160
|
+
}
|
|
1161
|
+
return [
|
|
1162
|
+
...paths,
|
|
1163
|
+
];
|
|
1164
|
+
}
|
|
1165
|
+
function defaultCatalogForProfile(profile) {
|
|
1166
|
+
if (profile.adapterId === WORKBUDDY_PROFILE.adapterId)
|
|
1167
|
+
return WORKBUDDY_DEFAULT_CATALOG;
|
|
1168
|
+
return CODEBUDDY_DEFAULT_CATALOG;
|
|
1169
|
+
}
|
|
1170
|
+
/** 从 CodeBuddy/WorkBuddy 产品快照投影主 Agent 真正可选的模型。 */
|
|
1171
|
+
function readProductCatalog(paths, readFile, command, profile, region = detectCodeBuddyRegion(process.env)) {
|
|
1172
|
+
const commandRoots = command === null ? [] : commandProductRoots(command);
|
|
1173
|
+
const commandCatalogs = commandRoots.flatMap((commandRoot) => [
|
|
1174
|
+
...(profile.adapterId === WORKBUDDY_PROFILE.adapterId
|
|
1175
|
+
? [join(commandRoot, 'product.internal.json')]
|
|
1176
|
+
: []),
|
|
1177
|
+
...(profile.adapterId === CODEBUDDY_PROFILE.adapterId && region === 'cn'
|
|
1178
|
+
? [join(commandRoot, 'product.internal.json'), join(commandRoot, 'product-ide-cn.json')]
|
|
1179
|
+
: []),
|
|
1180
|
+
join(commandRoot, 'product.json'),
|
|
1181
|
+
join(commandRoot, 'product-ide.json'),
|
|
1182
|
+
join(commandRoot, 'product.cloudhosted.json'),
|
|
1183
|
+
]);
|
|
1184
|
+
const candidates = [
|
|
1185
|
+
...paths,
|
|
1186
|
+
...commandCatalogs,
|
|
1187
|
+
];
|
|
1188
|
+
const seen = new Set();
|
|
1189
|
+
for (const path of candidates) {
|
|
1190
|
+
if (seen.has(path))
|
|
1191
|
+
continue;
|
|
1192
|
+
seen.add(path);
|
|
1193
|
+
let parsed;
|
|
1194
|
+
try {
|
|
1195
|
+
parsed = JSON.parse(String(readFile(path, 'utf8')));
|
|
1196
|
+
}
|
|
1197
|
+
catch {
|
|
1198
|
+
continue;
|
|
1199
|
+
}
|
|
1200
|
+
const catalog = parseProductCatalog(parsed, profile);
|
|
1201
|
+
if (catalog !== null)
|
|
1202
|
+
return catalog;
|
|
1203
|
+
}
|
|
1204
|
+
return null;
|
|
1205
|
+
}
|
|
1206
|
+
/** 返回 WorkBuddy 桌面端按更新时间排列的产品配置 envelope 文件。 */
|
|
1207
|
+
function workBuddyLocalStorageCatalogPaths(root) {
|
|
1208
|
+
try {
|
|
1209
|
+
return readdirSync(root, { withFileTypes: true })
|
|
1210
|
+
.filter((entry) => entry.isFile() && /^(?:wb_)?entry_[0-9a-f]+\.info$/u.test(entry.name))
|
|
1211
|
+
.map((entry) => {
|
|
1212
|
+
const path = join(root, entry.name);
|
|
1213
|
+
try {
|
|
1214
|
+
return { path, modifiedAt: statSync(path).mtimeMs };
|
|
1215
|
+
}
|
|
1216
|
+
catch {
|
|
1217
|
+
return { path, modifiedAt: 0 };
|
|
1218
|
+
}
|
|
1219
|
+
})
|
|
1220
|
+
.sort((left, right) => right.modifiedAt - left.modifiedAt)
|
|
1221
|
+
.map((entry) => entry.path);
|
|
1222
|
+
}
|
|
1223
|
+
catch {
|
|
1224
|
+
return [];
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
function uniquePaths(paths) {
|
|
1228
|
+
return [...new Set(paths)];
|
|
1229
|
+
}
|
|
1230
|
+
/** 从 CLI 入口推导官方产品包根目录,兼容 npm shim 和直接入口。 */
|
|
1231
|
+
function commandProductRoots(command) {
|
|
1232
|
+
const roots = new Set();
|
|
1233
|
+
const add = (path) => {
|
|
1234
|
+
const root = dirname(dirname(path));
|
|
1235
|
+
if (root !== '.')
|
|
1236
|
+
roots.add(root);
|
|
1237
|
+
};
|
|
1238
|
+
add(command);
|
|
1239
|
+
try {
|
|
1240
|
+
add(realpathSync(command));
|
|
1241
|
+
}
|
|
1242
|
+
catch { /* 命令可能只是 PATH 中的名称 */ }
|
|
1243
|
+
return [...roots];
|
|
1244
|
+
}
|
|
1245
|
+
function parseProductCatalog(value, profile) {
|
|
1246
|
+
const records = [];
|
|
1247
|
+
if (isRecord(value))
|
|
1248
|
+
records.push(value);
|
|
1249
|
+
else if (Array.isArray(value)) {
|
|
1250
|
+
for (const entry of value) {
|
|
1251
|
+
if (!isRecord(entry))
|
|
1252
|
+
continue;
|
|
1253
|
+
records.push(isRecord(entry.data) ? entry.data : entry);
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
for (const record of records) {
|
|
1257
|
+
const catalog = parseProductCatalogRecord(record, profile);
|
|
1258
|
+
if (catalog !== null)
|
|
1259
|
+
return catalog;
|
|
1260
|
+
}
|
|
1261
|
+
return null;
|
|
1262
|
+
}
|
|
1263
|
+
function parseProductCatalogRecord(value, profile) {
|
|
1264
|
+
if (!isRecord(value) || !Array.isArray(value.models))
|
|
1265
|
+
return null;
|
|
1266
|
+
const metadata = new Map();
|
|
1267
|
+
for (const entry of value.models) {
|
|
1268
|
+
if (!isRecord(entry))
|
|
1269
|
+
continue;
|
|
1270
|
+
const id = typeof entry.id === 'string' ? entry.id.trim() : '';
|
|
1271
|
+
if (id !== '')
|
|
1272
|
+
metadata.set(id, entry);
|
|
1273
|
+
}
|
|
1274
|
+
if (metadata.size === 0)
|
|
1275
|
+
return null;
|
|
1276
|
+
const primaryAgentNames = profile.adapterId === WORKBUDDY_PROFILE.adapterId ? ['cli'] : ['craft', 'cli', 'agent', 'chat'];
|
|
1277
|
+
const configuredIds = Array.isArray(value.agents)
|
|
1278
|
+
? value.agents
|
|
1279
|
+
.filter((entry) => isRecord(entry) && typeof entry.name === 'string' && primaryAgentNames.includes(entry.name))
|
|
1280
|
+
.flatMap((entry) => isRecord(entry) && Array.isArray(entry.models) ? entry.models : [])
|
|
1281
|
+
.filter((entry) => typeof entry === 'string' && entry.trim() !== '')
|
|
1282
|
+
.map((entry) => entry.trim())
|
|
1283
|
+
: [...metadata.keys()];
|
|
1284
|
+
const configuredModelIds = [...new Set(configuredIds)].filter((id) => metadata.has(id));
|
|
1285
|
+
// WorkBuddy 的最新快照已不再把 Auto 三档写入 `agents[].models`,但全局
|
|
1286
|
+
// `models` 仍然保留它们。目录投影必须以两处数据的并集为准,否则三档会
|
|
1287
|
+
// 消失,且 `auto.reasoning.effort` 会被错误地当成普通模型思考强度。
|
|
1288
|
+
const autoTierIds = profile.adapterId === WORKBUDDY_PROFILE.adapterId
|
|
1289
|
+
? WORKBUDDY_AUTO_TIER_MODELS.filter((model) => metadata.has(model.id)).map((model) => model.id)
|
|
1290
|
+
: [];
|
|
1291
|
+
const modelIds = [...new Set([...configuredModelIds, ...autoTierIds])];
|
|
1292
|
+
if (modelIds.length === 0)
|
|
1293
|
+
return null;
|
|
1294
|
+
// 产品配置把 Auto 三档展开进 agent.models;在 CodingNS 的目录契约中,
|
|
1295
|
+
// 它们属于默认模型的思考强度,不能继续作为三个普通模型渲染。
|
|
1296
|
+
const regularModelIds = modelIds.filter((id) => !autoTierIds.includes(id)
|
|
1297
|
+
&& !(profile.adapterId === WORKBUDDY_PROFILE.adapterId && id === WORKBUDDY_AUTO_MODEL_ID));
|
|
1298
|
+
const models = regularModelIds.map((id) => {
|
|
1299
|
+
const entry = metadata.get(id);
|
|
1300
|
+
const reasoning = isRecord(entry.reasoning) ? entry.reasoning : undefined;
|
|
1301
|
+
const supportedEfforts = reasoning !== undefined && Array.isArray(reasoning.supportedEfforts)
|
|
1302
|
+
? reasoning.supportedEfforts.filter((effort) => typeof effort === 'string' && effort.trim() !== '')
|
|
1303
|
+
: reasoning !== undefined && typeof reasoning.effort === 'string' && reasoning.effort.trim() !== ''
|
|
1304
|
+
? [reasoning.effort.trim()]
|
|
1305
|
+
: [];
|
|
1306
|
+
const name = typeof entry.name === 'string' && entry.name.trim() !== '' ? entry.name.trim() : id;
|
|
1307
|
+
const description = typeof entry.descriptionZh === 'string' && entry.descriptionZh.trim() !== ''
|
|
1308
|
+
? entry.descriptionZh.trim()
|
|
1309
|
+
: typeof entry.descriptionEn === 'string' && entry.descriptionEn.trim() !== ''
|
|
1310
|
+
? entry.descriptionEn.trim()
|
|
1311
|
+
: undefined;
|
|
1312
|
+
return {
|
|
1313
|
+
id,
|
|
1314
|
+
name,
|
|
1315
|
+
...(description === undefined ? {} : { description }),
|
|
1316
|
+
efforts: [...new Set(supportedEfforts)],
|
|
1317
|
+
};
|
|
1318
|
+
});
|
|
1319
|
+
const autoTierLabels = Object.fromEntries(autoTierIds.map((id) => {
|
|
1320
|
+
const configuredName = metadata.get(id)?.name;
|
|
1321
|
+
const fallbackName = WORKBUDDY_AUTO_TIER_MODELS.find((model) => model.id === id)?.name ?? id;
|
|
1322
|
+
return [id, typeof configuredName === 'string' && configuredName.trim() !== '' ? configuredName.trim() : fallbackName];
|
|
1323
|
+
}));
|
|
1324
|
+
const providerDefault = {
|
|
1325
|
+
id: 'provider-default',
|
|
1326
|
+
name: `跟随 ${profile.displayName} 默认模型`,
|
|
1327
|
+
efforts: autoTierIds,
|
|
1328
|
+
...(autoTierIds.length === 0 ? {} : { effortLabels: autoTierLabels }),
|
|
1329
|
+
};
|
|
1330
|
+
return {
|
|
1331
|
+
groups: [{ id: profile.adapterId, name: profile.displayName, models: [providerDefault, ...models] }],
|
|
1332
|
+
currentModel: null,
|
|
1333
|
+
currentEffort: null,
|
|
1334
|
+
};
|
|
1335
|
+
}
|
|
1336
|
+
function resolveCodeBuddyModelId(profile, modelId, effortId) {
|
|
1337
|
+
if (profile.adapterId === WORKBUDDY_PROFILE.adapterId && isProviderDefaultModel(modelId) && effortId !== undefined && WORKBUDDY_AUTO_TIER_IDS.has(effortId)) {
|
|
1338
|
+
return effortId;
|
|
1339
|
+
}
|
|
1340
|
+
return modelId;
|
|
1341
|
+
}
|
|
1342
|
+
function codeBuddyMessageToChunk(message) {
|
|
1343
|
+
const params = isRecord(message.params) ? message.params : message;
|
|
1344
|
+
const update = isRecord(params.update) ? params.update : params;
|
|
1345
|
+
const method = typeof message.method === 'string' ? message.method.toLowerCase() : '';
|
|
1346
|
+
const rawType = update.sessionUpdate ?? update.type ?? method;
|
|
1347
|
+
const type = typeof rawType === 'string' ? rawType.toLowerCase() : '';
|
|
1348
|
+
const text = acpText(update.delta ?? update.text ?? update.content ?? update.message);
|
|
1349
|
+
const content = isRecord(update.content) ? update.content : undefined;
|
|
1350
|
+
const messageId = firstToolText(update.messageId, update.message_id, update.itemId, update.item_id, content?.messageId, content?.message_id, content?.id);
|
|
1351
|
+
const withMessageId = messageId === undefined ? {} : { messageId };
|
|
1352
|
+
if (type.includes('permission') || type.includes('interruption'))
|
|
1353
|
+
return null;
|
|
1354
|
+
if (type.includes('thought') || type.includes('reason'))
|
|
1355
|
+
return text ? { type: 'reasoning-delta', text, ...withMessageId } : null;
|
|
1356
|
+
if (type.includes('user') && type.includes('message'))
|
|
1357
|
+
return null;
|
|
1358
|
+
if (type.includes('agent_message') || type.includes('text') || type === 'message')
|
|
1359
|
+
return text ? { type: 'text-delta', text, ...withMessageId } : null;
|
|
1360
|
+
if (type.includes('tool') || type.includes('command')) {
|
|
1361
|
+
const tool = isToolRecord(update.toolCall) ? update.toolCall : isToolRecord(update.tool_call) ? update.tool_call : update;
|
|
1362
|
+
const toolName = firstToolText(tool.name, tool.toolName, tool.tool_name, tool.title, update.name, update.toolName, update.tool_name, update.title);
|
|
1363
|
+
const callId = firstToolText(tool.callId, tool.call_id, tool.toolCallId, tool.tool_call_id, tool.toolUseId, tool.tool_use_id, tool.id, update.toolCallId, update.tool_call_id, update.id);
|
|
1364
|
+
if (toolName === undefined && callId === undefined)
|
|
1365
|
+
return null;
|
|
1366
|
+
const input = serializeToolValue(tool.rawInput ?? tool.input ?? tool.arguments ?? tool.args ?? tool.parameters ?? update.rawInput);
|
|
1367
|
+
const output = serializeToolValue(tool.rawOutput ?? tool.output ?? tool.result ?? update.rawOutput);
|
|
1368
|
+
const error = serializeToolValue(tool.error ?? update.error);
|
|
1369
|
+
const fallback = error !== undefined || type.includes('error') || type.includes('fail')
|
|
1370
|
+
? 'failed'
|
|
1371
|
+
: output !== undefined || type.includes('result') || type.includes('complete')
|
|
1372
|
+
? 'completed'
|
|
1373
|
+
: 'running';
|
|
1374
|
+
return {
|
|
1375
|
+
type: 'tool-event',
|
|
1376
|
+
toolName: toolName ?? 'tool',
|
|
1377
|
+
status: normalizeToolStatus(tool.status ?? tool.state ?? update.status, fallback),
|
|
1378
|
+
...(callId === undefined ? {} : { callId }),
|
|
1379
|
+
...(input === undefined ? {} : { input }),
|
|
1380
|
+
...(output === undefined ? {} : { output, outputMode: type.includes('delta') ? 'delta' : 'snapshot' }),
|
|
1381
|
+
...(error === undefined ? {} : { error }),
|
|
1382
|
+
...(firstToolText(tool.agentId, tool.agent_id, update.agentId, update.agent_id) ? { agentId: firstToolText(tool.agentId, tool.agent_id, update.agentId, update.agent_id) } : {}),
|
|
1383
|
+
...(serializeToolValue(tool.detail ?? update.detail) === undefined ? {} : { detail: serializeToolValue(tool.detail ?? update.detail) }),
|
|
1384
|
+
};
|
|
1385
|
+
}
|
|
1386
|
+
// CodeBuddy ACP 的 usage_update 使用 used/size 表示当前上下文占用和窗口,
|
|
1387
|
+
// 这不是账户套餐余量;先按会话上下文事件解析,避免把 used 误当成输入 token。
|
|
1388
|
+
if (type.includes('usage_update')) {
|
|
1389
|
+
const usage = codeBuddyUsageChunk(update);
|
|
1390
|
+
if (usage !== null)
|
|
1391
|
+
return usage;
|
|
1392
|
+
}
|
|
1393
|
+
const usage = usageChunk(update);
|
|
1394
|
+
if (usage !== null)
|
|
1395
|
+
return usage;
|
|
1396
|
+
if (type.includes('turn_completed') || type.includes('turn_complete') || type.includes('prompt_end') || type === 'done' || type === 'result' || type === 'completed')
|
|
1397
|
+
return { type: 'finish', reason: 'stop' };
|
|
1398
|
+
if (type.includes('error') || type.includes('failed')) {
|
|
1399
|
+
const failure = readFailure(update);
|
|
1400
|
+
return { type: 'finish', reason: 'error', ...(failure === undefined ? {} : { failure }) };
|
|
1401
|
+
}
|
|
1402
|
+
return null;
|
|
1403
|
+
}
|
|
1404
|
+
/** 解析 CodeBuddy ACP usage_update;仅投影会话上下文,不推断账户套餐额度。 */
|
|
1405
|
+
export function codeBuddyUsageChunk(value) {
|
|
1406
|
+
if (!isRecord(value))
|
|
1407
|
+
return null;
|
|
1408
|
+
const used = finiteNonNegativeNumber(value.used);
|
|
1409
|
+
const size = finiteNonNegativeNumber(value.size);
|
|
1410
|
+
if (used === undefined && size === undefined)
|
|
1411
|
+
return null;
|
|
1412
|
+
const contextTokens = used ?? 0;
|
|
1413
|
+
const contextWindow = size !== undefined && size > 0 ? size : undefined;
|
|
1414
|
+
return {
|
|
1415
|
+
type: 'usage',
|
|
1416
|
+
inputTokens: 0,
|
|
1417
|
+
outputTokens: 0,
|
|
1418
|
+
...(contextWindow === undefined ? {} : { contextWindow }),
|
|
1419
|
+
contextTokens,
|
|
1420
|
+
...(contextWindow === undefined ? {} : { contextUsageRatio: Number(Math.min(1, contextTokens / contextWindow).toFixed(6)) }),
|
|
1421
|
+
};
|
|
1422
|
+
}
|
|
1423
|
+
/** 判断 ACP usage 是否已经包含可用于 token-meter 的真实数值。 */
|
|
1424
|
+
function hasTokenUsage(event) {
|
|
1425
|
+
return event.inputTokens > 0 || event.outputTokens > 0
|
|
1426
|
+
|| (event.cacheReadTokens ?? 0) > 0 || (event.cacheWriteTokens ?? 0) > 0;
|
|
1427
|
+
}
|
|
1428
|
+
/** 把 ACP 已确认的窗口补到 JSONL token 采样,保证最后一个 usage 也含上下文比例。 */
|
|
1429
|
+
function enrichCodeBuddyUsageContext(event, contextWindow) {
|
|
1430
|
+
if (event.type !== 'usage' || event.contextWindow !== undefined || contextWindow === undefined || contextWindow <= 0)
|
|
1431
|
+
return event;
|
|
1432
|
+
const contextTokens = event.contextTokens ?? event.inputTokens;
|
|
1433
|
+
return {
|
|
1434
|
+
...event,
|
|
1435
|
+
contextWindow,
|
|
1436
|
+
contextTokens,
|
|
1437
|
+
contextUsageRatio: Number(Math.min(1, contextTokens / contextWindow).toFixed(6)),
|
|
1438
|
+
};
|
|
1439
|
+
}
|
|
1440
|
+
/**
|
|
1441
|
+
* 读取 CodeBuddy JSONL 中最新 assistant 记录的 provider 用量。
|
|
1442
|
+
*
|
|
1443
|
+
* CodeBuddy 的 ACP 通道不会把这组字段放进 `session/prompt`,但同一轮完成后
|
|
1444
|
+
* 会把 `rawUsage` 和 `message.usage` 原样写入会话文件。这里按完整输入、缓存读、
|
|
1445
|
+
* 缓存写三桶折算,避免把 `prompt_tokens` 和缓存桶重复计入 DSH。
|
|
1446
|
+
*/
|
|
1447
|
+
export function readCodeBuddyHistoryUsage(path) {
|
|
1448
|
+
let text;
|
|
1449
|
+
try {
|
|
1450
|
+
text = readFileSync(path, 'utf8');
|
|
1451
|
+
}
|
|
1452
|
+
catch {
|
|
1453
|
+
return null;
|
|
1454
|
+
}
|
|
1455
|
+
const lines = text.split(/\r?\n/u);
|
|
1456
|
+
for (let index = lines.length - 1; index >= 0; index -= 1) {
|
|
1457
|
+
const line = lines[index]?.trim();
|
|
1458
|
+
if (!line)
|
|
1459
|
+
continue;
|
|
1460
|
+
try {
|
|
1461
|
+
const record = JSON.parse(line);
|
|
1462
|
+
const usage = parseCodeBuddyHistoryUsage(record);
|
|
1463
|
+
if (usage !== null)
|
|
1464
|
+
return historyUsageEvent(usage);
|
|
1465
|
+
}
|
|
1466
|
+
catch {
|
|
1467
|
+
// JSONL 可能正被 CLI 追加,跳过损坏的尾行并继续寻找上一条完整记录。
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
return null;
|
|
1471
|
+
}
|
|
1472
|
+
/** 从最新 assistant 记录取得实际路由模型,用于查找动态产品快照的窗口。 */
|
|
1473
|
+
function readCodeBuddyHistoryModel(path) {
|
|
1474
|
+
let text;
|
|
1475
|
+
try {
|
|
1476
|
+
text = readFileSync(path, 'utf8');
|
|
1477
|
+
}
|
|
1478
|
+
catch {
|
|
1479
|
+
return null;
|
|
1480
|
+
}
|
|
1481
|
+
const lines = text.split(/\r?\n/u);
|
|
1482
|
+
for (let index = lines.length - 1; index >= 0; index -= 1) {
|
|
1483
|
+
const line = lines[index]?.trim();
|
|
1484
|
+
if (!line)
|
|
1485
|
+
continue;
|
|
1486
|
+
try {
|
|
1487
|
+
const value = JSON.parse(line);
|
|
1488
|
+
if (!isRecord(value))
|
|
1489
|
+
continue;
|
|
1490
|
+
const providerData = isRecord(value.providerData) ? value.providerData : undefined;
|
|
1491
|
+
const model = firstText(providerData?.model, providerData?.requestModelId, value.model);
|
|
1492
|
+
if (model !== undefined)
|
|
1493
|
+
return model;
|
|
1494
|
+
}
|
|
1495
|
+
catch {
|
|
1496
|
+
// 跳过尚未写完整的尾行。
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
return null;
|
|
1500
|
+
}
|
|
1501
|
+
/** 读取 CodeBuddy 动态产品快照中的模型上下文窗口。 */
|
|
1502
|
+
function readCodeBuddyModelContextWindow(modelId, configRoot, environment, readFile) {
|
|
1503
|
+
const home = environment.HOME?.trim() || homedir();
|
|
1504
|
+
const files = [
|
|
1505
|
+
join(configRoot, 'product.internal.json'),
|
|
1506
|
+
join(configRoot, 'product.json'),
|
|
1507
|
+
];
|
|
1508
|
+
const storageRoots = uniquePaths([
|
|
1509
|
+
join(configRoot, 'local_storage'),
|
|
1510
|
+
join(home, '.codebuddy/local_storage'),
|
|
1511
|
+
]);
|
|
1512
|
+
for (const root of storageRoots) {
|
|
1513
|
+
try {
|
|
1514
|
+
for (const entry of readdirSync(root, { withFileTypes: true })) {
|
|
1515
|
+
if (entry.isFile() && entry.name.endsWith('.info'))
|
|
1516
|
+
files.push(join(root, entry.name));
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
catch {
|
|
1520
|
+
// 当前安装可能没有 local_storage,继续检查产品文件。
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
for (const path of files) {
|
|
1524
|
+
let parsed;
|
|
1525
|
+
try {
|
|
1526
|
+
parsed = JSON.parse(String(readFile(path, 'utf8')));
|
|
1527
|
+
}
|
|
1528
|
+
catch {
|
|
1529
|
+
continue;
|
|
1530
|
+
}
|
|
1531
|
+
const records = Array.isArray(parsed) ? parsed : [parsed];
|
|
1532
|
+
for (const record of records) {
|
|
1533
|
+
const data = isRecord(record) && isRecord(record.data) ? record.data : record;
|
|
1534
|
+
if (!isRecord(data) || !Array.isArray(data.models))
|
|
1535
|
+
continue;
|
|
1536
|
+
const model = data.models.find((entry) => isRecord(entry) && entry.id === modelId);
|
|
1537
|
+
if (model === undefined)
|
|
1538
|
+
continue;
|
|
1539
|
+
const maxInputTokens = finiteNonNegativeNumber(model.maxInputTokens);
|
|
1540
|
+
if (maxInputTokens !== undefined && maxInputTokens > 0)
|
|
1541
|
+
return maxInputTokens;
|
|
1542
|
+
const contextWindow = isRecord(model.contextWindow) ? model.contextWindow : undefined;
|
|
1543
|
+
const defaultLength = finiteNonNegativeNumber(contextWindow?.defaultLength);
|
|
1544
|
+
if (defaultLength !== undefined && defaultLength > 0)
|
|
1545
|
+
return defaultLength;
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
return undefined;
|
|
1549
|
+
}
|
|
1550
|
+
function firstText(...values) {
|
|
1551
|
+
return values.find((value) => typeof value === 'string' && value.trim() !== '')?.trim();
|
|
1552
|
+
}
|
|
1553
|
+
function parseCodeBuddyHistoryUsage(value) {
|
|
1554
|
+
if (!isRecord(value))
|
|
1555
|
+
return null;
|
|
1556
|
+
const providerData = isRecord(value.providerData) ? value.providerData : undefined;
|
|
1557
|
+
const raw = providerData !== undefined && isRecord(providerData.rawUsage) ? providerData.rawUsage : undefined;
|
|
1558
|
+
const message = isRecord(value.message) ? value.message : undefined;
|
|
1559
|
+
const messageUsage = message !== undefined && isRecord(message.usage) ? message.usage : undefined;
|
|
1560
|
+
const providerUsage = providerData !== undefined && isRecord(providerData.usage) ? providerData.usage : undefined;
|
|
1561
|
+
if (raw === undefined && messageUsage === undefined && providerUsage === undefined)
|
|
1562
|
+
return null;
|
|
1563
|
+
const inputTokens = firstNumber(raw?.prompt_tokens, messageUsage?.input_tokens, providerUsage?.inputTokens, value.input_tokens);
|
|
1564
|
+
const outputTokens = firstNumber(raw?.completion_tokens, messageUsage?.output_tokens, providerUsage?.outputTokens, value.output_tokens);
|
|
1565
|
+
if (inputTokens === undefined && outputTokens === undefined)
|
|
1566
|
+
return null;
|
|
1567
|
+
const cacheReadTokens = largestNumber(raw?.prompt_cache_hit_tokens, raw?.cache_read_input_tokens, messageUsage?.cache_read_input_tokens, cachedTokenDetail(providerUsage));
|
|
1568
|
+
const cacheWriteTokens = largestNumber(raw?.cache_creation_input_tokens, raw?.prompt_cache_miss_tokens, raw?.prompt_cache_write_tokens, messageUsage?.cache_creation_input_tokens);
|
|
1569
|
+
const fullInput = inputTokens ?? Math.max(0, (cacheReadTokens ?? 0) + (cacheWriteTokens ?? 0));
|
|
1570
|
+
const output = outputTokens ?? 0;
|
|
1571
|
+
const totalTokens = firstNumber(raw?.total_tokens, messageUsage?.total_tokens, providerUsage?.totalTokens)
|
|
1572
|
+
?? fullInput + output;
|
|
1573
|
+
const modelUsage = isRecord(value.modelUsage)
|
|
1574
|
+
? value.modelUsage
|
|
1575
|
+
: providerData !== undefined && isRecord(providerData.modelUsage) ? providerData.modelUsage : undefined;
|
|
1576
|
+
const modelContextWindows = modelUsage === undefined
|
|
1577
|
+
? []
|
|
1578
|
+
: Object.values(modelUsage).map((entry) => isRecord(entry) ? entry.contextWindow ?? entry.context_window : undefined);
|
|
1579
|
+
const contextWindow = largestNumber(value.contextWindow, value.context_window, ...modelContextWindows);
|
|
1580
|
+
return {
|
|
1581
|
+
inputTokens: fullInput,
|
|
1582
|
+
outputTokens: output,
|
|
1583
|
+
...(cacheReadTokens === undefined ? {} : { cacheReadTokens }),
|
|
1584
|
+
...(cacheWriteTokens === undefined ? {} : { cacheWriteTokens }),
|
|
1585
|
+
totalTokens,
|
|
1586
|
+
...(contextWindow === undefined ? {} : { contextWindow }),
|
|
1587
|
+
};
|
|
1588
|
+
}
|
|
1589
|
+
function historyUsageEvent(usage) {
|
|
1590
|
+
const cached = (usage.cacheReadTokens ?? 0) + (usage.cacheWriteTokens ?? 0);
|
|
1591
|
+
const uncachedInputTokens = Math.max(0, usage.inputTokens - cached);
|
|
1592
|
+
return {
|
|
1593
|
+
type: 'usage',
|
|
1594
|
+
inputTokens: usage.inputTokens,
|
|
1595
|
+
outputTokens: usage.outputTokens,
|
|
1596
|
+
...(usage.cacheReadTokens === undefined ? {} : { cacheReadTokens: usage.cacheReadTokens }),
|
|
1597
|
+
...(usage.cacheWriteTokens === undefined ? {} : { cacheWriteTokens: usage.cacheWriteTokens }),
|
|
1598
|
+
uncachedInputTokens,
|
|
1599
|
+
...(usage.totalTokens === undefined ? {} : { totalTokens: usage.totalTokens }),
|
|
1600
|
+
...(usage.cacheReadTokens === undefined || usage.inputTokens <= 0
|
|
1601
|
+
? {}
|
|
1602
|
+
: { cacheHitRate: Number((usage.cacheReadTokens / usage.inputTokens * 100).toFixed(4)) }),
|
|
1603
|
+
contextTokens: usage.inputTokens,
|
|
1604
|
+
...(usage.contextWindow === undefined ? {} : {
|
|
1605
|
+
contextWindow: usage.contextWindow,
|
|
1606
|
+
contextUsageRatio: Number(Math.min(1, usage.inputTokens / usage.contextWindow).toFixed(6)),
|
|
1607
|
+
}),
|
|
1608
|
+
};
|
|
1609
|
+
}
|
|
1610
|
+
function cachedTokenDetail(value) {
|
|
1611
|
+
if (value === undefined || !Array.isArray(value.inputTokensDetails))
|
|
1612
|
+
return undefined;
|
|
1613
|
+
return largestNumber(...value.inputTokensDetails.map((entry) => isRecord(entry) ? entry.cached_tokens : undefined));
|
|
1614
|
+
}
|
|
1615
|
+
function firstNumber(...values) {
|
|
1616
|
+
for (const value of values) {
|
|
1617
|
+
const parsed = finiteNonNegativeNumber(value);
|
|
1618
|
+
if (parsed !== undefined)
|
|
1619
|
+
return parsed;
|
|
1620
|
+
}
|
|
1621
|
+
return undefined;
|
|
1622
|
+
}
|
|
1623
|
+
function largestNumber(...values) {
|
|
1624
|
+
const numbers = values.map(finiteNonNegativeNumber).filter((value) => value !== undefined);
|
|
1625
|
+
return numbers.length === 0 ? undefined : Math.max(...numbers);
|
|
1626
|
+
}
|
|
1627
|
+
function delay(milliseconds) {
|
|
1628
|
+
return new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
1629
|
+
}
|
|
1630
|
+
function finiteNonNegativeNumber(value) {
|
|
1631
|
+
if (typeof value === 'number' && Number.isFinite(value))
|
|
1632
|
+
return Math.max(0, value);
|
|
1633
|
+
if (typeof value === 'string' && value.trim() !== '') {
|
|
1634
|
+
const parsed = Number(value);
|
|
1635
|
+
return Number.isFinite(parsed) ? Math.max(0, parsed) : undefined;
|
|
1636
|
+
}
|
|
1637
|
+
return undefined;
|
|
1638
|
+
}
|
|
1639
|
+
function readSessionId(value) {
|
|
1640
|
+
if (!isRecord(value))
|
|
1641
|
+
return null;
|
|
1642
|
+
if (typeof value.sessionId === 'string' && value.sessionId.trim())
|
|
1643
|
+
return value.sessionId.trim();
|
|
1644
|
+
if (typeof value.session_id === 'string' && value.session_id.trim())
|
|
1645
|
+
return value.session_id.trim();
|
|
1646
|
+
if (isRecord(value.session) && typeof value.session.id === 'string' && value.session.id.trim())
|
|
1647
|
+
return value.session.id.trim();
|
|
1648
|
+
return typeof value.id === 'string' && value.id.trim() ? value.id.trim() : null;
|
|
1649
|
+
}
|
|
1650
|
+
function promptFinish(value, signal) {
|
|
1651
|
+
if (signal?.aborted)
|
|
1652
|
+
return { type: 'finish', reason: 'cancel' };
|
|
1653
|
+
if (!isRecord(value) || typeof value.stopReason !== 'string')
|
|
1654
|
+
return emptyResponseFinish();
|
|
1655
|
+
const reason = value.stopReason.toLowerCase();
|
|
1656
|
+
if (reason === 'cancelled' || reason === 'canceled')
|
|
1657
|
+
return { type: 'finish', reason: 'cancel' };
|
|
1658
|
+
if (reason === 'error' || reason === 'failed' || reason === 'refusal') {
|
|
1659
|
+
return {
|
|
1660
|
+
type: 'finish',
|
|
1661
|
+
reason: 'error',
|
|
1662
|
+
failure: promptFailure(value) ?? { message: 'WorkBuddy ACP 拒绝或结束了请求,但未返回具体错误信息。', code: 'PROVIDER_ERROR' },
|
|
1663
|
+
};
|
|
1664
|
+
}
|
|
1665
|
+
if (reason === 'end_turn' || reason === 'endturn' || reason === 'max_tokens' || reason === 'max_turn_requests' || reason === 'stop') {
|
|
1666
|
+
return { type: 'finish', reason: 'stop' };
|
|
1667
|
+
}
|
|
1668
|
+
return emptyResponseFinish();
|
|
1669
|
+
}
|
|
1670
|
+
function emptyResponseFinish(failure = { message: 'CODINGNS_PROVIDER_EMPTY_RESPONSE: WorkBuddy ACP 未返回任何有效响应。', code: 'PROVIDER_ERROR' }) {
|
|
1671
|
+
return { type: 'finish', reason: 'error', failure };
|
|
1672
|
+
}
|
|
1673
|
+
function promptFailure(value) {
|
|
1674
|
+
const failure = isRecord(value) ? readPromptFailure(value) : undefined;
|
|
1675
|
+
return failure;
|
|
1676
|
+
}
|
|
1677
|
+
function readPromptFailure(value) {
|
|
1678
|
+
const direct = readFailure(value);
|
|
1679
|
+
if (direct !== undefined)
|
|
1680
|
+
return direct;
|
|
1681
|
+
const metadata = isRecord(value._meta) ? value._meta : undefined;
|
|
1682
|
+
const encoded = metadata === undefined ? undefined : firstToolText(metadata['codebuddy.ai/errorMessage'], metadata.errorMessage, metadata.error_message);
|
|
1683
|
+
if (encoded === undefined)
|
|
1684
|
+
return undefined;
|
|
1685
|
+
try {
|
|
1686
|
+
const parsed = JSON.parse(encoded);
|
|
1687
|
+
if (isRecord(parsed))
|
|
1688
|
+
return readPromptFailure(parsed) ?? { message: encoded };
|
|
1689
|
+
}
|
|
1690
|
+
catch {
|
|
1691
|
+
// WorkBuddy 某些版本直接写入纯文本错误,保留该文本。
|
|
1692
|
+
}
|
|
1693
|
+
return { message: encoded };
|
|
1694
|
+
}
|
|
1695
|
+
function acpText(value) {
|
|
1696
|
+
const direct = textValue(value);
|
|
1697
|
+
if (direct)
|
|
1698
|
+
return direct;
|
|
1699
|
+
if (Array.isArray(value)) {
|
|
1700
|
+
const joined = value.map((item) => acpText(item) ?? '').join('');
|
|
1701
|
+
return joined.trim() ? joined : null;
|
|
1702
|
+
}
|
|
1703
|
+
if (isRecord(value)) {
|
|
1704
|
+
for (const key of ['text', 'delta', 'content', 'message']) {
|
|
1705
|
+
const nested = acpText(value[key]);
|
|
1706
|
+
if (nested)
|
|
1707
|
+
return nested;
|
|
1708
|
+
}
|
|
1709
|
+
}
|
|
1710
|
+
return null;
|
|
1711
|
+
}
|
|
1712
|
+
function readFailure(value) {
|
|
1713
|
+
const error = isRecord(value.error) ? value.error : value;
|
|
1714
|
+
const message = firstToolText(error.message, error.errorMessage, error.error_message, error.detail, error.reason);
|
|
1715
|
+
if (message === undefined)
|
|
1716
|
+
return undefined;
|
|
1717
|
+
const data = isRecord(value.data) ? value.data : undefined;
|
|
1718
|
+
const code = firstToolText(error.code, error.errorCode, error.error_code, data?.category);
|
|
1719
|
+
return code === undefined ? { message } : { message, code };
|
|
1720
|
+
}
|
|
1721
|
+
//# sourceMappingURL=codebuddy-driver.js.map
|