@jingyi0605/codingns4dsh 0.2.0-rc.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 +15 -6
- 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/qoder-cn.svg +5 -0
- package/assets/provider-icons/qoder.svg +5 -0
- package/assets/provider-icons/workbuddy.svg +29 -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/bundle.js +3864 -507
- 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 +111 -13
- package/data/build/dist/client/cli-slots.js.map +1 -1
- package/data/build/dist/client/debug/ui.d.ts.map +1 -1
- package/data/build/dist/client/debug/ui.js +2 -1
- package/data/build/dist/client/debug/ui.js.map +1 -1
- package/data/build/dist/client/delegate-command.d.ts +1 -0
- package/data/build/dist/client/delegate-command.d.ts.map +1 -1
- package/data/build/dist/client/delegate-command.js +486 -27
- package/data/build/dist/client/delegate-command.js.map +1 -1
- package/data/build/dist/client/delegate-plan.d.ts +13 -4
- package/data/build/dist/client/delegate-plan.d.ts.map +1 -1
- package/data/build/dist/client/delegate-plan.js +10 -6
- package/data/build/dist/client/delegate-plan.js.map +1 -1
- package/data/build/dist/client/delegate-ui-dom.d.ts +46 -0
- package/data/build/dist/client/delegate-ui-dom.d.ts.map +1 -1
- package/data/build/dist/client/delegate-ui-dom.js +291 -7
- package/data/build/dist/client/delegate-ui-dom.js.map +1 -1
- package/data/build/dist/client/features/cli-adapters.d.ts.map +1 -1
- package/data/build/dist/client/features/cli-adapters.js +117 -8
- package/data/build/dist/client/features/cli-adapters.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/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 +346 -18
- package/data/build/dist/client/features/peer-host.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.map +1 -1
- package/data/build/dist/client/git-management.js +21 -1
- package/data/build/dist/client/git-management.js.map +1 -1
- package/data/build/dist/client/locale.d.ts.map +1 -1
- package/data/build/dist/client/locale.js +60 -6
- package/data/build/dist/client/locale.js.map +1 -1
- package/data/build/dist/client/locales/terminalAuth.d.ts.map +1 -1
- package/data/build/dist/client/locales/terminalAuth.js +4 -0
- package/data/build/dist/client/locales/terminalAuth.js.map +1 -1
- package/data/build/dist/client/locales/usage.d.ts.map +1 -1
- package/data/build/dist/client/locales/usage.js +20 -0
- package/data/build/dist/client/locales/usage.js.map +1 -1
- package/data/build/dist/client/mobile-session-interaction.d.ts.map +1 -1
- package/data/build/dist/client/mobile-session-interaction.js +24 -7
- package/data/build/dist/client/mobile-session-interaction.js.map +1 -1
- 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/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-native-projection.d.ts +16 -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 +49 -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/provider-icon-assets.js +14 -0
- package/data/build/dist/client/provider-icon-assets.js.map +1 -1
- package/data/build/dist/client/provider-icons.d.ts.map +1 -1
- package/data/build/dist/client/provider-icons.js +15 -3
- package/data/build/dist/client/provider-icons.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/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 +7 -4
- package/data/build/dist/client/settings-section.js.map +1 -1
- package/data/build/dist/client/subscription-slot.d.ts.map +1 -1
- package/data/build/dist/client/subscription-slot.js +181 -16
- package/data/build/dist/client/subscription-slot.js.map +1 -1
- package/data/build/dist/client/terminal/model.d.ts +82 -3
- package/data/build/dist/client/terminal/model.d.ts.map +1 -1
- package/data/build/dist/client/terminal/model.js +368 -29
- 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 +215 -104
- package/data/build/dist/client/terminal/ui.js.map +1 -1
- package/data/build/dist/client/terminal/xterm-view.d.ts +5 -1
- package/data/build/dist/client/terminal/xterm-view.d.ts.map +1 -1
- package/data/build/dist/client/terminal/xterm-view.js +378 -20
- package/data/build/dist/client/terminal/xterm-view.js.map +1 -1
- 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.js +1 -1
- package/data/build/dist/host/cli-adapters/claude-driver.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 +12 -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 +153 -6
- 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 +637 -72
- 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 +2 -1
- package/data/build/dist/host/cli-adapters/delegate-dispatch.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/delegate-dispatch.js +14 -61
- package/data/build/dist/host/cli-adapters/delegate-dispatch.js.map +1 -1
- 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/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/feature.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/feature.js +269 -18
- package/data/build/dist/host/cli-adapters/feature.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.map +1 -1
- package/data/build/dist/host/cli-adapters/legacy-session-adapter.js +11 -0
- package/data/build/dist/host/cli-adapters/legacy-session-adapter.js.map +1 -1
- package/data/build/dist/host/cli-adapters/model-catalog.d.ts +5 -0
- package/data/build/dist/host/cli-adapters/model-catalog.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/model-catalog.js +130 -0
- 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 +33 -3
- package/data/build/dist/host/cli-adapters/native-subagent-dispatch.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/native-subagent-dispatch.js +115 -9
- package/data/build/dist/host/cli-adapters/native-subagent-dispatch.js.map +1 -1
- package/data/build/dist/host/cli-adapters/native-team-subagent.d.ts +14 -2
- package/data/build/dist/host/cli-adapters/native-team-subagent.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/native-team-subagent.js +23 -4
- package/data/build/dist/host/cli-adapters/native-team-subagent.js.map +1 -1
- 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 +9 -7
- package/data/build/dist/host/cli-adapters/opencode-driver.js.map +1 -1
- package/data/build/dist/host/cli-adapters/provider-subscription.d.ts +33 -3
- package/data/build/dist/host/cli-adapters/provider-subscription.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/provider-subscription.js +158 -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.d.ts +41 -2
- package/data/build/dist/host/cli-adapters/registry.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/registry.js +187 -18
- package/data/build/dist/host/cli-adapters/registry.js.map +1 -1
- package/data/build/dist/host/cli-adapters/session-store.d.ts +4 -0
- package/data/build/dist/host/cli-adapters/session-store.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/session-store.js +18 -2
- 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 +14 -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 +121 -23
- 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.map +1 -1
- package/data/build/dist/host/cli-adapters/subagent-tool.js +53 -7
- package/data/build/dist/host/cli-adapters/subagent-tool.js.map +1 -1
- package/data/build/dist/host/cli-bridge/bridge-server.d.ts +5 -0
- package/data/build/dist/host/cli-bridge/bridge-server.d.ts.map +1 -1
- package/data/build/dist/host/cli-bridge/bridge-server.js +7 -2
- package/data/build/dist/host/cli-bridge/bridge-server.js.map +1 -1
- package/data/build/dist/host/cli-bridge/command-code-mod.d.ts +5 -1
- package/data/build/dist/host/cli-bridge/command-code-mod.d.ts.map +1 -1
- package/data/build/dist/host/cli-bridge/command-code-mod.js +43 -12
- package/data/build/dist/host/cli-bridge/command-code-mod.js.map +1 -1
- package/data/build/dist/host/cli-bridge/dispatch.d.ts.map +1 -1
- package/data/build/dist/host/cli-bridge/dispatch.js +43 -5
- package/data/build/dist/host/cli-bridge/dispatch.js.map +1 -1
- package/data/build/dist/host/cli-bridge/mcp-stdio-entry.js +24 -5
- package/data/build/dist/host/cli-bridge/mcp-stdio-entry.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 +174 -30
- 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 +11 -1
- package/data/build/dist/host/index.d.ts.map +1 -1
- package/data/build/dist/host/index.js +12 -2
- package/data/build/dist/host/index.js.map +1 -1
- package/data/build/dist/host/modules/peer-host/peer-host-native-protocol.js +6 -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-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/native-session-bridge.d.ts.map +1 -1
- package/data/build/dist/host/native-session-bridge.js +36 -7
- package/data/build/dist/host/native-session-bridge.js.map +1 -1
- package/data/build/dist/host/rpc.js +5 -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 +12 -5
- 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 +64 -2
- package/data/build/dist/shared/contracts/cli-adapter.d.ts.map +1 -1
- package/data/build/dist/shared/contracts/config.d.ts +54 -11
- package/data/build/dist/shared/contracts/config.d.ts.map +1 -1
- package/data/build/dist/shared/contracts/config.js +52 -8
- package/data/build/dist/shared/contracts/config.js.map +1 -1
- package/data/build/dist/shared/contracts/subscription.d.ts +5 -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.js +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 +2 -2
- 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 +1 -1
- package/profile/README.md +2 -2
- package/profile/package.json +2 -2
- package/profile/version.json +1 -1
- package/version.json +1 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Service } from '@deepseek-ai/cordis';
|
|
2
2
|
import { debugInfo, debugWarn } from '../../shared/debug.js';
|
|
3
3
|
import { resolveCodingNsTranslator } from '../locale.js';
|
|
4
|
+
import { stripTerminalDeviceAttributeResponses } from '../../shared/terminal-input.js';
|
|
4
5
|
class ObservableValue {
|
|
5
6
|
value;
|
|
6
7
|
listeners = new Set();
|
|
@@ -18,7 +19,7 @@ class ObservableValue {
|
|
|
18
19
|
listener();
|
|
19
20
|
}
|
|
20
21
|
}
|
|
21
|
-
/** 一个 Sidebar
|
|
22
|
+
/** 一个 Sidebar 标签对应的终端模型;普通视图卸载只 detach,聚合视图可常驻 follow。 */
|
|
22
23
|
export class CodingNsTerminalView {
|
|
23
24
|
sessionId;
|
|
24
25
|
remote;
|
|
@@ -39,9 +40,21 @@ export class CodingNsTerminalView {
|
|
|
39
40
|
attachmentId;
|
|
40
41
|
/** 最近一次已排队的尺寸;ResizeObserver 可能在同一布局周期内重复触发。 */
|
|
41
42
|
lastResize;
|
|
43
|
+
reconnectTimer;
|
|
44
|
+
reconnectAttempts = 0;
|
|
45
|
+
/**
|
|
46
|
+
* 聚合终端由工作区库存拥有生命周期。
|
|
47
|
+
*
|
|
48
|
+
* 聚合页在切换右栏页签时可能会卸载 DOM,但 Host 里的终端仍然运行;
|
|
49
|
+
* 这类视图不能因为一次 DOM 卸载就释放 follow,否则再次点开标签必然
|
|
50
|
+
* 重新经历 environment/list/follow。显式 close 和 dispose 仍会强制释放。
|
|
51
|
+
*/
|
|
52
|
+
keepAlive;
|
|
53
|
+
initialState;
|
|
54
|
+
initialStateConsumed = false;
|
|
42
55
|
constructor(sessionId, id, remote, createWhenMissing, shellPath, onWorkspaceResolved,
|
|
43
56
|
// 追加在末尾:既有调用方与测试按位置传 shellPath / onWorkspaceResolved,不能前移。
|
|
44
|
-
t = resolveCodingNsTranslator()) {
|
|
57
|
+
t = resolveCodingNsTranslator(), keepAlive = false, initialState) {
|
|
45
58
|
this.sessionId = sessionId;
|
|
46
59
|
this.remote = remote;
|
|
47
60
|
this.createWhenMissing = createWhenMissing;
|
|
@@ -49,21 +62,43 @@ export class CodingNsTerminalView {
|
|
|
49
62
|
this.onWorkspaceResolved = onWorkspaceResolved;
|
|
50
63
|
this.t = t;
|
|
51
64
|
this.id = id;
|
|
65
|
+
this.keepAlive = keepAlive;
|
|
66
|
+
this.initialState = initialState;
|
|
52
67
|
// 标签兜底标题必须走词典:字段初始化阶段还取不到构造参数,因此在构造函数里建 store。
|
|
53
|
-
this.store = new ObservableValue({
|
|
68
|
+
this.store = new ObservableValue({
|
|
69
|
+
phase: initialState?.info === undefined ? 'idle' : 'disconnected',
|
|
70
|
+
...(initialState?.environment === undefined ? {} : { environment: initialState.environment }),
|
|
71
|
+
...(initialState?.info === undefined ? {} : { info: initialState.info }),
|
|
72
|
+
title: initialState?.info?.title ?? t('terminal.title'),
|
|
73
|
+
writable: false,
|
|
74
|
+
});
|
|
54
75
|
this.state = this.store;
|
|
55
76
|
}
|
|
56
77
|
mount() {
|
|
57
78
|
this.mounted += 1;
|
|
58
|
-
if (this.mounted === 1)
|
|
59
|
-
|
|
79
|
+
if (this.mounted === 1) {
|
|
80
|
+
const initial = this.initialState;
|
|
81
|
+
if (!this.initialStateConsumed && initial?.info !== undefined) {
|
|
82
|
+
this.initialStateConsumed = true;
|
|
83
|
+
// 库存快照已经证明终端仍在运行时,直接订阅 Host 常驻连接,
|
|
84
|
+
// 不再为切换会话重新读取 environment/list。环境快照尚未到达时也
|
|
85
|
+
// 可以 follow:environment 只影响输入上限和外观默认值,不能阻塞恢复。
|
|
86
|
+
if (initial.info.state === 'running')
|
|
87
|
+
this.connect();
|
|
88
|
+
else
|
|
89
|
+
void this.refresh();
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
void this.refresh();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
60
95
|
let active = true;
|
|
61
96
|
return () => {
|
|
62
97
|
if (!active)
|
|
63
98
|
return;
|
|
64
99
|
active = false;
|
|
65
100
|
this.mounted = Math.max(0, this.mounted - 1);
|
|
66
|
-
if (this.mounted === 0)
|
|
101
|
+
if (this.mounted === 0 && !this.keepAlive)
|
|
67
102
|
this.detach();
|
|
68
103
|
};
|
|
69
104
|
}
|
|
@@ -75,6 +110,44 @@ export class CodingNsTerminalView {
|
|
|
75
110
|
this.loading = this.load().finally(() => { this.loading = undefined; });
|
|
76
111
|
return this.loading;
|
|
77
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* 运行时丢失后按当前绑定重建终端。
|
|
115
|
+
*
|
|
116
|
+
* 这是"进程已经不存在"时的显式恢复动作:沿用同一个 terminalId 与工作区绑定,
|
|
117
|
+
* 让 Sidebar 标签、标题和尺寸都保持不变,而不是让用户去点"新建终端"。
|
|
118
|
+
*/
|
|
119
|
+
async rebuild() {
|
|
120
|
+
if (this.loading !== undefined)
|
|
121
|
+
return this.loading;
|
|
122
|
+
this.clearReconnect();
|
|
123
|
+
this.reconnectAttempts = 0;
|
|
124
|
+
this.patch({ phase: 'loading', writable: false });
|
|
125
|
+
const task = this.loadWithRebuild().finally(() => { this.loading = undefined; });
|
|
126
|
+
this.loading = task;
|
|
127
|
+
return task;
|
|
128
|
+
}
|
|
129
|
+
async loadWithRebuild() {
|
|
130
|
+
try {
|
|
131
|
+
const remote = resolveRemote(this.remote);
|
|
132
|
+
const environment = unwrap(await remote.environment(this.sessionId, this.lifetime.signal));
|
|
133
|
+
this.patch({ phase: 'creating', environment });
|
|
134
|
+
const info = unwrap(await remote.create(this.sessionId, {
|
|
135
|
+
id: this.id,
|
|
136
|
+
...(this.shellPath === undefined ? {} : { shellPath: this.shellPath }),
|
|
137
|
+
cols: Math.min(80, environment.maxCols),
|
|
138
|
+
rows: Math.min(24, environment.maxRows),
|
|
139
|
+
}, this.lifetime.signal));
|
|
140
|
+
debugInfo('codingns4dsh: client terminal rebuilt', { sessionId: this.sessionId, terminalId: info.id });
|
|
141
|
+
const { error: _error, ...rest } = this.store.getSnapshot();
|
|
142
|
+
this.store.set({ ...rest, environment, info, title: info.title });
|
|
143
|
+
if (this.mounted > 0)
|
|
144
|
+
this.connect();
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
if (!this.lifetime.signal.aborted)
|
|
148
|
+
this.fail(error);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
78
151
|
acknowledge(revision) {
|
|
79
152
|
if (this.pendingRender?.revision !== revision)
|
|
80
153
|
return;
|
|
@@ -82,6 +155,9 @@ export class CodingNsTerminalView {
|
|
|
82
155
|
this.pendingRender = undefined;
|
|
83
156
|
}
|
|
84
157
|
write(data) {
|
|
158
|
+
data = stripTerminalDeviceAttributeResponses(data);
|
|
159
|
+
if (data === '')
|
|
160
|
+
return;
|
|
85
161
|
const state = this.store.getSnapshot();
|
|
86
162
|
const attachmentId = this.attachmentId;
|
|
87
163
|
if (!state.writable || attachmentId === undefined || data === '')
|
|
@@ -156,7 +232,10 @@ export class CodingNsTerminalView {
|
|
|
156
232
|
if (binding.id !== this.id)
|
|
157
233
|
this.id = binding.id;
|
|
158
234
|
// 工作区已有绑定时,恢复必须失败闭合,不能用新 ID 偷建替代终端。
|
|
159
|
-
|
|
235
|
+
// 显式传入 terminalId 或读取到会话绑定时同样不能新建:工作区绑定
|
|
236
|
+
// 可能刚被另一个会话的关闭动作删除,此时旧标签仍在卸载途中,不能
|
|
237
|
+
// 把已经关闭的 ID 当成新终端重新 create。
|
|
238
|
+
createWhenMissing = createWhenMissing && !binding.existing;
|
|
160
239
|
}
|
|
161
240
|
}
|
|
162
241
|
const listed = unwrap(await remote.list(this.sessionId));
|
|
@@ -186,6 +265,7 @@ export class CodingNsTerminalView {
|
|
|
186
265
|
connect() {
|
|
187
266
|
if (this.followController !== undefined || this.lifetime.signal.aborted || this.mounted === 0)
|
|
188
267
|
return;
|
|
268
|
+
this.clearReconnect();
|
|
189
269
|
const controller = new AbortController();
|
|
190
270
|
this.followController = controller;
|
|
191
271
|
this.attachmentId = crypto.randomUUID();
|
|
@@ -207,12 +287,22 @@ export class CodingNsTerminalView {
|
|
|
207
287
|
}
|
|
208
288
|
await this.deliver(frame);
|
|
209
289
|
}
|
|
210
|
-
if (!signal.aborted)
|
|
211
|
-
|
|
290
|
+
if (!signal.aborted) {
|
|
291
|
+
// 流正常结束但终端仍在运行:这是连接层断开(Host 重连、服务器短暂不可达),
|
|
292
|
+
// 必须自动重连;只有真正结束的终端才停在终态。
|
|
293
|
+
if (this.isRunning())
|
|
294
|
+
this.scheduleReconnect();
|
|
295
|
+
else
|
|
296
|
+
this.patch({ phase: 'disconnected', writable: false });
|
|
297
|
+
}
|
|
212
298
|
}
|
|
213
299
|
catch (error) {
|
|
214
|
-
if (!signal.aborted)
|
|
215
|
-
this.
|
|
300
|
+
if (!signal.aborted) {
|
|
301
|
+
if (this.isRunning())
|
|
302
|
+
this.scheduleReconnect();
|
|
303
|
+
else
|
|
304
|
+
this.fail(error);
|
|
305
|
+
}
|
|
216
306
|
}
|
|
217
307
|
finally {
|
|
218
308
|
if (this.followController?.signal === signal) {
|
|
@@ -222,6 +312,9 @@ export class CodingNsTerminalView {
|
|
|
222
312
|
}
|
|
223
313
|
}
|
|
224
314
|
async deliver(frame) {
|
|
315
|
+
// 真正收到画面才算连接成功,退避计数在这里归零。
|
|
316
|
+
this.reconnectAttempts = 0;
|
|
317
|
+
this.clearReconnect();
|
|
225
318
|
this.pendingRender?.resolve();
|
|
226
319
|
this.revision += 1;
|
|
227
320
|
let resolveWaiting = () => { };
|
|
@@ -231,6 +324,7 @@ export class CodingNsTerminalView {
|
|
|
231
324
|
await waiting;
|
|
232
325
|
}
|
|
233
326
|
detach() {
|
|
327
|
+
this.clearReconnect();
|
|
234
328
|
this.pendingRender?.resolve();
|
|
235
329
|
this.pendingRender = undefined;
|
|
236
330
|
this.followController?.abort(new Error('终端视图已 detach'));
|
|
@@ -242,6 +336,54 @@ export class CodingNsTerminalView {
|
|
|
242
336
|
this.store.set({ ...state, phase: 'disconnected', writable: false });
|
|
243
337
|
}
|
|
244
338
|
}
|
|
339
|
+
/** 终端是否仍被认为在运行;终态(exited/failed/closed)不参与自动重连。 */
|
|
340
|
+
isRunning() {
|
|
341
|
+
const state = this.store.getSnapshot();
|
|
342
|
+
if (state.phase === 'closed' || state.phase === 'failed')
|
|
343
|
+
return false;
|
|
344
|
+
if (state.info === undefined)
|
|
345
|
+
return true;
|
|
346
|
+
return state.info.state === 'running';
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* 恢复阶段 Host 列表为空时,判断当前标签是否仍有本地生命周期保护。
|
|
350
|
+
* 新建终端在 create 完成前不能被恢复流程关闭;已连接且 Host 报告 running
|
|
351
|
+
* 的终端也不能因为一次空列表响应被误判为残留。
|
|
352
|
+
*/
|
|
353
|
+
isRecoveryProtected() {
|
|
354
|
+
const state = this.store.getSnapshot();
|
|
355
|
+
if (state.phase === 'closed' || state.phase === 'failed')
|
|
356
|
+
return false;
|
|
357
|
+
if (this.createWhenMissing && (state.phase === 'idle' || state.phase === 'loading' || state.phase === 'creating'))
|
|
358
|
+
return true;
|
|
359
|
+
return state.info?.id === this.id && state.info.state === 'running';
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* 连接层断开后的自动重连。
|
|
363
|
+
*
|
|
364
|
+
* 指数退避到 5 秒封顶:DSH 重启、中继抖动或 tmux 服务器短暂不可达时,用户
|
|
365
|
+
* 不需要手动点"重新连接",也不会看到刷屏式的重试。
|
|
366
|
+
*/
|
|
367
|
+
scheduleReconnect() {
|
|
368
|
+
if (this.reconnectTimer !== undefined || this.lifetime.signal.aborted || this.mounted === 0)
|
|
369
|
+
return;
|
|
370
|
+
const delay = Math.min(500 * 2 ** this.reconnectAttempts, 5000);
|
|
371
|
+
this.reconnectAttempts += 1;
|
|
372
|
+
this.patch({ phase: 'disconnected', writable: false });
|
|
373
|
+
debugInfo('codingns4dsh: client terminal reconnect scheduled', { sessionId: this.sessionId, terminalId: this.id, delay, attempt: this.reconnectAttempts });
|
|
374
|
+
this.reconnectTimer = setTimeout(() => {
|
|
375
|
+
this.reconnectTimer = undefined;
|
|
376
|
+
if (this.lifetime.signal.aborted || this.mounted === 0)
|
|
377
|
+
return;
|
|
378
|
+
void this.refresh();
|
|
379
|
+
}, delay);
|
|
380
|
+
}
|
|
381
|
+
clearReconnect() {
|
|
382
|
+
if (this.reconnectTimer !== undefined) {
|
|
383
|
+
clearTimeout(this.reconnectTimer);
|
|
384
|
+
this.reconnectTimer = undefined;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
245
387
|
patch(patch) {
|
|
246
388
|
this.store.set({ ...this.store.getSnapshot(), ...patch });
|
|
247
389
|
}
|
|
@@ -263,6 +405,16 @@ export class CodingNsWebTerminals extends Service {
|
|
|
263
405
|
closeFailures = this.closeFailureStore;
|
|
264
406
|
remoteReadyStore = new ObservableValue(false);
|
|
265
407
|
remoteReadyState = this.remoteReadyStore;
|
|
408
|
+
inventoryRevisionStore = new ObservableValue(0);
|
|
409
|
+
/** 任一工作区库存变化都会递增;聚合页用它触发跨会话刷新。 */
|
|
410
|
+
inventoryRevision = this.inventoryRevisionStore;
|
|
411
|
+
inventories = new Map();
|
|
412
|
+
/** 已解析过的会话环境;切换回已有会话时无需再次请求 Host environment。 */
|
|
413
|
+
environments = new Map();
|
|
414
|
+
/** 每个会话最近一次库存请求的序号;旧请求返回时不得覆盖新建结果。 */
|
|
415
|
+
inventoryRequestIds = new Map();
|
|
416
|
+
/** 任意工作区刷新都会递增;尚未解析工作区的旧请求也必须随之失效。 */
|
|
417
|
+
inventoryEpoch = 0;
|
|
266
418
|
closeRequests = new Map();
|
|
267
419
|
/** Remote 注入前不能执行关闭请求;就绪后统一冲刷,避免把启动竞态显示成永久错误。 */
|
|
268
420
|
cleanupQueued = false;
|
|
@@ -274,28 +426,66 @@ export class CodingNsWebTerminals extends Service {
|
|
|
274
426
|
this.closeRequests.set(String(request.id), request);
|
|
275
427
|
void this.flushCleanup();
|
|
276
428
|
}
|
|
277
|
-
view(sessionId, key, contentId, terminalId, shellPath) {
|
|
429
|
+
view(sessionId, key, contentId, terminalId, shellPath, createFresh = false, persistBinding = true, keepAlive = false, initialState) {
|
|
278
430
|
const mapKey = JSON.stringify([sessionId, key]);
|
|
279
431
|
const existing = this.views.get(mapKey);
|
|
280
432
|
if (existing !== undefined)
|
|
281
433
|
return existing.view;
|
|
282
434
|
const workspaceId = this.workspaceIds.get(sessionId);
|
|
283
|
-
const
|
|
284
|
-
|
|
285
|
-
|
|
435
|
+
const sessionBinding = readBinding(sessionId, contentId);
|
|
436
|
+
// “新建”是一次性动作:首次消费标记时忽略工作区单终端绑定,生成新的身份;
|
|
437
|
+
// 标签重载后若已有会话绑定,则恢复刚创建的终端,避免再次创建重复进程。
|
|
438
|
+
const fresh = createFresh && sessionBinding === undefined;
|
|
439
|
+
const saved = fresh
|
|
440
|
+
? terminalId
|
|
441
|
+
: terminalId
|
|
442
|
+
?? sessionBinding
|
|
443
|
+
?? (workspaceId === undefined ? undefined : readWorkspaceBindingWithMigration(workspaceId, contentId));
|
|
286
444
|
const id = saved ?? crypto.randomUUID();
|
|
287
445
|
debugInfo('codingns4dsh: client terminal view', { sessionId, key, contentId, workspaceId: workspaceId ?? null, savedId: saved ?? null, terminalId: id, bindingSource: terminalId !== undefined ? 'argument' : workspaceId !== undefined && readWorkspaceBindingWithMigration(workspaceId, contentId) !== undefined ? 'workspace-storage' : readBinding(sessionId, contentId) !== undefined ? 'session-storage' : 'new' });
|
|
288
|
-
if (saved === undefined)
|
|
446
|
+
if (persistBinding && (fresh || saved === undefined))
|
|
289
447
|
writeBinding(sessionId, contentId, id);
|
|
290
|
-
const view = new CodingNsTerminalView(sessionId, id, this.remote, saved === undefined, shellPath,
|
|
448
|
+
const view = new CodingNsTerminalView(sessionId, id, this.remote, fresh || saved === undefined, shellPath, persistBinding
|
|
449
|
+
? (resolvedWorkspaceId, currentId) => this.rememberWorkspace(sessionId, contentId, currentId, resolvedWorkspaceId, !fresh && terminalId === undefined)
|
|
450
|
+
: (resolvedWorkspaceId) => { this.workspaceIds.set(sessionId, resolvedWorkspaceId); return undefined; }, this.t, keepAlive, initialState);
|
|
291
451
|
this.views.set(mapKey, { contentId, view });
|
|
292
452
|
return view;
|
|
293
453
|
}
|
|
454
|
+
/** 聚合页按 Host terminalId 获取内部视图,不再依赖 Sidebar 标签身份。 */
|
|
455
|
+
viewForTerminal(sessionId, terminalId, shellPath) {
|
|
456
|
+
const key = `aggregate:${terminalId}`;
|
|
457
|
+
const info = this.inventoryForSession(sessionId).find((item) => item.id === terminalId);
|
|
458
|
+
const environment = this.environmentForSession(sessionId);
|
|
459
|
+
const initialState = info === undefined && environment === undefined
|
|
460
|
+
? undefined
|
|
461
|
+
: {
|
|
462
|
+
...(info === undefined ? {} : { info }),
|
|
463
|
+
...(environment === undefined ? {} : { environment }),
|
|
464
|
+
};
|
|
465
|
+
return this.view(sessionId, key, key, terminalId, shellPath, false, false, true, initialState);
|
|
466
|
+
}
|
|
294
467
|
/** 返回某个 Sidebar 内容已经保存的 Host 终端身份,用于恢复时去重。 */
|
|
295
468
|
boundTerminalId(sessionId, contentId) {
|
|
296
469
|
const workspaceId = this.workspaceIds.get(sessionId);
|
|
297
|
-
return (
|
|
298
|
-
??
|
|
470
|
+
return readBinding(sessionId, contentId)
|
|
471
|
+
?? (workspaceId === undefined ? undefined : readWorkspaceBindingWithMigration(workspaceId, contentId));
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* 判断恢复阶段是否应保留某个标签。
|
|
475
|
+
*
|
|
476
|
+
* Host 列表为空有两种含义:新建终端的 create 还没有完成,或者 Host 已经
|
|
477
|
+
* 关闭终端而 Sidebar 只剩残留标签。只有当前 Client 视图明确处于创建中或
|
|
478
|
+
* 仍收到 Host 的 running 状态时,空列表才属于前一种情况。
|
|
479
|
+
*/
|
|
480
|
+
isTerminalRecoveryProtected(sessionId, contentId, terminalId) {
|
|
481
|
+
for (const record of this.views.values()) {
|
|
482
|
+
if (record.view.sessionId !== sessionId || record.contentId !== contentId)
|
|
483
|
+
continue;
|
|
484
|
+
if (record.view.id !== terminalId)
|
|
485
|
+
continue;
|
|
486
|
+
return record.view.isRecoveryProtected();
|
|
487
|
+
}
|
|
488
|
+
return false;
|
|
299
489
|
}
|
|
300
490
|
async launchShells(sessionId, signal) {
|
|
301
491
|
const shells = unwrap(await resolveRemote(this.remote).shells(sessionId, signal));
|
|
@@ -306,6 +496,32 @@ export class CodingNsWebTerminals extends Service {
|
|
|
306
496
|
};
|
|
307
497
|
}
|
|
308
498
|
selectShell(path) { writeString(SHELL_KEY, path); }
|
|
499
|
+
/** 在聚合页内创建一个新的 Host 终端,并把它加入工作区库存。 */
|
|
500
|
+
async createTerminal(sessionId, shellPath) {
|
|
501
|
+
const terminalId = crypto.randomUUID();
|
|
502
|
+
const view = this.view(sessionId, `aggregate:${terminalId}`, `aggregate:${terminalId}`, terminalId, shellPath, true, false, true);
|
|
503
|
+
await view.refresh();
|
|
504
|
+
const info = view.state.getSnapshot().info;
|
|
505
|
+
if (info === undefined)
|
|
506
|
+
throw new Error(view.state.getSnapshot().error ?? '终端创建失败');
|
|
507
|
+
await this.refreshInventory(sessionId);
|
|
508
|
+
return info;
|
|
509
|
+
}
|
|
510
|
+
/** 只关闭指定 terminalId;其他会话中对应的 attach 视图只做 detach。 */
|
|
511
|
+
async closeTerminal(sessionId, terminalId) {
|
|
512
|
+
const records = [...this.views.entries()].filter(([, record]) => record.view.id === terminalId);
|
|
513
|
+
const primary = records.find(([, record]) => record.view.sessionId === sessionId)?.[1];
|
|
514
|
+
if (primary !== undefined)
|
|
515
|
+
await primary.view.close();
|
|
516
|
+
else
|
|
517
|
+
unwrap(await resolveRemote(this.remote).close(sessionId, terminalId));
|
|
518
|
+
for (const [mapKey, record] of records) {
|
|
519
|
+
this.views.delete(mapKey);
|
|
520
|
+
// close() 只结束 Host 终端,dispose() 负责释放聚合视图保留的 follow。
|
|
521
|
+
await record.view.dispose();
|
|
522
|
+
}
|
|
523
|
+
await this.refreshInventory(sessionId);
|
|
524
|
+
}
|
|
309
525
|
close(sessionId, key, contentId, terminalId) {
|
|
310
526
|
const mapKey = JSON.stringify([sessionId, key]);
|
|
311
527
|
const record = this.views.get(mapKey);
|
|
@@ -319,7 +535,7 @@ export class CodingNsWebTerminals extends Service {
|
|
|
319
535
|
deleteBinding(sessionId, contentId);
|
|
320
536
|
const workspaceId = this.workspaceIds.get(sessionId);
|
|
321
537
|
if (workspaceId !== undefined) {
|
|
322
|
-
|
|
538
|
+
deleteWorkspaceBindingIfMatches(workspaceId, id);
|
|
323
539
|
deleteLegacyWorkspaceBinding(workspaceId, contentId);
|
|
324
540
|
}
|
|
325
541
|
this.views.delete(mapKey);
|
|
@@ -329,15 +545,44 @@ export class CodingNsWebTerminals extends Service {
|
|
|
329
545
|
const pending = this.recoveries.get(sessionId);
|
|
330
546
|
if (pending !== undefined)
|
|
331
547
|
return pending;
|
|
548
|
+
const requestId = (this.inventoryRequestIds.get(sessionId) ?? 0) + 1;
|
|
549
|
+
this.inventoryRequestIds.set(sessionId, requestId);
|
|
550
|
+
const inventoryEpoch = this.inventoryEpoch;
|
|
332
551
|
// 官方 list wire 只有 sessionId;先用 agent-scoped environment 让 Host 解析工作区。
|
|
333
|
-
|
|
334
|
-
|
|
552
|
+
// 使用可选变量避免 TS 将闭包中的自引用判定为“赋值前使用”;
|
|
553
|
+
// Promise 创建后才会执行异步主体,因此运行时仍能安全比较身份。
|
|
554
|
+
let recovery;
|
|
555
|
+
recovery = (async () => {
|
|
556
|
+
const environment = await this.resolveEnvironment(sessionId);
|
|
335
557
|
if (environment.workspaceId !== undefined)
|
|
336
558
|
this.workspaceIds.set(sessionId, environment.workspaceId);
|
|
337
|
-
const result = unwrap(await resolveRemote(this.remote).list(sessionId));
|
|
559
|
+
const result = dedupeInventory(unwrap(await resolveRemote(this.remote).list(sessionId)));
|
|
560
|
+
const currentRequestId = this.inventoryRequestIds.get(sessionId);
|
|
561
|
+
if (currentRequestId !== requestId || this.inventoryEpoch !== inventoryEpoch) {
|
|
562
|
+
// 工作区尚未解析时,刷新方无法提前把这个会话加入失效集合。
|
|
563
|
+
// 旧 list 结果不能交给 Sidebar,否则关闭后的终端会被重新投影出来;
|
|
564
|
+
// 让当前会话重新发起一轮查询,直到拿到刷新后的库存。
|
|
565
|
+
debugInfo('codingns4dsh: client terminal recover stale retry', {
|
|
566
|
+
sessionId,
|
|
567
|
+
terminalIds: result.map((entry) => entry.id),
|
|
568
|
+
requestId,
|
|
569
|
+
currentRequestId,
|
|
570
|
+
inventoryEpoch,
|
|
571
|
+
currentInventoryEpoch: this.inventoryEpoch,
|
|
572
|
+
});
|
|
573
|
+
// 当前请求尚未进入 finally;先移除自身,才能让 retry 创建新一代
|
|
574
|
+
// Promise,而不是再次命中这个仍在执行的旧请求。
|
|
575
|
+
if (recovery !== undefined && this.recoveries.get(sessionId) === recovery)
|
|
576
|
+
this.recoveries.delete(sessionId);
|
|
577
|
+
return this.recover(sessionId);
|
|
578
|
+
}
|
|
338
579
|
debugInfo('codingns4dsh: client terminal recover', { sessionId, workspaceId: environment.workspaceId, terminalIds: result.map((entry) => entry.id) });
|
|
580
|
+
this.rememberInventory(sessionId, result);
|
|
339
581
|
return result;
|
|
340
|
-
})().finally(() =>
|
|
582
|
+
})().finally(() => {
|
|
583
|
+
if (recovery !== undefined && this.recoveries.get(sessionId) === recovery)
|
|
584
|
+
this.recoveries.delete(sessionId);
|
|
585
|
+
});
|
|
341
586
|
this.recoveries.set(sessionId, recovery);
|
|
342
587
|
return recovery;
|
|
343
588
|
}
|
|
@@ -357,18 +602,80 @@ export class CodingNsWebTerminals extends Service {
|
|
|
357
602
|
this.views.clear();
|
|
358
603
|
this.workspaceIds.clear();
|
|
359
604
|
this.recoveries.clear();
|
|
605
|
+
this.inventories.clear();
|
|
606
|
+
this.environments.clear();
|
|
607
|
+
this.inventoryRequestIds.clear();
|
|
608
|
+
this.inventoryEpoch = 0;
|
|
609
|
+
}
|
|
610
|
+
/** 强制刷新当前会话对应工作区的库存。 */
|
|
611
|
+
async refreshInventory(sessionId) {
|
|
612
|
+
this.inventoryEpoch += 1;
|
|
613
|
+
// 聚合页创建/关闭后必须绕过同一工作区所有会话的旧 list 请求;否则
|
|
614
|
+
// 另一个会话的迟到响应仍可能把关闭后的终端写回共享库存。
|
|
615
|
+
const workspaceId = this.workspaceIds.get(sessionId);
|
|
616
|
+
const invalidated = new Set();
|
|
617
|
+
for (const [knownSessionId, knownWorkspaceId] of this.workspaceIds) {
|
|
618
|
+
if (knownSessionId === sessionId || (workspaceId !== undefined && knownWorkspaceId === workspaceId)) {
|
|
619
|
+
invalidated.add(knownSessionId);
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
invalidated.add(sessionId);
|
|
623
|
+
for (const knownSessionId of invalidated)
|
|
624
|
+
this.invalidateRecovery(knownSessionId);
|
|
625
|
+
return this.recover(sessionId);
|
|
626
|
+
}
|
|
627
|
+
/** 令指定会话尚未完成的库存请求失效,并清掉可复用的旧 Promise。 */
|
|
628
|
+
invalidateRecovery(sessionId) {
|
|
629
|
+
this.inventoryRequestIds.set(sessionId, (this.inventoryRequestIds.get(sessionId) ?? 0) + 1);
|
|
630
|
+
this.recoveries.delete(sessionId);
|
|
631
|
+
}
|
|
632
|
+
async resolveEnvironment(sessionId) {
|
|
633
|
+
const cached = this.environments.get(`session:${sessionId}`);
|
|
634
|
+
if (cached !== undefined)
|
|
635
|
+
return cached;
|
|
636
|
+
const environment = unwrap(await resolveRemote(this.remote).environment(sessionId));
|
|
637
|
+
this.environments.set(`session:${sessionId}`, environment);
|
|
638
|
+
if (environment.workspaceId !== undefined)
|
|
639
|
+
this.environments.set(`workspace:${environment.workspaceId}`, environment);
|
|
640
|
+
return environment;
|
|
641
|
+
}
|
|
642
|
+
environmentForSession(sessionId) {
|
|
643
|
+
const session = this.environments.get(`session:${sessionId}`);
|
|
644
|
+
if (session !== undefined)
|
|
645
|
+
return session;
|
|
646
|
+
const workspaceId = this.workspaceIds.get(sessionId);
|
|
647
|
+
return workspaceId === undefined ? undefined : this.environments.get(`workspace:${workspaceId}`);
|
|
360
648
|
}
|
|
361
|
-
|
|
649
|
+
/** 返回当前会话最近一次拿到的工作区库存;首次加载时为空。 */
|
|
650
|
+
inventoryForSession(sessionId) {
|
|
651
|
+
const workspaceId = this.workspaceIds.get(sessionId) ?? `session:${sessionId}`;
|
|
652
|
+
return this.inventories.get(workspaceId)?.terminals ?? [];
|
|
653
|
+
}
|
|
654
|
+
rememberWorkspace(sessionId, contentId, id, workspaceId, useWorkspaceBinding) {
|
|
362
655
|
this.workspaceIds.set(sessionId, workspaceId);
|
|
363
|
-
|
|
656
|
+
// 显式恢复和“新建”都必须保留自己的 terminalId;只有普通无参数入口
|
|
657
|
+
// 才使用旧的单终端工作区绑定兼容行为。
|
|
658
|
+
const existing = useWorkspaceBinding ? readWorkspaceBindingWithMigration(workspaceId, contentId) : undefined;
|
|
364
659
|
const resolvedId = existing ?? id;
|
|
365
660
|
// 修正首次渲染时已经写入的会话键,避免第二个会话继续携带临时 ID。
|
|
366
661
|
writeBinding(sessionId, contentId, resolvedId);
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
662
|
+
if (useWorkspaceBinding) {
|
|
663
|
+
// 旧版本的工作区键带 contentId;统一重写成只含 Workspace ID 的新键。
|
|
664
|
+
writeWorkspaceBinding(workspaceId, contentId, resolvedId);
|
|
665
|
+
deleteLegacyWorkspaceBinding(workspaceId, contentId);
|
|
666
|
+
}
|
|
370
667
|
return { id: resolvedId, existing: existing !== undefined };
|
|
371
668
|
}
|
|
669
|
+
rememberInventory(sessionId, terminals) {
|
|
670
|
+
const workspaceId = this.workspaceIds.get(sessionId) ?? `session:${sessionId}`;
|
|
671
|
+
const normalized = dedupeInventory(terminals);
|
|
672
|
+
const previous = this.inventories.get(workspaceId);
|
|
673
|
+
if (previous !== undefined && sameInventory(previous.terminals, normalized))
|
|
674
|
+
return;
|
|
675
|
+
const revision = this.inventoryRevisionStore.getSnapshot() + 1;
|
|
676
|
+
this.inventories.set(workspaceId, { workspaceId, terminals: normalized, revision });
|
|
677
|
+
this.inventoryRevisionStore.set(revision);
|
|
678
|
+
}
|
|
372
679
|
async cleanup(request, view) {
|
|
373
680
|
try {
|
|
374
681
|
debugInfo('codingns4dsh: client terminal cleanup begin', { sessionId: request.sessionId, terminalId: request.id, hasView: view !== undefined });
|
|
@@ -380,6 +687,15 @@ export class CodingNsWebTerminals extends Service {
|
|
|
380
687
|
this.closeRequests.delete(String(request.id));
|
|
381
688
|
persistCloseRequests(this.closeRequests.values());
|
|
382
689
|
this.closeFailureStore.set(this.closeFailureStore.getSnapshot().filter((item) => item.id !== request.id));
|
|
690
|
+
// 兼容旧 Sidebar 的显式关闭路径:关闭成功后也必须刷新工作区库存,
|
|
691
|
+
// 否则其他会话会继续看到已经关闭的终端记录。
|
|
692
|
+
void this.refreshInventory(request.sessionId).catch((error) => {
|
|
693
|
+
debugWarn('codingns4dsh: client terminal inventory refresh after close failed', {
|
|
694
|
+
sessionId: request.sessionId,
|
|
695
|
+
terminalId: request.id,
|
|
696
|
+
error: errorMessage(error),
|
|
697
|
+
});
|
|
698
|
+
});
|
|
383
699
|
}
|
|
384
700
|
catch (error) {
|
|
385
701
|
if (isTerminalRemoteUnavailable(error)) {
|
|
@@ -448,6 +764,24 @@ function errorMessage(error) {
|
|
|
448
764
|
function isTerminalRemoteUnavailable(error) {
|
|
449
765
|
return error instanceof TerminalRemoteUnavailableError;
|
|
450
766
|
}
|
|
767
|
+
function sameInventory(left, right) {
|
|
768
|
+
if (left.length !== right.length)
|
|
769
|
+
return false;
|
|
770
|
+
for (let index = 0; index < left.length; index += 1) {
|
|
771
|
+
const a = left[index];
|
|
772
|
+
const b = right[index];
|
|
773
|
+
if (a.id !== b.id || a.title !== b.title || a.cwd !== b.cwd || a.cols !== b.cols || a.rows !== b.rows || a.state !== b.state || a.exitCode !== b.exitCode || a.shell.path !== b.shell.path || a.shell.name !== b.shell.name)
|
|
774
|
+
return false;
|
|
775
|
+
}
|
|
776
|
+
return true;
|
|
777
|
+
}
|
|
778
|
+
/** Host 列表按 terminalId 去重,避免重复记录共享同一个关闭动作。 */
|
|
779
|
+
function dedupeInventory(terminals) {
|
|
780
|
+
const unique = new Map();
|
|
781
|
+
for (const terminal of terminals)
|
|
782
|
+
unique.set(terminal.id, terminal);
|
|
783
|
+
return [...unique.values()];
|
|
784
|
+
}
|
|
451
785
|
function bindingKey(sessionId, contentId) {
|
|
452
786
|
return `${BINDING_PREFIX}${JSON.stringify([sessionId, contentId])}`;
|
|
453
787
|
}
|
|
@@ -487,6 +821,11 @@ function deleteWorkspaceBinding(workspaceId, _contentId) {
|
|
|
487
821
|
}
|
|
488
822
|
catch { /* 浏览器禁用存储时仅失去跨刷新绑定。 */ }
|
|
489
823
|
}
|
|
824
|
+
function deleteWorkspaceBindingIfMatches(workspaceId, id) {
|
|
825
|
+
if (readWorkspaceBinding(workspaceId) !== id)
|
|
826
|
+
return;
|
|
827
|
+
deleteWorkspaceBinding(workspaceId);
|
|
828
|
+
}
|
|
490
829
|
function deleteLegacyWorkspaceBinding(workspaceId, contentId) {
|
|
491
830
|
try {
|
|
492
831
|
localStorage.removeItem(legacyWorkspaceBindingKey(workspaceId, contentId));
|