@google/gemini-cli-core 0.34.0-preview.3 → 0.35.0-nightly.20260314.3038fdce2
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/dist/docs/changelogs/index.md +19 -0
- package/dist/docs/changelogs/latest.md +217 -191
- package/dist/docs/changelogs/preview.md +447 -184
- package/dist/docs/cli/model-routing.md +18 -1
- package/dist/docs/cli/model-steering.md +79 -0
- package/dist/docs/cli/plan-mode.md +99 -14
- package/dist/docs/cli/settings.md +14 -12
- package/dist/docs/cli/telemetry.md +45 -0
- package/dist/docs/cli/tutorials/plan-mode-steering.md +89 -0
- package/dist/docs/core/index.md +2 -0
- package/dist/docs/core/local-model-routing.md +193 -0
- package/dist/docs/reference/configuration.md +27 -3
- package/dist/docs/reference/keyboard-shortcuts.md +168 -90
- package/dist/docs/reference/policy-engine.md +30 -6
- package/dist/docs/resources/troubleshooting.md +15 -0
- package/dist/docs/sidebar.json +10 -0
- package/dist/docs/tools/shell.md +8 -0
- package/dist/google-gemini-cli-core-0.35.0-nightly.20260311.657f19c1f.tgz +0 -0
- package/dist/src/agents/a2a-client-manager.d.ts +11 -4
- package/dist/src/agents/a2a-client-manager.js +61 -34
- package/dist/src/agents/a2a-client-manager.js.map +1 -1
- package/dist/src/agents/a2a-client-manager.test.js +178 -115
- package/dist/src/agents/a2a-client-manager.test.js.map +1 -1
- package/dist/src/agents/a2aUtils.d.ts +4 -28
- package/dist/src/agents/a2aUtils.js +20 -181
- package/dist/src/agents/a2aUtils.js.map +1 -1
- package/dist/src/agents/a2aUtils.test.js +19 -106
- package/dist/src/agents/a2aUtils.test.js.map +1 -1
- package/dist/src/agents/agent-scheduler.d.ts +2 -0
- package/dist/src/agents/agent-scheduler.js +3 -2
- package/dist/src/agents/agent-scheduler.js.map +1 -1
- package/dist/src/agents/agent-scheduler.test.js +12 -3
- package/dist/src/agents/agent-scheduler.test.js.map +1 -1
- package/dist/src/agents/agentLoader.d.ts +2 -2
- package/dist/src/agents/agentLoader.js +15 -0
- package/dist/src/agents/agentLoader.js.map +1 -1
- package/dist/src/agents/auth-provider/factory.d.ts +2 -0
- package/dist/src/agents/auth-provider/factory.js +6 -3
- package/dist/src/agents/auth-provider/factory.js.map +1 -1
- package/dist/src/agents/auth-provider/google-credentials-provider.d.ts +26 -0
- package/dist/src/agents/auth-provider/google-credentials-provider.js +117 -0
- package/dist/src/agents/auth-provider/google-credentials-provider.js.map +1 -0
- package/dist/src/agents/auth-provider/google-credentials-provider.test.d.ts +6 -0
- package/dist/src/agents/auth-provider/google-credentials-provider.test.js +126 -0
- package/dist/src/agents/auth-provider/google-credentials-provider.test.js.map +1 -0
- package/dist/src/agents/browser/browserAgentFactory.js +11 -2
- package/dist/src/agents/browser/browserAgentFactory.js.map +1 -1
- package/dist/src/agents/browser/browserAgentInvocation.d.ts +4 -3
- package/dist/src/agents/browser/browserAgentInvocation.js +10 -4
- package/dist/src/agents/browser/browserAgentInvocation.js.map +1 -1
- package/dist/src/agents/browser/browserAgentInvocation.test.js +1 -0
- package/dist/src/agents/browser/browserAgentInvocation.test.js.map +1 -1
- package/dist/src/agents/browser/browserManager.d.ts +10 -0
- package/dist/src/agents/browser/browserManager.js +53 -8
- package/dist/src/agents/browser/browserManager.js.map +1 -1
- package/dist/src/agents/browser/inputBlocker.d.ts +51 -0
- package/dist/src/agents/browser/inputBlocker.js +234 -0
- package/dist/src/agents/browser/inputBlocker.js.map +1 -0
- package/dist/src/agents/browser/inputBlocker.test.d.ts +6 -0
- package/dist/src/agents/browser/inputBlocker.test.js +82 -0
- package/dist/src/agents/browser/inputBlocker.test.js.map +1 -0
- package/dist/src/agents/browser/mcpToolWrapper.d.ts +3 -2
- package/dist/src/agents/browser/mcpToolWrapper.js +49 -8
- package/dist/src/agents/browser/mcpToolWrapper.js.map +1 -1
- package/dist/src/agents/browser/mcpToolWrapper.test.js +51 -0
- package/dist/src/agents/browser/mcpToolWrapper.test.js.map +1 -1
- package/dist/src/agents/local-executor.d.ts +5 -4
- package/dist/src/agents/local-executor.js +30 -25
- package/dist/src/agents/local-executor.js.map +1 -1
- package/dist/src/agents/local-executor.test.js +269 -3
- package/dist/src/agents/local-executor.test.js.map +1 -1
- package/dist/src/agents/local-invocation.d.ts +4 -4
- package/dist/src/agents/local-invocation.js +12 -9
- package/dist/src/agents/local-invocation.js.map +1 -1
- package/dist/src/agents/local-invocation.test.js +19 -3
- package/dist/src/agents/local-invocation.test.js.map +1 -1
- package/dist/src/agents/registry.js +3 -2
- package/dist/src/agents/registry.js.map +1 -1
- package/dist/src/agents/registry.test.js +1 -0
- package/dist/src/agents/registry.test.js.map +1 -1
- package/dist/src/agents/remote-invocation.d.ts +0 -9
- package/dist/src/agents/remote-invocation.js +1 -28
- package/dist/src/agents/remote-invocation.js.map +1 -1
- package/dist/src/agents/remote-invocation.test.js +1 -0
- package/dist/src/agents/remote-invocation.test.js.map +1 -1
- package/dist/src/agents/subagent-tool-wrapper.d.ts +5 -4
- package/dist/src/agents/subagent-tool-wrapper.js +9 -5
- package/dist/src/agents/subagent-tool-wrapper.js.map +1 -1
- package/dist/src/agents/subagent-tool-wrapper.test.js +15 -1
- package/dist/src/agents/subagent-tool-wrapper.test.js.map +1 -1
- package/dist/src/agents/subagent-tool.d.ts +3 -3
- package/dist/src/agents/subagent-tool.js +16 -12
- package/dist/src/agents/subagent-tool.js.map +1 -1
- package/dist/src/agents/subagent-tool.test.js +13 -3
- package/dist/src/agents/subagent-tool.test.js.map +1 -1
- package/dist/src/code_assist/oauth2.js +0 -1
- package/dist/src/code_assist/oauth2.js.map +1 -1
- package/dist/src/code_assist/server.js +2 -2
- package/dist/src/code_assist/server.js.map +1 -1
- package/dist/src/code_assist/server.test.js +5 -1
- package/dist/src/code_assist/server.test.js.map +1 -1
- package/dist/src/code_assist/telemetry.d.ts +2 -2
- package/dist/src/code_assist/telemetry.js +5 -3
- package/dist/src/code_assist/telemetry.js.map +1 -1
- package/dist/src/code_assist/telemetry.test.js +14 -12
- package/dist/src/code_assist/telemetry.test.js.map +1 -1
- package/dist/src/code_assist/types.d.ts +14 -12
- package/dist/src/code_assist/types.js.map +1 -1
- package/dist/src/config/agent-loop-context.d.ts +3 -0
- package/dist/src/config/config.d.ts +75 -2
- package/dist/src/config/config.js +78 -11
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/config.test.js +170 -10
- package/dist/src/config/config.test.js.map +1 -1
- package/dist/src/config/models.d.ts +14 -0
- package/dist/src/config/models.js +37 -0
- package/dist/src/config/models.js.map +1 -1
- package/dist/src/config/models.test.js +51 -1
- package/dist/src/config/models.test.js.map +1 -1
- package/dist/src/config/storage.d.ts +1 -0
- package/dist/src/config/storage.js +3 -0
- package/dist/src/config/storage.js.map +1 -1
- package/dist/src/core/baseLlmClient.js +19 -3
- package/dist/src/core/baseLlmClient.js.map +1 -1
- package/dist/src/core/baseLlmClient.test.js +2 -0
- package/dist/src/core/baseLlmClient.test.js.map +1 -1
- package/dist/src/core/client.d.ts +4 -3
- package/dist/src/core/client.js +42 -17
- package/dist/src/core/client.js.map +1 -1
- package/dist/src/core/client.test.js +8 -0
- package/dist/src/core/client.test.js.map +1 -1
- package/dist/src/core/contentGenerator.d.ts +1 -0
- package/dist/src/core/contentGenerator.js +35 -3
- package/dist/src/core/contentGenerator.js.map +1 -1
- package/dist/src/core/contentGenerator.test.js +137 -2
- package/dist/src/core/contentGenerator.test.js.map +1 -1
- package/dist/src/core/coreToolHookTriggers.d.ts +2 -3
- package/dist/src/core/coreToolHookTriggers.js +5 -11
- package/dist/src/core/coreToolHookTriggers.js.map +1 -1
- package/dist/src/core/coreToolHookTriggers.test.js +24 -0
- package/dist/src/core/coreToolHookTriggers.test.js.map +1 -1
- package/dist/src/core/coreToolScheduler.js +1 -1
- package/dist/src/core/coreToolScheduler.js.map +1 -1
- package/dist/src/core/geminiChat.js +31 -21
- package/dist/src/core/geminiChat.js.map +1 -1
- package/dist/src/core/geminiChat.test.js +6 -4
- package/dist/src/core/geminiChat.test.js.map +1 -1
- package/dist/src/core/geminiChat_network_retry.test.js +21 -11
- package/dist/src/core/geminiChat_network_retry.test.js.map +1 -1
- package/dist/src/fallback/handler.js +0 -4
- package/dist/src/fallback/handler.js.map +1 -1
- package/dist/src/fallback/handler.test.js +0 -6
- package/dist/src/fallback/handler.test.js.map +1 -1
- package/dist/src/generated/git-commit.d.ts +2 -2
- package/dist/src/generated/git-commit.js +2 -2
- package/dist/src/generated/git-commit.js.map +1 -1
- package/dist/src/ide/detect-ide.d.ts +8 -0
- package/dist/src/ide/detect-ide.js +12 -1
- package/dist/src/ide/detect-ide.js.map +1 -1
- package/dist/src/ide/detect-ide.test.js +12 -0
- package/dist/src/ide/detect-ide.test.js.map +1 -1
- package/dist/src/ide/ide-installer.js +20 -8
- package/dist/src/ide/ide-installer.js.map +1 -1
- package/dist/src/ide/ide-installer.test.js +60 -4
- package/dist/src/ide/ide-installer.test.js.map +1 -1
- package/dist/src/mcp/oauth-provider.js +0 -2
- package/dist/src/mcp/oauth-provider.js.map +1 -1
- package/dist/src/mcp/oauth-token-storage.test.js +6 -10
- package/dist/src/mcp/oauth-token-storage.test.js.map +1 -1
- package/dist/src/mcp/oauth-utils.js +0 -2
- package/dist/src/mcp/oauth-utils.js.map +1 -1
- package/dist/src/mcp/token-storage/file-token-storage.d.ts +24 -0
- package/dist/src/mcp/token-storage/file-token-storage.js +152 -0
- package/dist/src/mcp/token-storage/file-token-storage.js.map +1 -0
- package/dist/src/mcp/token-storage/file-token-storage.test.d.ts +6 -0
- package/dist/src/mcp/token-storage/file-token-storage.test.js +276 -0
- package/dist/src/mcp/token-storage/file-token-storage.test.js.map +1 -0
- package/dist/src/mcp/token-storage/hybrid-token-storage.js +21 -9
- package/dist/src/mcp/token-storage/hybrid-token-storage.js.map +1 -1
- package/dist/src/mcp/token-storage/hybrid-token-storage.test.js +59 -10
- package/dist/src/mcp/token-storage/hybrid-token-storage.test.js.map +1 -1
- package/dist/src/mcp/token-storage/index.d.ts +1 -1
- package/dist/src/mcp/token-storage/index.js +1 -1
- package/dist/src/mcp/token-storage/index.js.map +1 -1
- package/dist/src/mcp/token-storage/keychain-token-storage.d.ts +0 -1
- package/dist/src/mcp/token-storage/keychain-token-storage.js +0 -3
- package/dist/src/mcp/token-storage/keychain-token-storage.js.map +1 -1
- package/dist/src/policy/config.d.ts +14 -2
- package/dist/src/policy/config.js +94 -49
- package/dist/src/policy/config.js.map +1 -1
- package/dist/src/policy/config.test.js +208 -488
- package/dist/src/policy/config.test.js.map +1 -1
- package/dist/src/policy/policies/plan.toml +1 -1
- package/dist/src/policy/stable-stringify.js +15 -5
- package/dist/src/policy/stable-stringify.js.map +1 -1
- package/dist/src/policy/types.d.ts +1 -0
- package/dist/src/policy/types.js.map +1 -1
- package/dist/src/policy/utils.d.ts +17 -0
- package/dist/src/policy/utils.js +27 -7
- package/dist/src/policy/utils.js.map +1 -1
- package/dist/src/prompts/snippets.js +2 -1
- package/dist/src/prompts/snippets.js.map +1 -1
- package/dist/src/prompts/utils.test.d.ts +6 -0
- package/dist/src/prompts/utils.test.js +225 -0
- package/dist/src/prompts/utils.test.js.map +1 -0
- package/dist/src/routing/modelRouterService.js +2 -2
- package/dist/src/routing/modelRouterService.js.map +1 -1
- package/dist/src/routing/modelRouterService.test.js +4 -3
- package/dist/src/routing/modelRouterService.test.js.map +1 -1
- package/dist/src/routing/strategies/numericalClassifierStrategy.js +6 -40
- package/dist/src/routing/strategies/numericalClassifierStrategy.js.map +1 -1
- package/dist/src/routing/strategies/numericalClassifierStrategy.test.js +29 -64
- package/dist/src/routing/strategies/numericalClassifierStrategy.test.js.map +1 -1
- package/dist/src/scheduler/policy.d.ts +3 -7
- package/dist/src/scheduler/policy.js +4 -3
- package/dist/src/scheduler/policy.js.map +1 -1
- package/dist/src/scheduler/policy.test.js +51 -31
- package/dist/src/scheduler/policy.test.js.map +1 -1
- package/dist/src/scheduler/scheduler.d.ts +4 -2
- package/dist/src/scheduler/scheduler.js +19 -16
- package/dist/src/scheduler/scheduler.js.map +1 -1
- package/dist/src/scheduler/scheduler.test.js +33 -10
- package/dist/src/scheduler/scheduler.test.js.map +1 -1
- package/dist/src/scheduler/scheduler_parallel.test.js +40 -2
- package/dist/src/scheduler/scheduler_parallel.test.js.map +1 -1
- package/dist/src/scheduler/scheduler_waiting_callback.test.js +1 -1
- package/dist/src/scheduler/scheduler_waiting_callback.test.js.map +1 -1
- package/dist/src/scheduler/tool-executor.d.ts +3 -3
- package/dist/src/scheduler/tool-executor.js +15 -21
- package/dist/src/scheduler/tool-executor.js.map +1 -1
- package/dist/src/scheduler/tool-executor.test.js +43 -8
- package/dist/src/scheduler/tool-executor.test.js.map +1 -1
- package/dist/src/services/chatRecordingService.js +2 -1
- package/dist/src/services/chatRecordingService.js.map +1 -1
- package/dist/src/services/chatRecordingService.test.js +21 -0
- package/dist/src/services/chatRecordingService.test.js.map +1 -1
- package/dist/src/services/executionLifecycleService.d.ts +83 -0
- package/dist/src/services/executionLifecycleService.js +271 -0
- package/dist/src/services/executionLifecycleService.js.map +1 -0
- package/dist/src/services/executionLifecycleService.test.d.ts +6 -0
- package/dist/src/services/executionLifecycleService.test.js +227 -0
- package/dist/src/services/executionLifecycleService.test.js.map +1 -0
- package/dist/src/services/keychainService.d.ts +0 -5
- package/dist/src/services/keychainService.js +13 -30
- package/dist/src/services/keychainService.js.map +1 -1
- package/dist/src/services/keychainService.test.js +22 -52
- package/dist/src/services/keychainService.test.js.map +1 -1
- package/dist/src/services/sandboxManager.d.ts +54 -0
- package/dist/src/services/sandboxManager.js +30 -0
- package/dist/src/services/sandboxManager.js.map +1 -0
- package/dist/src/services/sandboxManager.test.d.ts +6 -0
- package/dist/src/services/sandboxManager.test.js +95 -0
- package/dist/src/services/sandboxManager.test.js.map +1 -0
- package/dist/src/services/shellExecutionService.d.ts +4 -52
- package/dist/src/services/shellExecutionService.js +437 -498
- package/dist/src/services/shellExecutionService.js.map +1 -1
- package/dist/src/services/shellExecutionService.test.js +10 -21
- package/dist/src/services/shellExecutionService.test.js.map +1 -1
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.d.ts +13 -2
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.js +91 -1
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.js.map +1 -1
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js +77 -0
- package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js.map +1 -1
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.d.ts +10 -1
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.js +25 -1
- package/dist/src/telemetry/clearcut-logger/event-metadata-key.js.map +1 -1
- package/dist/src/telemetry/index.d.ts +3 -3
- package/dist/src/telemetry/index.js +3 -3
- package/dist/src/telemetry/index.js.map +1 -1
- package/dist/src/telemetry/loggers.d.ts +2 -1
- package/dist/src/telemetry/loggers.js +32 -1
- package/dist/src/telemetry/loggers.js.map +1 -1
- package/dist/src/telemetry/loggers.test.js +37 -2
- package/dist/src/telemetry/loggers.test.js.map +1 -1
- package/dist/src/telemetry/metrics.d.ts +13 -0
- package/dist/src/telemetry/metrics.js +17 -0
- package/dist/src/telemetry/metrics.js.map +1 -1
- package/dist/src/telemetry/startupProfiler.js +4 -4
- package/dist/src/telemetry/startupProfiler.js.map +1 -1
- package/dist/src/telemetry/startupProfiler.test.js +19 -0
- package/dist/src/telemetry/startupProfiler.test.js.map +1 -1
- package/dist/src/telemetry/telemetry-utils.test.js +3 -3
- package/dist/src/telemetry/types.d.ts +16 -2
- package/dist/src/telemetry/types.js +34 -0
- package/dist/src/telemetry/types.js.map +1 -1
- package/dist/src/telemetry/uiTelemetry.d.ts +7 -0
- package/dist/src/telemetry/uiTelemetry.js +78 -0
- package/dist/src/telemetry/uiTelemetry.js.map +1 -1
- package/dist/src/telemetry/uiTelemetry.test.js +101 -0
- package/dist/src/telemetry/uiTelemetry.test.js.map +1 -1
- package/dist/src/tools/exit-plan-mode.js +10 -2
- package/dist/src/tools/exit-plan-mode.js.map +1 -1
- package/dist/src/tools/exit-plan-mode.test.js +15 -0
- package/dist/src/tools/exit-plan-mode.test.js.map +1 -1
- package/dist/src/tools/ls.js +2 -2
- package/dist/src/tools/ls.js.map +1 -1
- package/dist/src/tools/mcp-client.js +0 -1
- package/dist/src/tools/mcp-client.js.map +1 -1
- package/dist/src/tools/mcp-client.test.js +4 -0
- package/dist/src/tools/mcp-client.test.js.map +1 -1
- package/dist/src/tools/mcp-tool.test.js +10 -1
- package/dist/src/tools/mcp-tool.test.js.map +1 -1
- package/dist/src/tools/read-many-files.js +2 -4
- package/dist/src/tools/read-many-files.js.map +1 -1
- package/dist/src/tools/shell.d.ts +1 -1
- package/dist/src/tools/shell.js +3 -3
- package/dist/src/tools/shell.js.map +1 -1
- package/dist/src/tools/tool-registry.test.js +4 -0
- package/dist/src/tools/tool-registry.test.js.map +1 -1
- package/dist/src/tools/tools.d.ts +34 -1
- package/dist/src/tools/tools.js +47 -1
- package/dist/src/tools/tools.js.map +1 -1
- package/dist/src/tools/web-fetch.d.ts +5 -0
- package/dist/src/tools/web-fetch.js +177 -100
- package/dist/src/tools/web-fetch.js.map +1 -1
- package/dist/src/tools/web-fetch.test.js +130 -26
- package/dist/src/tools/web-fetch.test.js.map +1 -1
- package/dist/src/tools/web-search.test.js +2 -0
- package/dist/src/tools/web-search.test.js.map +1 -1
- package/dist/src/utils/fetch.d.ts +0 -29
- package/dist/src/utils/fetch.js +10 -123
- package/dist/src/utils/fetch.js.map +1 -1
- package/dist/src/utils/fetch.test.js +1 -103
- package/dist/src/utils/fetch.test.js.map +1 -1
- package/dist/src/utils/language-detection.js +96 -89
- package/dist/src/utils/language-detection.js.map +1 -1
- package/dist/src/utils/language-detection.test.d.ts +6 -0
- package/dist/src/utils/language-detection.test.js +39 -0
- package/dist/src/utils/language-detection.test.js.map +1 -0
- package/dist/src/utils/oauth-flow.js +0 -2
- package/dist/src/utils/oauth-flow.js.map +1 -1
- package/dist/src/utils/paths.js +5 -2
- package/dist/src/utils/paths.js.map +1 -1
- package/dist/src/utils/paths.test.js +11 -0
- package/dist/src/utils/paths.test.js.map +1 -1
- package/dist/src/utils/process-utils.js +18 -4
- package/dist/src/utils/process-utils.js.map +1 -1
- package/dist/src/utils/process-utils.test.js +32 -10
- package/dist/src/utils/process-utils.test.js.map +1 -1
- package/dist/src/utils/retry.d.ts +7 -0
- package/dist/src/utils/retry.js +40 -3
- package/dist/src/utils/retry.js.map +1 -1
- package/dist/src/utils/retry.test.js +13 -0
- package/dist/src/utils/retry.test.js.map +1 -1
- package/dist/src/utils/summarizer.test.js +5 -0
- package/dist/src/utils/summarizer.test.js.map +1 -1
- package/dist/src/utils/surface.d.ts +18 -0
- package/dist/src/utils/surface.js +46 -0
- package/dist/src/utils/surface.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/dist/google-gemini-cli-core-0.34.0-preview.2.tgz +0 -0
- package/dist/src/services/fileKeychain.d.ts +0 -24
- package/dist/src/services/fileKeychain.js +0 -123
- package/dist/src/services/fileKeychain.js.map +0 -1
|
@@ -18,7 +18,9 @@ import { debugLogger } from '../utils/debugLogger.js';
|
|
|
18
18
|
import { Storage } from '../config/storage.js';
|
|
19
19
|
import { serializeTerminalToObject, } from '../utils/terminalSerializer.js';
|
|
20
20
|
import { sanitizeEnvironment, } from './environmentSanitization.js';
|
|
21
|
+
import { NoopSandboxManager } from './sandboxManager.js';
|
|
21
22
|
import { killProcessGroup } from '../utils/process-utils.js';
|
|
23
|
+
import { ExecutionLifecycleService, } from './executionLifecycleService.js';
|
|
22
24
|
const { Terminal } = pkg;
|
|
23
25
|
const MAX_CHILD_PROCESS_BUFFER_SIZE = 16 * 1024 * 1024; // 16MB
|
|
24
26
|
/**
|
|
@@ -135,7 +137,6 @@ export class ShellExecutionService {
|
|
|
135
137
|
static activeChildProcesses = new Map();
|
|
136
138
|
static backgroundLogPids = new Set();
|
|
137
139
|
static backgroundLogStreams = new Map();
|
|
138
|
-
static exitedPtyInfo = new Map();
|
|
139
140
|
static getLogDir() {
|
|
140
141
|
return path.join(Storage.getGlobalTempDir(), 'background-processes');
|
|
141
142
|
}
|
|
@@ -161,8 +162,6 @@ export class ShellExecutionService {
|
|
|
161
162
|
}
|
|
162
163
|
this.backgroundLogPids.delete(pid);
|
|
163
164
|
}
|
|
164
|
-
static activeResolvers = new Map();
|
|
165
|
-
static activeListeners = new Map();
|
|
166
165
|
/**
|
|
167
166
|
* Executes a shell command using `node-pty`, capturing all output and lifecycle events.
|
|
168
167
|
*
|
|
@@ -174,11 +173,19 @@ export class ShellExecutionService {
|
|
|
174
173
|
* resolves with the complete execution result.
|
|
175
174
|
*/
|
|
176
175
|
static async execute(commandToExecute, cwd, onOutputEvent, abortSignal, shouldUseNodePty, shellExecutionConfig) {
|
|
176
|
+
const sandboxManager = new NoopSandboxManager();
|
|
177
|
+
const { env: sanitizedEnv } = await sandboxManager.prepareCommand({
|
|
178
|
+
command: commandToExecute,
|
|
179
|
+
args: [],
|
|
180
|
+
env: process.env,
|
|
181
|
+
cwd,
|
|
182
|
+
config: shellExecutionConfig,
|
|
183
|
+
});
|
|
177
184
|
if (shouldUseNodePty) {
|
|
178
185
|
const ptyInfo = await getPty();
|
|
179
186
|
if (ptyInfo) {
|
|
180
187
|
try {
|
|
181
|
-
return await this.executeWithPty(commandToExecute, cwd, onOutputEvent, abortSignal, shellExecutionConfig, ptyInfo);
|
|
188
|
+
return await this.executeWithPty(commandToExecute, cwd, onOutputEvent, abortSignal, shellExecutionConfig, ptyInfo, sanitizedEnv);
|
|
182
189
|
}
|
|
183
190
|
catch (_e) {
|
|
184
191
|
// Fallback to child_process
|
|
@@ -209,12 +216,6 @@ export class ShellExecutionService {
|
|
|
209
216
|
const truncatedBuffer = currentBuffer.substring(charsToTrim);
|
|
210
217
|
return { newBuffer: truncatedBuffer + chunk, truncated: true };
|
|
211
218
|
}
|
|
212
|
-
static emitEvent(pid, event) {
|
|
213
|
-
const listeners = this.activeListeners.get(pid);
|
|
214
|
-
if (listeners) {
|
|
215
|
-
listeners.forEach((listener) => listener(event));
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
219
|
static childProcessFallback(commandToExecute, cwd, onOutputEvent, abortSignal, sanitizationConfig, isInteractive) {
|
|
219
220
|
try {
|
|
220
221
|
const isWindows = os.platform() === 'win32';
|
|
@@ -276,169 +277,202 @@ export class ShellExecutionService {
|
|
|
276
277
|
state,
|
|
277
278
|
});
|
|
278
279
|
}
|
|
279
|
-
const
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
280
|
+
const lifecycleHandle = child.pid
|
|
281
|
+
? ExecutionLifecycleService.attachExecution(child.pid, {
|
|
282
|
+
executionMethod: 'child_process',
|
|
283
|
+
getBackgroundOutput: () => state.output,
|
|
284
|
+
getSubscriptionSnapshot: () => state.output || undefined,
|
|
285
|
+
writeInput: (input) => {
|
|
286
|
+
const stdin = child.stdin;
|
|
287
|
+
if (stdin) {
|
|
288
|
+
stdin.write(input);
|
|
289
|
+
}
|
|
290
|
+
},
|
|
291
|
+
kill: () => {
|
|
292
|
+
if (child.pid) {
|
|
293
|
+
killProcessGroup({ pid: child.pid }).catch(() => { });
|
|
294
|
+
this.activeChildProcesses.delete(child.pid);
|
|
295
|
+
}
|
|
296
|
+
},
|
|
297
|
+
isActive: () => {
|
|
298
|
+
if (!child.pid) {
|
|
299
|
+
return false;
|
|
300
|
+
}
|
|
293
301
|
try {
|
|
294
|
-
|
|
295
|
-
stderrDecoder = new TextDecoder(encoding);
|
|
302
|
+
return process.kill(child.pid, 0);
|
|
296
303
|
}
|
|
297
304
|
catch {
|
|
298
|
-
|
|
299
|
-
stderrDecoder = new TextDecoder('utf-8');
|
|
305
|
+
return false;
|
|
300
306
|
}
|
|
307
|
+
},
|
|
308
|
+
})
|
|
309
|
+
: undefined;
|
|
310
|
+
let resolveWithoutPid;
|
|
311
|
+
const result = lifecycleHandle?.result ??
|
|
312
|
+
new Promise((resolve) => {
|
|
313
|
+
resolveWithoutPid = resolve;
|
|
314
|
+
});
|
|
315
|
+
let stdoutDecoder = null;
|
|
316
|
+
let stderrDecoder = null;
|
|
317
|
+
let error = null;
|
|
318
|
+
let exited = false;
|
|
319
|
+
let isStreamingRawContent = true;
|
|
320
|
+
const MAX_SNIFF_SIZE = 4096;
|
|
321
|
+
let sniffedBytes = 0;
|
|
322
|
+
const handleOutput = (data, stream) => {
|
|
323
|
+
if (!stdoutDecoder || !stderrDecoder) {
|
|
324
|
+
const encoding = getCachedEncodingForBuffer(data);
|
|
325
|
+
try {
|
|
326
|
+
stdoutDecoder = new TextDecoder(encoding);
|
|
327
|
+
stderrDecoder = new TextDecoder(encoding);
|
|
301
328
|
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
sniffedBytes = sniffBuffer.length;
|
|
306
|
-
if (isBinary(sniffBuffer)) {
|
|
307
|
-
isStreamingRawContent = false;
|
|
308
|
-
const event = { type: 'binary_detected' };
|
|
309
|
-
onOutputEvent(event);
|
|
310
|
-
if (child.pid)
|
|
311
|
-
ShellExecutionService.emitEvent(child.pid, event);
|
|
312
|
-
}
|
|
329
|
+
catch {
|
|
330
|
+
stdoutDecoder = new TextDecoder('utf-8');
|
|
331
|
+
stderrDecoder = new TextDecoder('utf-8');
|
|
313
332
|
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
chunk: decodedChunk,
|
|
326
|
-
};
|
|
327
|
-
onOutputEvent(event);
|
|
328
|
-
if (child.pid) {
|
|
329
|
-
ShellExecutionService.emitEvent(child.pid, event);
|
|
330
|
-
if (ShellExecutionService.backgroundLogPids.has(child.pid)) {
|
|
331
|
-
ShellExecutionService.syncBackgroundLog(child.pid, decodedChunk);
|
|
332
|
-
}
|
|
333
|
-
}
|
|
333
|
+
}
|
|
334
|
+
state.outputChunks.push(data);
|
|
335
|
+
if (isStreamingRawContent && sniffedBytes < MAX_SNIFF_SIZE) {
|
|
336
|
+
const sniffBuffer = Buffer.concat(state.outputChunks.slice(0, 20));
|
|
337
|
+
sniffedBytes = sniffBuffer.length;
|
|
338
|
+
if (isBinary(sniffBuffer)) {
|
|
339
|
+
isStreamingRawContent = false;
|
|
340
|
+
const event = { type: 'binary_detected' };
|
|
341
|
+
onOutputEvent(event);
|
|
342
|
+
if (child.pid) {
|
|
343
|
+
ExecutionLifecycleService.emitEvent(child.pid, event);
|
|
334
344
|
}
|
|
335
345
|
}
|
|
336
|
-
|
|
337
|
-
|
|
346
|
+
}
|
|
347
|
+
if (isStreamingRawContent) {
|
|
348
|
+
const decoder = stream === 'stdout' ? stdoutDecoder : stderrDecoder;
|
|
349
|
+
const decodedChunk = decoder.decode(data, { stream: true });
|
|
350
|
+
const { newBuffer, truncated } = this.appendAndTruncate(state.output, decodedChunk, MAX_CHILD_PROCESS_BUFFER_SIZE);
|
|
351
|
+
state.output = newBuffer;
|
|
352
|
+
if (truncated) {
|
|
353
|
+
state.truncated = true;
|
|
354
|
+
}
|
|
355
|
+
if (decodedChunk) {
|
|
338
356
|
const event = {
|
|
339
|
-
type: '
|
|
340
|
-
|
|
357
|
+
type: 'data',
|
|
358
|
+
chunk: decodedChunk,
|
|
341
359
|
};
|
|
342
360
|
onOutputEvent(event);
|
|
343
|
-
if (child.pid)
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
let combinedOutput = state.output;
|
|
350
|
-
if (state.truncated) {
|
|
351
|
-
const truncationMessage = `\n[GEMINI_CLI_WARNING: Output truncated. The buffer is limited to ${MAX_CHILD_PROCESS_BUFFER_SIZE / (1024 * 1024)}MB.]`;
|
|
352
|
-
combinedOutput += truncationMessage;
|
|
361
|
+
if (child.pid) {
|
|
362
|
+
ExecutionLifecycleService.emitEvent(child.pid, event);
|
|
363
|
+
if (ShellExecutionService.backgroundLogPids.has(child.pid)) {
|
|
364
|
+
ShellExecutionService.syncBackgroundLog(child.pid, decodedChunk);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
353
367
|
}
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
const
|
|
368
|
+
}
|
|
369
|
+
else {
|
|
370
|
+
const totalBytes = state.outputChunks.reduce((sum, chunk) => sum + chunk.length, 0);
|
|
371
|
+
const event = {
|
|
372
|
+
type: 'binary_progress',
|
|
373
|
+
bytesReceived: totalBytes,
|
|
374
|
+
};
|
|
375
|
+
onOutputEvent(event);
|
|
357
376
|
if (child.pid) {
|
|
358
|
-
|
|
359
|
-
const event = {
|
|
360
|
-
type: 'exit',
|
|
361
|
-
exitCode,
|
|
362
|
-
signal: exitSignal,
|
|
363
|
-
};
|
|
364
|
-
onOutputEvent(event);
|
|
365
|
-
ShellExecutionService.emitEvent(pid, event);
|
|
366
|
-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
367
|
-
ShellExecutionService.cleanupLogStream(pid).then(() => {
|
|
368
|
-
this.activeChildProcesses.delete(pid);
|
|
369
|
-
this.activeResolvers.delete(pid);
|
|
370
|
-
this.activeListeners.delete(pid);
|
|
371
|
-
});
|
|
377
|
+
ExecutionLifecycleService.emitEvent(child.pid, event);
|
|
372
378
|
}
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
const handleExit = (code, signal) => {
|
|
382
|
+
const { finalBuffer } = cleanup();
|
|
383
|
+
let combinedOutput = state.output;
|
|
384
|
+
if (state.truncated) {
|
|
385
|
+
const truncationMessage = `\n[GEMINI_CLI_WARNING: Output truncated. The buffer is limited to ${MAX_CHILD_PROCESS_BUFFER_SIZE / (1024 * 1024)}MB.]`;
|
|
386
|
+
combinedOutput += truncationMessage;
|
|
387
|
+
}
|
|
388
|
+
const finalStrippedOutput = stripAnsi(combinedOutput).trim();
|
|
389
|
+
const exitCode = code;
|
|
390
|
+
const exitSignal = signal ? os.constants.signals[signal] : null;
|
|
391
|
+
const resultPayload = {
|
|
392
|
+
rawOutput: finalBuffer,
|
|
393
|
+
output: finalStrippedOutput,
|
|
394
|
+
exitCode,
|
|
395
|
+
signal: exitSignal,
|
|
396
|
+
error,
|
|
397
|
+
aborted: abortSignal.aborted,
|
|
398
|
+
pid: child.pid,
|
|
399
|
+
executionMethod: 'child_process',
|
|
400
|
+
};
|
|
401
|
+
if (child.pid) {
|
|
402
|
+
const pid = child.pid;
|
|
403
|
+
const event = {
|
|
404
|
+
type: 'exit',
|
|
376
405
|
exitCode,
|
|
377
406
|
signal: exitSignal,
|
|
378
|
-
|
|
379
|
-
|
|
407
|
+
};
|
|
408
|
+
onOutputEvent(event);
|
|
409
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
410
|
+
ShellExecutionService.cleanupLogStream(pid).then(() => {
|
|
411
|
+
ShellExecutionService.activeChildProcesses.delete(pid);
|
|
412
|
+
});
|
|
413
|
+
ExecutionLifecycleService.completeWithResult(pid, resultPayload);
|
|
414
|
+
}
|
|
415
|
+
else {
|
|
416
|
+
resolveWithoutPid?.(resultPayload);
|
|
417
|
+
}
|
|
418
|
+
};
|
|
419
|
+
child.stdout.on('data', (data) => handleOutput(data, 'stdout'));
|
|
420
|
+
child.stderr.on('data', (data) => handleOutput(data, 'stderr'));
|
|
421
|
+
child.on('error', (err) => {
|
|
422
|
+
error = err;
|
|
423
|
+
handleExit(1, null);
|
|
424
|
+
});
|
|
425
|
+
const abortHandler = async () => {
|
|
426
|
+
if (child.pid && !exited) {
|
|
427
|
+
await killProcessGroup({
|
|
380
428
|
pid: child.pid,
|
|
381
|
-
|
|
429
|
+
escalate: true,
|
|
430
|
+
isExited: () => exited,
|
|
382
431
|
});
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
exited = true;
|
|
405
|
-
abortSignal.removeEventListener('abort', abortHandler);
|
|
406
|
-
if (stdoutDecoder) {
|
|
407
|
-
const remaining = stdoutDecoder.decode();
|
|
408
|
-
if (remaining) {
|
|
409
|
-
state.output += remaining;
|
|
410
|
-
// If there's remaining output, we should technically emit it too,
|
|
411
|
-
// but it's rare to have partial utf8 chars at the very end of stream.
|
|
412
|
-
if (isStreamingRawContent && remaining) {
|
|
413
|
-
const event = {
|
|
414
|
-
type: 'data',
|
|
415
|
-
chunk: remaining,
|
|
416
|
-
};
|
|
417
|
-
onOutputEvent(event);
|
|
418
|
-
if (child.pid)
|
|
419
|
-
ShellExecutionService.emitEvent(child.pid, event);
|
|
432
|
+
}
|
|
433
|
+
};
|
|
434
|
+
abortSignal.addEventListener('abort', abortHandler, { once: true });
|
|
435
|
+
child.on('exit', (code, signal) => {
|
|
436
|
+
handleExit(code, signal);
|
|
437
|
+
});
|
|
438
|
+
function cleanup() {
|
|
439
|
+
exited = true;
|
|
440
|
+
abortSignal.removeEventListener('abort', abortHandler);
|
|
441
|
+
if (stdoutDecoder) {
|
|
442
|
+
const remaining = stdoutDecoder.decode();
|
|
443
|
+
if (remaining) {
|
|
444
|
+
state.output += remaining;
|
|
445
|
+
if (isStreamingRawContent) {
|
|
446
|
+
const event = {
|
|
447
|
+
type: 'data',
|
|
448
|
+
chunk: remaining,
|
|
449
|
+
};
|
|
450
|
+
onOutputEvent(event);
|
|
451
|
+
if (child.pid) {
|
|
452
|
+
ExecutionLifecycleService.emitEvent(child.pid, event);
|
|
420
453
|
}
|
|
421
454
|
}
|
|
422
455
|
}
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
456
|
+
}
|
|
457
|
+
if (stderrDecoder) {
|
|
458
|
+
const remaining = stderrDecoder.decode();
|
|
459
|
+
if (remaining) {
|
|
460
|
+
state.output += remaining;
|
|
461
|
+
if (isStreamingRawContent) {
|
|
462
|
+
const event = {
|
|
463
|
+
type: 'data',
|
|
464
|
+
chunk: remaining,
|
|
465
|
+
};
|
|
466
|
+
onOutputEvent(event);
|
|
467
|
+
if (child.pid) {
|
|
468
|
+
ExecutionLifecycleService.emitEvent(child.pid, event);
|
|
435
469
|
}
|
|
436
470
|
}
|
|
437
471
|
}
|
|
438
|
-
const finalBuffer = Buffer.concat(state.outputChunks);
|
|
439
|
-
return { finalBuffer };
|
|
440
472
|
}
|
|
441
|
-
|
|
473
|
+
const finalBuffer = Buffer.concat(state.outputChunks);
|
|
474
|
+
return { finalBuffer };
|
|
475
|
+
}
|
|
442
476
|
return { pid: child.pid, result };
|
|
443
477
|
}
|
|
444
478
|
catch (e) {
|
|
@@ -459,7 +493,7 @@ export class ShellExecutionService {
|
|
|
459
493
|
};
|
|
460
494
|
}
|
|
461
495
|
}
|
|
462
|
-
static async executeWithPty(commandToExecute, cwd, onOutputEvent, abortSignal, shellExecutionConfig, ptyInfo) {
|
|
496
|
+
static async executeWithPty(commandToExecute, cwd, onOutputEvent, abortSignal, shellExecutionConfig, ptyInfo, sanitizedEnv) {
|
|
463
497
|
if (!ptyInfo) {
|
|
464
498
|
// This should not happen, but as a safeguard...
|
|
465
499
|
throw new Error('PTY implementation not found');
|
|
@@ -482,7 +516,7 @@ export class ShellExecutionService {
|
|
|
482
516
|
cols,
|
|
483
517
|
rows,
|
|
484
518
|
env: {
|
|
485
|
-
...
|
|
519
|
+
...sanitizedEnv,
|
|
486
520
|
GEMINI_CLI: '1',
|
|
487
521
|
TERM: 'xterm-256color',
|
|
488
522
|
PAGER: shellExecutionConfig.pager ?? 'cat',
|
|
@@ -492,246 +526,271 @@ export class ShellExecutionService {
|
|
|
492
526
|
});
|
|
493
527
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
494
528
|
spawnedPty = ptyProcess;
|
|
495
|
-
const
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
const outputChunks = [];
|
|
514
|
-
const error = null;
|
|
515
|
-
let exited = false;
|
|
516
|
-
let isStreamingRawContent = true;
|
|
517
|
-
const MAX_SNIFF_SIZE = 4096;
|
|
518
|
-
let sniffedBytes = 0;
|
|
519
|
-
let isWriting = false;
|
|
520
|
-
let hasStartedOutput = false;
|
|
521
|
-
let renderTimeout = null;
|
|
522
|
-
const renderFn = () => {
|
|
523
|
-
renderTimeout = null;
|
|
524
|
-
if (!isStreamingRawContent) {
|
|
529
|
+
const ptyPid = Number(ptyProcess.pid);
|
|
530
|
+
const headlessTerminal = new Terminal({
|
|
531
|
+
allowProposedApi: true,
|
|
532
|
+
cols,
|
|
533
|
+
rows,
|
|
534
|
+
scrollback: shellExecutionConfig.scrollback ?? SCROLLBACK_LIMIT,
|
|
535
|
+
});
|
|
536
|
+
headlessTerminal.scrollToTop();
|
|
537
|
+
this.activePtys.set(ptyPid, {
|
|
538
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
539
|
+
ptyProcess,
|
|
540
|
+
headlessTerminal,
|
|
541
|
+
maxSerializedLines: shellExecutionConfig.maxSerializedLines,
|
|
542
|
+
});
|
|
543
|
+
const result = ExecutionLifecycleService.attachExecution(ptyPid, {
|
|
544
|
+
executionMethod: ptyInfo?.name ?? 'node-pty',
|
|
545
|
+
writeInput: (input) => {
|
|
546
|
+
if (!ExecutionLifecycleService.isActive(ptyPid)) {
|
|
525
547
|
return;
|
|
526
548
|
}
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
const startLine = Math.max(0, endLine - (shellExecutionConfig.maxSerializedLines ?? 2000));
|
|
539
|
-
let newOutput;
|
|
540
|
-
if (shellExecutionConfig.showColor) {
|
|
541
|
-
newOutput = serializeTerminalToObject(headlessTerminal, startLine, endLine);
|
|
542
|
-
}
|
|
543
|
-
else {
|
|
544
|
-
newOutput = (serializeTerminalToObject(headlessTerminal, startLine, endLine) ||
|
|
545
|
-
[]).map((line) => line.map((token) => {
|
|
546
|
-
token.fg = '';
|
|
547
|
-
token.bg = '';
|
|
548
|
-
return token;
|
|
549
|
-
}));
|
|
549
|
+
ptyProcess.write(input);
|
|
550
|
+
},
|
|
551
|
+
kill: () => {
|
|
552
|
+
killProcessGroup({
|
|
553
|
+
pid: ptyPid,
|
|
554
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
555
|
+
pty: ptyProcess,
|
|
556
|
+
}).catch(() => { });
|
|
557
|
+
try {
|
|
558
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
559
|
+
ptyProcess.destroy?.();
|
|
550
560
|
}
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
const line = newOutput[i];
|
|
554
|
-
if (line
|
|
555
|
-
.map((segment) => segment.text)
|
|
556
|
-
.join('')
|
|
557
|
-
.trim().length > 0) {
|
|
558
|
-
lastNonEmptyLine = i;
|
|
559
|
-
break;
|
|
560
|
-
}
|
|
561
|
+
catch {
|
|
562
|
+
// Ignore errors during cleanup
|
|
561
563
|
}
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
564
|
+
this.activePtys.delete(ptyPid);
|
|
565
|
+
},
|
|
566
|
+
isActive: () => {
|
|
567
|
+
try {
|
|
568
|
+
return process.kill(ptyPid, 0);
|
|
566
569
|
}
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
? newOutput
|
|
570
|
-
: trimmedOutput;
|
|
571
|
-
if (output !== finalOutput) {
|
|
572
|
-
output = finalOutput;
|
|
573
|
-
const event = {
|
|
574
|
-
type: 'data',
|
|
575
|
-
chunk: finalOutput,
|
|
576
|
-
};
|
|
577
|
-
onOutputEvent(event);
|
|
578
|
-
ShellExecutionService.emitEvent(ptyProcess.pid, event);
|
|
570
|
+
catch {
|
|
571
|
+
return false;
|
|
579
572
|
}
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
573
|
+
},
|
|
574
|
+
getBackgroundOutput: () => getFullBufferText(headlessTerminal),
|
|
575
|
+
getSubscriptionSnapshot: () => {
|
|
576
|
+
const endLine = headlessTerminal.buffer.active.length;
|
|
577
|
+
const startLine = Math.max(0, endLine - (shellExecutionConfig.maxSerializedLines ?? 2000));
|
|
578
|
+
const bufferData = serializeTerminalToObject(headlessTerminal, startLine, endLine);
|
|
579
|
+
return bufferData.length > 0 ? bufferData : undefined;
|
|
580
|
+
},
|
|
581
|
+
}).result;
|
|
582
|
+
let processingChain = Promise.resolve();
|
|
583
|
+
let decoder = null;
|
|
584
|
+
let output = null;
|
|
585
|
+
const outputChunks = [];
|
|
586
|
+
const error = null;
|
|
587
|
+
let exited = false;
|
|
588
|
+
let isStreamingRawContent = true;
|
|
589
|
+
const MAX_SNIFF_SIZE = 4096;
|
|
590
|
+
let sniffedBytes = 0;
|
|
591
|
+
let isWriting = false;
|
|
592
|
+
let hasStartedOutput = false;
|
|
593
|
+
let renderTimeout = null;
|
|
594
|
+
const renderFn = () => {
|
|
595
|
+
renderTimeout = null;
|
|
596
|
+
if (!isStreamingRawContent) {
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
if (!shellExecutionConfig.disableDynamicLineTrimming) {
|
|
600
|
+
if (!hasStartedOutput) {
|
|
601
|
+
const bufferText = getFullBufferText(headlessTerminal);
|
|
602
|
+
if (bufferText.trim().length === 0) {
|
|
603
|
+
return;
|
|
585
604
|
}
|
|
586
|
-
|
|
587
|
-
return;
|
|
605
|
+
hasStartedOutput = true;
|
|
588
606
|
}
|
|
589
|
-
|
|
590
|
-
|
|
607
|
+
}
|
|
608
|
+
const buffer = headlessTerminal.buffer.active;
|
|
609
|
+
const endLine = buffer.length;
|
|
610
|
+
const startLine = Math.max(0, endLine - (shellExecutionConfig.maxSerializedLines ?? 2000));
|
|
611
|
+
let newOutput;
|
|
612
|
+
if (shellExecutionConfig.showColor) {
|
|
613
|
+
newOutput = serializeTerminalToObject(headlessTerminal, startLine, endLine);
|
|
614
|
+
}
|
|
615
|
+
else {
|
|
616
|
+
newOutput = (serializeTerminalToObject(headlessTerminal, startLine, endLine) ||
|
|
617
|
+
[]).map((line) => line.map((token) => {
|
|
618
|
+
token.fg = '';
|
|
619
|
+
token.bg = '';
|
|
620
|
+
return token;
|
|
621
|
+
}));
|
|
622
|
+
}
|
|
623
|
+
let lastNonEmptyLine = -1;
|
|
624
|
+
for (let i = newOutput.length - 1; i >= 0; i--) {
|
|
625
|
+
const line = newOutput[i];
|
|
626
|
+
if (line
|
|
627
|
+
.map((segment) => segment.text)
|
|
628
|
+
.join('')
|
|
629
|
+
.trim().length > 0) {
|
|
630
|
+
lastNonEmptyLine = i;
|
|
631
|
+
break;
|
|
591
632
|
}
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
633
|
+
}
|
|
634
|
+
const absoluteCursorY = buffer.baseY + buffer.cursorY;
|
|
635
|
+
const cursorRelativeIndex = absoluteCursorY - startLine;
|
|
636
|
+
if (cursorRelativeIndex > lastNonEmptyLine) {
|
|
637
|
+
lastNonEmptyLine = cursorRelativeIndex;
|
|
638
|
+
}
|
|
639
|
+
const trimmedOutput = newOutput.slice(0, lastNonEmptyLine + 1);
|
|
640
|
+
const finalOutput = shellExecutionConfig.disableDynamicLineTrimming
|
|
641
|
+
? newOutput
|
|
642
|
+
: trimmedOutput;
|
|
643
|
+
if (output !== finalOutput) {
|
|
644
|
+
output = finalOutput;
|
|
645
|
+
const event = {
|
|
646
|
+
type: 'data',
|
|
647
|
+
chunk: finalOutput,
|
|
648
|
+
};
|
|
649
|
+
onOutputEvent(event);
|
|
650
|
+
ExecutionLifecycleService.emitEvent(ptyPid, event);
|
|
651
|
+
}
|
|
652
|
+
};
|
|
653
|
+
const render = (finalRender = false) => {
|
|
654
|
+
if (finalRender) {
|
|
655
|
+
if (renderTimeout) {
|
|
656
|
+
clearTimeout(renderTimeout);
|
|
600
657
|
}
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
658
|
+
renderFn();
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
if (renderTimeout) {
|
|
662
|
+
return;
|
|
663
|
+
}
|
|
664
|
+
renderTimeout = setTimeout(() => {
|
|
665
|
+
renderFn();
|
|
666
|
+
renderTimeout = null;
|
|
667
|
+
}, 68);
|
|
668
|
+
};
|
|
669
|
+
headlessTerminal.onScroll(() => {
|
|
670
|
+
if (!isWriting) {
|
|
671
|
+
render();
|
|
672
|
+
}
|
|
673
|
+
});
|
|
674
|
+
const handleOutput = (data) => {
|
|
675
|
+
processingChain = processingChain.then(() => new Promise((resolveChunk) => {
|
|
676
|
+
if (!decoder) {
|
|
677
|
+
const encoding = getCachedEncodingForBuffer(data);
|
|
678
|
+
try {
|
|
679
|
+
decoder = new TextDecoder(encoding);
|
|
623
680
|
}
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
if (decodedChunk.length === 0) {
|
|
627
|
-
resolve();
|
|
628
|
-
return;
|
|
629
|
-
}
|
|
630
|
-
if (ShellExecutionService.backgroundLogPids.has(ptyProcess.pid)) {
|
|
631
|
-
ShellExecutionService.syncBackgroundLog(ptyProcess.pid, decodedChunk);
|
|
632
|
-
}
|
|
633
|
-
isWriting = true;
|
|
634
|
-
headlessTerminal.write(decodedChunk, () => {
|
|
635
|
-
render();
|
|
636
|
-
isWriting = false;
|
|
637
|
-
resolve();
|
|
638
|
-
});
|
|
681
|
+
catch {
|
|
682
|
+
decoder = new TextDecoder('utf-8');
|
|
639
683
|
}
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
684
|
+
}
|
|
685
|
+
outputChunks.push(data);
|
|
686
|
+
if (isStreamingRawContent && sniffedBytes < MAX_SNIFF_SIZE) {
|
|
687
|
+
const sniffBuffer = Buffer.concat(outputChunks.slice(0, 20));
|
|
688
|
+
sniffedBytes = sniffBuffer.length;
|
|
689
|
+
if (isBinary(sniffBuffer)) {
|
|
690
|
+
isStreamingRawContent = false;
|
|
691
|
+
const event = { type: 'binary_detected' };
|
|
646
692
|
onOutputEvent(event);
|
|
647
|
-
|
|
648
|
-
resolve();
|
|
693
|
+
ExecutionLifecycleService.emitEvent(ptyPid, event);
|
|
649
694
|
}
|
|
650
|
-
}));
|
|
651
|
-
};
|
|
652
|
-
ptyProcess.onData((data) => {
|
|
653
|
-
const bufferData = Buffer.from(data, 'utf-8');
|
|
654
|
-
handleOutput(bufferData);
|
|
655
|
-
});
|
|
656
|
-
ptyProcess.onExit(({ exitCode, signal }) => {
|
|
657
|
-
exited = true;
|
|
658
|
-
abortSignal.removeEventListener('abort', abortHandler);
|
|
659
|
-
// Attempt to destroy the PTY to ensure FD is closed
|
|
660
|
-
try {
|
|
661
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
662
|
-
ptyProcess.destroy?.();
|
|
663
695
|
}
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
const event = {
|
|
679
|
-
type: 'exit',
|
|
680
|
-
exitCode,
|
|
681
|
-
signal: signal ?? null,
|
|
682
|
-
};
|
|
683
|
-
onOutputEvent(event);
|
|
684
|
-
ShellExecutionService.emitEvent(ptyProcess.pid, event);
|
|
685
|
-
this.activeListeners.delete(ptyProcess.pid);
|
|
686
|
-
const finalBuffer = Buffer.concat(outputChunks);
|
|
687
|
-
resolve({
|
|
688
|
-
rawOutput: finalBuffer,
|
|
689
|
-
output: getFullBufferText(headlessTerminal),
|
|
690
|
-
exitCode,
|
|
691
|
-
signal: signal ?? null,
|
|
692
|
-
error,
|
|
693
|
-
aborted: abortSignal.aborted,
|
|
694
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
695
|
-
pid: ptyProcess.pid,
|
|
696
|
-
executionMethod: ptyInfo?.name ?? 'node-pty',
|
|
697
|
-
});
|
|
696
|
+
if (isStreamingRawContent) {
|
|
697
|
+
const decodedChunk = decoder.decode(data, { stream: true });
|
|
698
|
+
if (decodedChunk.length === 0) {
|
|
699
|
+
resolveChunk();
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
if (ShellExecutionService.backgroundLogPids.has(ptyPid)) {
|
|
703
|
+
ShellExecutionService.syncBackgroundLog(ptyPid, decodedChunk);
|
|
704
|
+
}
|
|
705
|
+
isWriting = true;
|
|
706
|
+
headlessTerminal.write(decodedChunk, () => {
|
|
707
|
+
render();
|
|
708
|
+
isWriting = false;
|
|
709
|
+
resolveChunk();
|
|
698
710
|
});
|
|
711
|
+
}
|
|
712
|
+
else {
|
|
713
|
+
const totalBytes = outputChunks.reduce((sum, chunk) => sum + chunk.length, 0);
|
|
714
|
+
const event = {
|
|
715
|
+
type: 'binary_progress',
|
|
716
|
+
bytesReceived: totalBytes,
|
|
717
|
+
};
|
|
718
|
+
onOutputEvent(event);
|
|
719
|
+
ExecutionLifecycleService.emitEvent(ptyPid, event);
|
|
720
|
+
resolveChunk();
|
|
721
|
+
}
|
|
722
|
+
}));
|
|
723
|
+
};
|
|
724
|
+
ptyProcess.onData((data) => {
|
|
725
|
+
const bufferData = Buffer.from(data, 'utf-8');
|
|
726
|
+
handleOutput(bufferData);
|
|
727
|
+
});
|
|
728
|
+
ptyProcess.onExit(({ exitCode, signal }) => {
|
|
729
|
+
exited = true;
|
|
730
|
+
abortSignal.removeEventListener('abort', abortHandler);
|
|
731
|
+
// Attempt to destroy the PTY to ensure FD is closed
|
|
732
|
+
try {
|
|
733
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
734
|
+
ptyProcess.destroy?.();
|
|
735
|
+
}
|
|
736
|
+
catch {
|
|
737
|
+
// Ignore errors during cleanup
|
|
738
|
+
}
|
|
739
|
+
const finalize = () => {
|
|
740
|
+
render(true);
|
|
741
|
+
const event = {
|
|
742
|
+
type: 'exit',
|
|
743
|
+
exitCode,
|
|
744
|
+
signal: signal ?? null,
|
|
699
745
|
};
|
|
746
|
+
onOutputEvent(event);
|
|
747
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
748
|
+
ShellExecutionService.cleanupLogStream(ptyPid).then(() => {
|
|
749
|
+
ShellExecutionService.activePtys.delete(ptyPid);
|
|
750
|
+
});
|
|
751
|
+
ExecutionLifecycleService.completeWithResult(ptyPid, {
|
|
752
|
+
rawOutput: Buffer.concat(outputChunks),
|
|
753
|
+
output: getFullBufferText(headlessTerminal),
|
|
754
|
+
exitCode,
|
|
755
|
+
signal: signal ?? null,
|
|
756
|
+
error,
|
|
757
|
+
aborted: abortSignal.aborted,
|
|
758
|
+
pid: ptyPid,
|
|
759
|
+
executionMethod: ptyInfo?.name ?? 'node-pty',
|
|
760
|
+
});
|
|
761
|
+
};
|
|
762
|
+
if (abortSignal.aborted) {
|
|
763
|
+
finalize();
|
|
764
|
+
return;
|
|
765
|
+
}
|
|
766
|
+
const processingComplete = processingChain.then(() => 'processed');
|
|
767
|
+
const abortFired = new Promise((res) => {
|
|
700
768
|
if (abortSignal.aborted) {
|
|
701
|
-
|
|
769
|
+
res('aborted');
|
|
702
770
|
return;
|
|
703
771
|
}
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
if (abortSignal.aborted) {
|
|
707
|
-
res('aborted');
|
|
708
|
-
return;
|
|
709
|
-
}
|
|
710
|
-
abortSignal.addEventListener('abort', () => res('aborted'), {
|
|
711
|
-
once: true,
|
|
712
|
-
});
|
|
713
|
-
});
|
|
714
|
-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
715
|
-
Promise.race([processingComplete, abortFired]).then(() => {
|
|
716
|
-
finalize();
|
|
772
|
+
abortSignal.addEventListener('abort', () => res('aborted'), {
|
|
773
|
+
once: true,
|
|
717
774
|
});
|
|
718
775
|
});
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
pid: ptyProcess.pid,
|
|
724
|
-
escalate: true,
|
|
725
|
-
isExited: () => exited,
|
|
726
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
727
|
-
pty: ptyProcess,
|
|
728
|
-
});
|
|
729
|
-
}
|
|
730
|
-
};
|
|
731
|
-
abortSignal.addEventListener('abort', abortHandler, { once: true });
|
|
776
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
777
|
+
Promise.race([processingComplete, abortFired]).then(() => {
|
|
778
|
+
finalize();
|
|
779
|
+
});
|
|
732
780
|
});
|
|
733
|
-
|
|
734
|
-
|
|
781
|
+
const abortHandler = async () => {
|
|
782
|
+
if (ptyProcess.pid && !exited) {
|
|
783
|
+
await killProcessGroup({
|
|
784
|
+
pid: ptyPid,
|
|
785
|
+
escalate: true,
|
|
786
|
+
isExited: () => exited,
|
|
787
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
788
|
+
pty: ptyProcess,
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
};
|
|
792
|
+
abortSignal.addEventListener('abort', abortHandler, { once: true });
|
|
793
|
+
return { pid: ptyPid, result };
|
|
735
794
|
}
|
|
736
795
|
catch (e) {
|
|
737
796
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
@@ -775,38 +834,10 @@ export class ShellExecutionService {
|
|
|
775
834
|
* @param input The string to write to the terminal.
|
|
776
835
|
*/
|
|
777
836
|
static writeToPty(pid, input) {
|
|
778
|
-
|
|
779
|
-
const activeChild = this.activeChildProcesses.get(pid);
|
|
780
|
-
if (activeChild) {
|
|
781
|
-
activeChild.process.stdin?.write(input);
|
|
782
|
-
}
|
|
783
|
-
return;
|
|
784
|
-
}
|
|
785
|
-
if (!this.isPtyActive(pid)) {
|
|
786
|
-
return;
|
|
787
|
-
}
|
|
788
|
-
const activePty = this.activePtys.get(pid);
|
|
789
|
-
if (activePty) {
|
|
790
|
-
activePty.ptyProcess.write(input);
|
|
791
|
-
}
|
|
837
|
+
ExecutionLifecycleService.writeInput(pid, input);
|
|
792
838
|
}
|
|
793
839
|
static isPtyActive(pid) {
|
|
794
|
-
|
|
795
|
-
try {
|
|
796
|
-
return process.kill(pid, 0);
|
|
797
|
-
}
|
|
798
|
-
catch {
|
|
799
|
-
return false;
|
|
800
|
-
}
|
|
801
|
-
}
|
|
802
|
-
try {
|
|
803
|
-
// process.kill with signal 0 is a way to check for the existence of a process.
|
|
804
|
-
// It doesn't actually send a signal.
|
|
805
|
-
return process.kill(pid, 0);
|
|
806
|
-
}
|
|
807
|
-
catch (_) {
|
|
808
|
-
return false;
|
|
809
|
-
}
|
|
840
|
+
return ExecutionLifecycleService.isActive(pid);
|
|
810
841
|
}
|
|
811
842
|
/**
|
|
812
843
|
* Registers a callback to be invoked when the process with the given PID exits.
|
|
@@ -817,36 +848,7 @@ export class ShellExecutionService {
|
|
|
817
848
|
* @returns An unsubscribe function.
|
|
818
849
|
*/
|
|
819
850
|
static onExit(pid, callback) {
|
|
820
|
-
|
|
821
|
-
if (activePty) {
|
|
822
|
-
const disposable = activePty.ptyProcess.onExit(({ exitCode, signal }) => {
|
|
823
|
-
callback(exitCode, signal);
|
|
824
|
-
disposable.dispose();
|
|
825
|
-
});
|
|
826
|
-
return () => disposable.dispose();
|
|
827
|
-
}
|
|
828
|
-
else if (this.activeChildProcesses.has(pid)) {
|
|
829
|
-
const activeChild = this.activeChildProcesses.get(pid);
|
|
830
|
-
const listener = (code, signal) => {
|
|
831
|
-
let signalNumber;
|
|
832
|
-
if (signal) {
|
|
833
|
-
signalNumber = os.constants.signals[signal];
|
|
834
|
-
}
|
|
835
|
-
callback(code ?? 0, signalNumber);
|
|
836
|
-
};
|
|
837
|
-
activeChild?.process.on('exit', listener);
|
|
838
|
-
return () => {
|
|
839
|
-
activeChild?.process.removeListener('exit', listener);
|
|
840
|
-
};
|
|
841
|
-
}
|
|
842
|
-
else {
|
|
843
|
-
// Check if it already exited recently
|
|
844
|
-
const exitedInfo = this.exitedPtyInfo.get(pid);
|
|
845
|
-
if (exitedInfo) {
|
|
846
|
-
callback(exitedInfo.exitCode, exitedInfo.signal);
|
|
847
|
-
}
|
|
848
|
-
return () => { };
|
|
849
|
-
}
|
|
851
|
+
return ExecutionLifecycleService.onExit(pid, callback);
|
|
850
852
|
}
|
|
851
853
|
/**
|
|
852
854
|
* Kills a process by its PID.
|
|
@@ -854,25 +856,10 @@ export class ShellExecutionService {
|
|
|
854
856
|
* @param pid The process ID to kill.
|
|
855
857
|
*/
|
|
856
858
|
static async kill(pid) {
|
|
857
|
-
const activePty = this.activePtys.get(pid);
|
|
858
|
-
const activeChild = this.activeChildProcesses.get(pid);
|
|
859
859
|
await this.cleanupLogStream(pid);
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
}
|
|
864
|
-
else if (activePty) {
|
|
865
|
-
killProcessGroup({ pid, pty: activePty.ptyProcess }).catch(() => { });
|
|
866
|
-
try {
|
|
867
|
-
activePty.ptyProcess.destroy?.();
|
|
868
|
-
}
|
|
869
|
-
catch {
|
|
870
|
-
// Ignore errors during cleanup
|
|
871
|
-
}
|
|
872
|
-
this.activePtys.delete(pid);
|
|
873
|
-
}
|
|
874
|
-
this.activeResolvers.delete(pid);
|
|
875
|
-
this.activeListeners.delete(pid);
|
|
860
|
+
this.activePtys.delete(pid);
|
|
861
|
+
this.activeChildProcesses.delete(pid);
|
|
862
|
+
ExecutionLifecycleService.kill(pid);
|
|
876
863
|
}
|
|
877
864
|
/**
|
|
878
865
|
* Moves a running shell command to the background.
|
|
@@ -881,17 +868,9 @@ export class ShellExecutionService {
|
|
|
881
868
|
* @param pid The process ID of the target PTY.
|
|
882
869
|
*/
|
|
883
870
|
static background(pid) {
|
|
884
|
-
const resolve = this.activeResolvers.get(pid);
|
|
885
|
-
if (!resolve)
|
|
886
|
-
return;
|
|
887
871
|
const activePty = this.activePtys.get(pid);
|
|
888
872
|
const activeChild = this.activeChildProcesses.get(pid);
|
|
889
|
-
|
|
890
|
-
return;
|
|
891
|
-
const output = activePty
|
|
892
|
-
? getFullBufferText(activePty.headlessTerminal)
|
|
893
|
-
: (activeChild?.state.output ?? '');
|
|
894
|
-
const executionMethod = activePty ? 'node-pty' : 'child_process';
|
|
873
|
+
// Set up background logging
|
|
895
874
|
const logPath = this.getLogFilePath(pid);
|
|
896
875
|
const logDir = this.getLogDir();
|
|
897
876
|
try {
|
|
@@ -905,6 +884,7 @@ export class ShellExecutionService {
|
|
|
905
884
|
writeBufferToLogStream(activePty.headlessTerminal, stream, 0);
|
|
906
885
|
}
|
|
907
886
|
else if (activeChild) {
|
|
887
|
+
const output = activeChild.state.output;
|
|
908
888
|
if (output) {
|
|
909
889
|
stream.write(stripAnsi(output) + '\n');
|
|
910
890
|
}
|
|
@@ -914,48 +894,10 @@ export class ShellExecutionService {
|
|
|
914
894
|
debugLogger.warn('Failed to setup background logging:', e);
|
|
915
895
|
}
|
|
916
896
|
this.backgroundLogPids.add(pid);
|
|
917
|
-
|
|
918
|
-
rawOutput: Buffer.from(''),
|
|
919
|
-
output,
|
|
920
|
-
exitCode: null,
|
|
921
|
-
signal: null,
|
|
922
|
-
error: null,
|
|
923
|
-
aborted: false,
|
|
924
|
-
pid,
|
|
925
|
-
executionMethod,
|
|
926
|
-
backgrounded: true,
|
|
927
|
-
});
|
|
928
|
-
this.activeResolvers.delete(pid);
|
|
897
|
+
ExecutionLifecycleService.background(pid);
|
|
929
898
|
}
|
|
930
899
|
static subscribe(pid, listener) {
|
|
931
|
-
|
|
932
|
-
this.activeListeners.set(pid, new Set());
|
|
933
|
-
}
|
|
934
|
-
this.activeListeners.get(pid)?.add(listener);
|
|
935
|
-
// Send current buffer content immediately
|
|
936
|
-
const activePty = this.activePtys.get(pid);
|
|
937
|
-
const activeChild = this.activeChildProcesses.get(pid);
|
|
938
|
-
if (activePty) {
|
|
939
|
-
// Use serializeTerminalToObject to preserve colors and structure
|
|
940
|
-
const endLine = activePty.headlessTerminal.buffer.active.length;
|
|
941
|
-
const startLine = Math.max(0, endLine - (activePty.maxSerializedLines ?? 2000));
|
|
942
|
-
const bufferData = serializeTerminalToObject(activePty.headlessTerminal, startLine, endLine);
|
|
943
|
-
if (bufferData && bufferData.length > 0) {
|
|
944
|
-
listener({ type: 'data', chunk: bufferData });
|
|
945
|
-
}
|
|
946
|
-
}
|
|
947
|
-
else if (activeChild) {
|
|
948
|
-
const output = activeChild.state.output;
|
|
949
|
-
if (output) {
|
|
950
|
-
listener({ type: 'data', chunk: output });
|
|
951
|
-
}
|
|
952
|
-
}
|
|
953
|
-
return () => {
|
|
954
|
-
this.activeListeners.get(pid)?.delete(listener);
|
|
955
|
-
if (this.activeListeners.get(pid)?.size === 0) {
|
|
956
|
-
this.activeListeners.delete(pid);
|
|
957
|
-
}
|
|
958
|
-
};
|
|
900
|
+
return ExecutionLifecycleService.subscribe(pid, listener);
|
|
959
901
|
}
|
|
960
902
|
/**
|
|
961
903
|
* Resizes the pseudo-terminal (PTY) of a running process.
|
|
@@ -997,10 +939,7 @@ export class ShellExecutionService {
|
|
|
997
939
|
const startLine = Math.max(0, endLine - (activePty.maxSerializedLines ?? 2000));
|
|
998
940
|
const bufferData = serializeTerminalToObject(activePty.headlessTerminal, startLine, endLine);
|
|
999
941
|
const event = { type: 'data', chunk: bufferData };
|
|
1000
|
-
|
|
1001
|
-
if (listeners) {
|
|
1002
|
-
listeners.forEach((listener) => listener(event));
|
|
1003
|
-
}
|
|
942
|
+
ExecutionLifecycleService.emitEvent(pid, event);
|
|
1004
943
|
}
|
|
1005
944
|
}
|
|
1006
945
|
/**
|