aws-runtime-bridge 1.9.172 → 1.9.179
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/CHANGELOG.md +314 -0
- package/README.md +61 -2
- package/dist/adapter/AcodeSdkAdapter.d.ts +374 -1
- package/dist/adapter/AcodeSdkAdapter.js +3 -3
- package/dist/adapter/AdapterRegistry.d.ts +28 -0
- package/dist/adapter/ClaudeSdkAdapter.d.ts +71 -0
- package/dist/adapter/CodexSdkAdapter.d.ts +14 -0
- package/dist/adapter/OpencodeSdkAdapter.d.ts +44 -0
- package/dist/adapter/acode-tool-names.d.ts +9 -0
- package/dist/adapter/idle-poll-loop.d.ts +20 -0
- package/dist/adapter/index.d.ts +3 -0
- package/dist/adapter/instance-ai-config-resolver.d.ts +20 -0
- package/dist/adapter/prompts.d.ts +12 -0
- package/dist/adapter/types.d.ts +184 -0
- package/dist/browser/browser-bootstrap.d.ts +15 -0
- package/dist/browser/browser-connect-token.d.ts +11 -0
- package/dist/browser/browser-frame-encode.d.ts +32 -0
- package/dist/browser/browser-pool.d.ts +129 -0
- package/dist/browser/browser-profile-store.d.ts +41 -0
- package/dist/browser/browser-ws-bridge.d.ts +20 -0
- package/dist/browser/built-in-browser-tools.d.ts +44 -0
- package/dist/browser/element-locator.d.ts +25 -0
- package/dist/browser/element-picker.d.ts +30 -0
- package/dist/browser/frame-encode.d.ts +19 -0
- package/dist/browser/input-dispatcher.d.ts +35 -0
- package/dist/browser/listener-store.d.ts +44 -0
- package/dist/browser/page-listener-manager.d.ts +88 -0
- package/dist/browser/page-listener-types.d.ts +38 -0
- package/dist/browser/screencast-differ.d.ts +10 -0
- package/dist/browser/screencast-manager.d.ts +94 -0
- package/dist/browser/snapshot-serializer.d.ts +56 -0
- package/dist/browser/ssrf-guard.d.ts +44 -0
- package/dist/browser/stealth.d.ts +21 -0
- package/dist/browser/step-archive.d.ts +13 -0
- package/dist/browser/types.d.ts +100 -0
- package/dist/browser/ws-server.d.ts +54 -0
- package/dist/config.d.ts +138 -2
- package/dist/config.js +2 -2
- package/dist/desktop/built-in-desktop-tools.d.ts +68 -0
- package/dist/desktop/built-in-desktop-tools.js +2 -1
- package/dist/desktop/desktop-control.d.ts +142 -0
- package/dist/desktop/desktop-control.js +2 -0
- package/dist/desktop/desktop-mcp-client.d.ts +28 -0
- package/dist/desktop/desktop-staleness.d.ts +66 -0
- package/dist/desktop/desktop-staleness.js +2 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +1 -1
- package/dist/middleware/auth.d.ts +26 -0
- package/dist/middleware/auth.js +1 -1
- package/dist/remote-desktop/computer-control.d.ts +3 -0
- package/dist/remote-desktop/coords.d.ts +6 -0
- package/dist/remote-desktop/frame-backpressure.d.ts +10 -0
- package/dist/remote-desktop/frame-encode.d.ts +3 -0
- package/dist/remote-desktop/index.js +2 -2
- package/dist/remote-desktop/input-dispatcher.d.ts +4 -0
- package/dist/remote-desktop/permission-manager.d.ts +3 -0
- package/dist/remote-desktop/screen-capture.d.ts +91 -0
- package/dist/remote-desktop/session-manager.d.ts +47 -0
- package/dist/remote-desktop/session-manager.js +1 -1
- package/dist/remote-desktop/system-backend.d.ts +64 -0
- package/dist/remote-desktop/types.d.ts +25 -0
- package/dist/remote-desktop/ui-automation.d.ts +11 -0
- package/dist/remote-desktop/win32-api.d.ts +10 -0
- package/dist/routes/acode-todos.d.ts +13 -0
- package/dist/routes/agent-state.d.ts +15 -0
- package/dist/routes/ai-sources.js +1 -1
- package/dist/routes/background-tasks.d.ts +18 -0
- package/dist/routes/background-tasks.js +2 -2
- package/dist/routes/bridge-temp-files.d.ts +1 -0
- package/dist/routes/bridge-temp-files.js +2 -0
- package/dist/routes/browser-listeners.d.ts +17 -0
- package/dist/routes/browser-sessions.d.ts +10 -0
- package/dist/routes/capabilities.js +1 -1
- package/dist/routes/dashboard-icon.d.ts +9 -0
- package/dist/routes/dashboard-icon.js +2 -0
- package/dist/routes/dashboard-theme.d.ts +24 -0
- package/dist/routes/dashboard-theme.js +157 -0
- package/dist/routes/dashboard.d.ts +9 -0
- package/dist/routes/dashboard.js +2021 -628
- package/dist/routes/debug-mount.d.ts +16 -0
- package/dist/routes/desktop-control.d.ts +15 -0
- package/dist/routes/desktop-control.js +2 -0
- package/dist/routes/events.d.ts +4 -0
- package/dist/routes/file-browser.d.ts +15 -10
- package/dist/routes/file-browser.js +1 -1
- package/dist/routes/file-change-diff.d.ts +25 -0
- package/dist/routes/file-diff-rollback.d.ts +28 -0
- package/dist/routes/file-snapshot.d.ts +7 -0
- package/dist/routes/git.d.ts +185 -0
- package/dist/routes/git.js +34 -34
- package/dist/routes/instance-tool-status.d.ts +5 -0
- package/dist/routes/instance.d.ts +8 -0
- package/dist/routes/instance.js +1 -1
- package/dist/routes/jdks.d.ts +4 -0
- package/dist/routes/launch.d.ts +5 -0
- package/dist/routes/lsp-providers.d.ts +11 -0
- package/dist/routes/lsp-providers.js +2 -0
- package/dist/routes/lsp.d.ts +41 -0
- package/dist/routes/lsp.js +4 -2
- package/dist/routes/mcp-extra.d.ts +15 -0
- package/dist/routes/mcp.d.ts +16 -0
- package/dist/routes/mcp.js +1 -1
- package/dist/routes/plugins.d.ts +19 -0
- package/dist/routes/plugins.js +1 -1
- package/dist/routes/processes.d.ts +4 -0
- package/dist/routes/properties.d.ts +4 -0
- package/dist/routes/pty.d.ts +29 -0
- package/dist/routes/runtime-binding.d.ts +3 -0
- package/dist/routes/sessions.d.ts +22 -0
- package/dist/routes/sessions.js +1 -1
- package/dist/routes/skills.d.ts +15 -0
- package/dist/routes/skills.js +1 -1
- package/dist/routes/sub-agents.d.ts +11 -0
- package/dist/routes/sub-agents.js +1 -1
- package/dist/routes/task-context.d.ts +5 -0
- package/dist/routes/tech-stack.d.ts +6 -0
- package/dist/routes/tech-stack.js +2 -0
- package/dist/routes/terminal.d.ts +122 -0
- package/dist/routes/terminal.js +22 -22
- package/dist/routes/workspace-index.d.ts +12 -0
- package/dist/routes/workspace-index.js +2 -0
- package/dist/routes/yml.d.ts +4 -0
- package/dist/scripts/migrate-user-scopes.d.ts +26 -0
- package/dist/scripts/migrate-user-scopes.js +2 -0
- package/dist/services/access-control.d.ts +21 -0
- package/dist/services/acode-bg-manager.d.ts +21 -0
- package/dist/services/acode-package.d.ts +10 -0
- package/dist/services/acode-package.js +1 -1
- package/dist/services/agent-capability-sync.d.ts +26 -0
- package/dist/services/agent-capability-sync.js +1 -1
- package/dist/services/agent-event-store.d.ts +20 -0
- package/dist/services/agent-ownership.d.ts +58 -0
- package/dist/services/agent-ownership.js +3 -0
- package/dist/services/agent-process-manager.d.ts +154 -0
- package/dist/services/agent-pty-manager.d.ts +39 -0
- package/dist/services/agent-pty-manager.js +5 -4
- package/dist/services/agent-startup-memory.d.ts +34 -0
- package/dist/services/agent-startup-memory.js +4 -4
- package/dist/services/ai-config-service.d.ts +26 -52
- package/dist/services/ai-config-service.js +1 -3
- package/dist/services/ai-source-apply.d.ts +7 -15
- package/dist/services/ai-source-apply.js +1 -1
- package/dist/services/auto-register.d.ts +165 -1
- package/dist/services/auto-register.js +1 -1
- package/dist/services/aws-client-agent-mcp.d.ts +14 -0
- package/dist/services/aws-client-agent-mcp.js +2 -2
- package/dist/services/bridge-package-publish-hooks.d.ts +14 -0
- package/dist/services/bridge-pairing.d.ts +82 -0
- package/dist/services/bridge-pairing.js +2 -0
- package/dist/services/bridge-temp-files.d.ts +65 -0
- package/dist/services/bridge-temp-files.js +2 -0
- package/dist/services/browser-session-manager.d.ts +71 -0
- package/dist/services/capability-config.d.ts +60 -15
- package/dist/services/capability-config.js +2 -2
- package/dist/services/change-cache-retention.d.ts +9 -0
- package/dist/services/changelog.d.ts +9 -0
- package/dist/services/cli-commands.d.ts +82 -0
- package/dist/services/cli-commands.js +6 -6
- package/dist/services/connection-key-rotation.d.ts +27 -0
- package/dist/services/connection-key-rotation.js +2 -0
- package/dist/services/debug-mount.d.ts +49 -0
- package/dist/services/desktop-mcp-manager.d.ts +103 -1
- package/dist/services/desktop-mcp-manager.js +1 -1
- package/dist/services/event-delta-pusher.d.ts +15 -0
- package/dist/services/fileSnapshotStore.d.ts +42 -0
- package/dist/services/fileSnapshotStore.js +1 -1
- package/dist/services/gitignore.d.ts +21 -0
- package/dist/services/gitignore.js +2 -0
- package/dist/services/instance-ai-config-service.d.ts +9 -0
- package/dist/services/instance-ai-config-service.js +1 -1
- package/dist/services/instance-features.d.ts +127 -0
- package/dist/services/instance-features.js +1 -1
- package/dist/services/instance-init-service.d.ts +3 -0
- package/dist/services/instance-init-service.js +1 -1
- package/dist/services/instance-state.d.ts +23 -0
- package/dist/services/jdk-scanner.d.ts +15 -0
- package/dist/services/launch-manager.d.ts +138 -0
- package/dist/services/launch-metrics.d.ts +42 -0
- package/dist/services/launch-streams.d.ts +19 -0
- package/dist/services/lifecycle-state.d.ts +5 -0
- package/dist/services/log-retention.d.ts +38 -0
- package/dist/services/log-retention.js +1 -1
- package/dist/services/lsp/install-manager.d.ts +55 -0
- package/dist/services/lsp/install-manager.js +2 -0
- package/dist/services/lsp/provider-registry.d.ts +108 -0
- package/dist/services/lsp/provider-registry.js +2 -0
- package/dist/services/lsp/uri-rewrite.d.ts +44 -0
- package/dist/services/lsp/uri-rewrite.js +2 -0
- package/dist/services/lsp-stdio-bridge.d.ts +41 -4
- package/dist/services/lsp-stdio-bridge.js +3 -3
- package/dist/services/mcp-deploy.d.ts +45 -4
- package/dist/services/mcp-deploy.js +4 -4
- package/dist/services/mcp-env-injector.d.ts +52 -0
- package/dist/services/mcp-launch-binding-queue.d.ts +12 -0
- package/dist/services/mcp-package.d.ts +22 -0
- package/dist/services/mcp-package.js +1 -1
- package/dist/services/mcp-probe-client.d.ts +19 -0
- package/dist/services/mcp-probe-client.js +1 -1
- package/dist/services/open-doc-events.d.ts +43 -0
- package/dist/services/open-doc-events.js +2 -0
- package/dist/services/open-doc-registry.d.ts +59 -0
- package/dist/services/open-doc-registry.js +2 -0
- package/dist/services/orphan-monitor.d.ts +43 -0
- package/dist/services/panel-auth.d.ts +24 -0
- package/dist/services/panel-env.d.ts +28 -0
- package/dist/services/plugin-deployment.d.ts +34 -1
- package/dist/services/plugin-deployment.js +2 -2
- package/dist/services/plugin-event-buffer.d.ts +21 -0
- package/dist/services/plugin-fs.d.ts +11 -0
- package/dist/services/plugin-handler-adapter.d.ts +13 -0
- package/dist/services/plugin-listener-runner.d.ts +10 -0
- package/dist/services/plugin-loader.d.ts +15 -0
- package/dist/services/plugin-manager.d.ts +95 -9
- package/dist/services/plugin-manager.js +4 -4
- package/dist/services/plugin-manifest.d.ts +14 -0
- package/dist/services/plugin-package.d.ts +13 -0
- package/dist/services/plugin-runtime.d.ts +15 -0
- package/dist/services/port-detector.d.ts +15 -0
- package/dist/services/process-detail.d.ts +3 -0
- package/dist/services/process-detector.d.ts +132 -0
- package/dist/services/process-registry.d.ts +120 -0
- package/dist/services/process-top.d.ts +6 -0
- package/dist/services/request-identity.d.ts +77 -0
- package/dist/services/request-identity.js +2 -0
- package/dist/services/request-invalid-dump.d.ts +4 -0
- package/dist/services/request-invalid-dump.js +1 -1
- package/dist/services/ring-buffer.d.ts +33 -0
- package/dist/services/runtime-binding.d.ts +56 -4
- package/dist/services/runtime-binding.js +4 -4
- package/dist/services/runtime-lifecycle-policy.d.ts +17 -0
- package/dist/services/runtime-resources-snapshot.d.ts +26 -0
- package/dist/services/runtime-resources-snapshot.js +2 -2
- package/dist/services/sdk-tool-file-change-recorder.d.ts +9 -0
- package/dist/services/session-output.d.ts +63 -0
- package/dist/services/session-output.js +3 -3
- package/dist/services/skill-manager.d.ts +42 -5
- package/dist/services/skill-manager.js +3 -3
- package/dist/services/snapshot-retention.d.ts +30 -0
- package/dist/services/snapshot-retention.js +2 -0
- package/dist/services/startup-config-wizard.d.ts +7 -1
- package/dist/services/startup-config-wizard.js +10 -10
- package/dist/services/system-metrics.d.ts +3 -0
- package/dist/services/tech-stack-scanner.d.ts +43 -0
- package/dist/services/tech-stack-scanner.js +2 -0
- package/dist/services/terminal-persistence.d.ts +43 -0
- package/dist/services/tool-installer.d.ts +12 -0
- package/dist/services/tunnel-client.d.ts +99 -2
- package/dist/services/tunnel-client.js +1 -1
- package/dist/services/update-log.d.ts +6 -0
- package/dist/services/update-notifier.d.ts +17 -0
- package/dist/services/update-notifier.js +2 -2
- package/dist/services/user-scope.d.ts +115 -0
- package/dist/services/user-scope.js +2 -0
- package/dist/services/windows-service-manager.d.ts +12 -0
- package/dist/services/workspace-files.d.ts +64 -0
- package/dist/services/workspace-index/index-manager.d.ts +46 -0
- package/dist/services/workspace-index/index-manager.js +2 -0
- package/dist/services/workspace-index/indexer.d.ts +15 -0
- package/dist/services/workspace-index/indexer.js +2 -0
- package/dist/services/workspace-index/query.d.ts +74 -0
- package/dist/services/workspace-index/query.js +5 -0
- package/dist/services/workspace-index/resolver.d.ts +26 -0
- package/dist/services/workspace-index/resolver.js +2 -0
- package/dist/services/workspace-index/types.d.ts +89 -0
- package/dist/services/workspace-index/types.js +2 -0
- package/dist/services/workspace-watch.d.ts +4 -0
- package/dist/services/workspaceTaskChangeWatcher.d.ts +49 -0
- package/dist/services/workspaceTaskChangeWatcher.js +2 -2
- package/dist/types.d.ts +9 -0
- package/dist/utils/config-write-queue.d.ts +11 -0
- package/dist/utils/env-safety.d.ts +21 -0
- package/dist/utils/file-utils.d.ts +15 -0
- package/dist/utils/instance-ai-config-debug-logger.d.ts +15 -0
- package/dist/utils/lifecycle-log.d.ts +5 -0
- package/dist/utils/logger.d.ts +13 -0
- package/dist/utils/logger.js +2 -1
- package/dist/utils/npm-version.d.ts +10 -0
- package/dist/utils/path-utils.d.ts +12 -0
- package/dist/utils/scheduler-url.d.ts +18 -0
- package/dist/utils/scheduler-url.js +2 -0
- package/dist/utils/sdk-package-loader.d.ts +13 -0
- package/dist/utils/sdk-package-loader.js +1 -1
- package/dist/utils/target-identity.d.ts +31 -0
- package/dist/utils/target-identity.js +2 -0
- package/dist/utils/validation.d.ts +17 -0
- package/dist/utils/yaml-utils.d.ts +48 -0
- package/package/acode/dist/__tests__/summary-request-helper.d.ts +12 -0
- package/package/acode/dist/__tests__/summary-request-helper.js +2 -0
- package/package/acode/dist/ask-user-question-tool.d.ts +17 -0
- package/package/acode/dist/await-complete-tool.d.ts +12 -0
- package/package/acode/dist/background-command-manager.d.ts +105 -0
- package/package/acode/dist/background-command-store.d.ts +27 -0
- package/package/acode/dist/background-command-tools.d.ts +8 -0
- package/package/acode/dist/background-output-ringbuffer.d.ts +15 -0
- package/package/acode/dist/built-in-file-tools.d.ts +65 -0
- package/package/acode/dist/built-in-symbols-tool.d.ts +3 -0
- package/package/acode/dist/builtins/commandArtifacts.d.ts +3 -0
- package/package/acode/dist/builtins/shellCommandPreparation.d.ts +17 -0
- package/package/acode/dist/builtins/shellCommandRunner.d.ts +4 -0
- package/package/acode/dist/builtins/shellOutputDecoder.d.ts +15 -0
- package/package/acode/dist/cache-control.d.ts +71 -0
- package/package/acode/dist/cache-control.js +2 -0
- package/package/acode/dist/commands/commandLoader.d.ts +3 -0
- package/package/acode/dist/commands/commandParser.d.ts +3 -0
- package/package/acode/dist/commands/commandRegistry.d.ts +3 -0
- package/package/acode/dist/compaction.d.ts +388 -4
- package/package/acode/dist/compaction.js +19 -17
- package/package/acode/dist/config.d.ts +3 -0
- package/package/acode/dist/context-overflow.d.ts +32 -0
- package/package/acode/dist/fs-utils.d.ts +18 -0
- package/package/acode/dist/hooks/hookBus.d.ts +3 -0
- package/package/acode/dist/image-utils.d.ts +35 -0
- package/package/acode/dist/index.d.ts +1 -0
- package/package/acode/dist/index.js +1 -1
- package/package/acode/dist/mcpToolNames.d.ts +32 -0
- package/package/acode/dist/microcompact.d.ts +110 -0
- package/package/acode/dist/microcompact.js +2 -0
- package/package/acode/dist/parsers/ast-cache.d.ts +41 -0
- package/package/acode/dist/parsers/file-index.d.ts +83 -0
- package/package/acode/dist/parsers/file-index.js +4 -0
- package/package/acode/dist/parsers/language-detector.d.ts +4 -0
- package/package/acode/dist/parsers/tree-sitter-loader.d.ts +12 -0
- package/package/acode/dist/parsers/types.d.ts +11 -0
- package/package/acode/dist/paths.d.ts +14 -0
- package/package/acode/dist/plugin-types.d.ts +34 -0
- package/package/acode/dist/read-file-state.d.ts +15 -0
- package/package/acode/dist/runtime-environment.d.ts +16 -0
- package/package/acode/dist/runtime.d.ts +828 -9
- package/package/acode/dist/runtime.js +40 -33
- package/package/acode/dist/skill-tool.d.ts +20 -0
- package/package/acode/dist/sub-agent-manager.d.ts +169 -0
- package/package/acode/dist/sub-agent-manager.js +1 -1
- package/package/acode/dist/sub-agent-store.d.ts +50 -0
- package/package/acode/dist/sub-agent-tools.d.ts +15 -0
- package/package/acode/dist/sub-agent-types.d.ts +13 -0
- package/package/acode/dist/terminal-tool.d.ts +15 -0
- package/package/acode/dist/terminal-types.d.ts +10 -0
- package/package/acode/dist/todo-store.d.ts +15 -0
- package/package/acode/dist/token-estimator.d.ts +32 -0
- package/package/acode/dist/token-estimator.js +2 -0
- package/package/acode/dist/tool-allocation.d.ts +113 -0
- package/package/acode/dist/tool-concurrency/index.d.ts +8 -0
- package/package/acode/dist/tool-concurrency/observability.d.ts +32 -0
- package/package/acode/dist/tool-concurrency/resource-lock.d.ts +30 -0
- package/package/acode/dist/tool-concurrency/scheduler.d.ts +19 -0
- package/package/acode/dist/tool-concurrency/types.d.ts +57 -0
- package/package/acode/dist/tool-group-manager.d.ts +195 -0
- package/package/acode/dist/tool-group-manager.js +4 -4
- package/package/acode/dist/types.d.ts +192 -0
- package/package/aws-client-agent-mcp/README.md +130 -215
- package/package/aws-client-agent-mcp/dist/ability-market-tools.d.ts +57 -0
- package/package/aws-client-agent-mcp/dist/ability-market-tools.js +1 -1
- package/package/aws-client-agent-mcp/dist/agent-client.d.ts +290 -3
- package/package/aws-client-agent-mcp/dist/agent-client.js +1 -1
- package/package/aws-client-agent-mcp/dist/config.d.ts +18 -0
- package/package/aws-client-agent-mcp/dist/constants.d.ts +2 -0
- package/package/aws-client-agent-mcp/dist/context-manager.d.ts +70 -0
- package/package/aws-client-agent-mcp/dist/http-client.d.ts +61 -0
- package/package/aws-client-agent-mcp/dist/index.d.ts +10 -0
- package/package/aws-client-agent-mcp/dist/logger.d.ts +24 -0
- package/package/aws-client-agent-mcp/dist/mcp-server.d.ts +610 -0
- package/package/aws-client-agent-mcp/dist/mcp-server.js +25 -23
- package/package/aws-client-agent-mcp/dist/mcp-tools.d.ts +158 -0
- package/package/aws-client-agent-mcp/dist/mcp-tools.js +38 -22
- package/package/aws-client-agent-mcp/dist/memory-store.d.ts +102 -2
- package/package/aws-client-agent-mcp/dist/memory-store.js +27 -2
- package/package/aws-client-agent-mcp/dist/memory-tools.d.ts +18 -0
- package/package/aws-client-agent-mcp/dist/memory-tools.js +13 -9
- package/package/aws-client-agent-mcp/dist/message-buffer.d.ts +86 -0
- package/package/aws-client-agent-mcp/dist/messageContent.d.ts +24 -0
- package/package/aws-client-agent-mcp/dist/office/office-equation.d.ts +19 -0
- package/package/aws-client-agent-mcp/dist/office/office-image.d.ts +18 -0
- package/package/aws-client-agent-mcp/dist/office/office-workspace.d.ts +29 -0
- package/package/aws-client-agent-mcp/dist/parent-lifecycle.d.ts +18 -0
- package/package/aws-client-agent-mcp/dist/runtime-launch-binding.d.ts +6 -0
- package/package/aws-client-agent-mcp/dist/server-memory-store.d.ts +5 -1
- package/package/aws-client-agent-mcp/dist/server-memory-store.js +1 -1
- package/package/aws-client-agent-mcp/dist/status-reporter.d.ts +41 -0
- package/package/aws-client-agent-mcp/dist/tool-groups.d.ts +136 -0
- package/package/aws-client-agent-mcp/dist/types.d.ts +174 -0
- package/package/aws-client-agent-mcp/dist/watchdog.d.ts +7 -0
- package/package/aws-client-agent-mcp/dist/websocket-client.d.ts +108 -0
- package/package.json +1 -1
package/dist/routes/git.js
CHANGED
|
@@ -1,37 +1,37 @@
|
|
|
1
|
-
import{spawn as
|
|
2
|
-
`).find(i=>i.trim());if(!t)return null;const
|
|
3
|
-
`).filter(i=>i.trim());for(const i of
|
|
4
|
-
`).filter(c=>c.trim());for(const c of
|
|
5
|
-
`).filter(c=>c.trim());for(const c of o){const u=c.split(" ");if(u.length<3)continue;const l=u[0]==="-"||u[1]==="-",f=u[0]==="-"?0:parseInt(u[0],10)||0,d=u[1]==="-"?0:parseInt(u[1],10)||0,p=
|
|
6
|
-
`).filter(i=>i.trim());for(const i of
|
|
7
|
-
`)?t:t+1}async function
|
|
8
|
-
`).map(
|
|
9
|
-
`)){const a
|
|
10
|
-
`)){const s=i.split("\0"),
|
|
11
|
-
${s.stderr}`.trim(),
|
|
12
|
-
${s.stderr}`)){const u=await h(
|
|
13
|
-
${s.stderr}`)?new Error(
|
|
14
|
-
`).map(_).filter(c=>c!==null);t.json({ok:!0,stashes:o,count:o.length,workspacePath:s})}catch(s){const n=s;t.status(500).json({error:n.message||"list stash failed"})}});async function K(r,t,e){const{workspacePath:i,ref:s}=r.body||{};if(!i||!String(i).trim()){t.status(400).json({error:"workspacePath is required"});return}try{const n=x.resolve(String(i).trim()),a=s&&String(s).trim()?String(s).trim():"stash@{0}";await h(n,["checkout","--","."]);const o=await h(n,["stash",e,a]);if(o.exitCode!==0){if(o.stdout.includes("CONFLICT")||o.stderr.includes("CONFLICT")){t.json({ok:!1,hasConflict:!0,message:"\u6062\u590D\u65F6\u53D1\u751F\u51B2\u7A81\uFF0C\u8BF7\u624B\u52A8\u89E3\u51B3",details:o.stdout||o.stderr,workspacePath:n});return}if(o.stderr.includes("is not a valid reference")){t.status(404).json({error:`\u5FEB\u7167 ${a} \u4E0D\u5B58\u5728`});return}t.status(400).json({error:o.stderr||`git stash ${e} failed`});return}t.json({ok:!0,stashRef:a,message:e==="apply"?`\u5DF2\u5E94\u7528 ${a}`:`\u5DF2\u6062\u590D ${a}`,preservedStash:e==="apply",workspacePath:n})}catch(n){const a=n;t.status(500).json({error:a.message||`${e} stash failed`})}}S.post("/git/stash/apply",k,async(r,t)=>{await K(r,t,"apply")}),S.post("/git/stash/pop",k,async(r,t)=>{await K(r,t,"pop")}),S.post("/git/stash/drop",k,async(r,t)=>{const{workspacePath:e,ref:i}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}try{const s=x.resolve(String(e).trim()),n=i&&String(i).trim()?String(i).trim():"stash@{0}",a=await h(s,["stash","drop",n]);if(a.exitCode!==0){if(a.stderr.includes("is not a valid reference")){t.status(404).json({error:`\u5FEB\u7167 ${n} \u4E0D\u5B58\u5728`});return}t.status(400).json({error:a.stderr||"git stash drop failed"});return}t.json({ok:!0,stashRef:n,message:`\u5DF2\u5220\u9664 ${n}`,workspacePath:s})}catch(s){const n=s;t.status(500).json({error:n.message||"drop stash failed"})}}),S.post("/git/diff",k,async(r,t)=>{const{workspacePath:e,repositoryPath:i}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}try{const s=await j(String(i||e).trim());if(!s.hasRepository||!s.repositoryRootPath){t.json({ok:!0,isGitRepo:s.isGitRepo,hasRepository:!1,usingParentGitRepo:!1,hasChanges:!1,files:[],workspacePath:s.workspacePath,repositoryRootPath:null,relativeWorkspacePath:""});return}const n=await h(s.repositoryRootPath,A(["diff","--name-status"],s));if(n.exitCode!==0){t.status(400).json({error:n.stderr||"git diff failed"});return}const a=await h(s.repositoryRootPath,A(["diff","--numstat"],s));if(a.exitCode!==0){t.status(400).json({error:a.stderr||"git diff failed"});return}const o=await h(s.repositoryRootPath,A(["diff","--cached","--name-status"],s));if(o.exitCode!==0){t.status(400).json({error:o.stderr||"git staged diff failed"});return}const c=await h(s.repositoryRootPath,A(["diff","--cached","--numstat"],s));if(c.exitCode!==0){t.status(400).json({error:c.stderr||"git staged diff failed"});return}const u=await h(s.repositoryRootPath,A(["status","--porcelain=v1"],s));if(u.exitCode!==0){t.status(400).json({error:u.stderr||"git status failed"});return}const l=wt(u.stdout),f=xt(H(n.stdout,a.stdout,l),H(o.stdout,c.stdout,l,!0)),d=kt(u.stdout);if(d.length>0){const p=new Set(f.map(g=>g.path)),y=await Rt(s.repositoryRootPath,d,p);f.push(...y)}t.json({ok:!0,isGitRepo:s.isGitRepo,hasRepository:!0,usingParentGitRepo:s.usingParentGitRepo,hasChanges:f.length>0,files:f,workspacePath:s.workspacePath,repositoryRootPath:s.repositoryRootPath,relativeWorkspacePath:s.relativeWorkspacePath})}catch(s){const n=s;t.status(500).json({error:n.message||"git diff failed"})}});async function Y(r){const t=[],e=await h(r,["config","user.name"]);(e.exitCode!==0||!e.stdout.trim())&&t.push("-c","user.name=AgentsWork");const i=await h(r,["config","user.email"]);return(i.exitCode!==0||!i.stdout.trim())&&t.push("-c","user.email=agent@agentswork.com"),t}async function Et(r,t,e){if(!t)throw new Error("commit message is required");if(e.length===0)throw new Error("filePaths must be valid workspace-relative paths");const i=e.map(c=>`:(literal)${c}`),s=await h(r,["add","--",...i]);if(s.exitCode!==0)throw new Error(s.stderr||"git add failed");const n=await h(r,["diff","--cached","--quiet","--",...i]);if(n.exitCode===0)throw new Error("\u6240\u9009\u6587\u4EF6\u6CA1\u6709\u53EF\u63D0\u4EA4\u7684\u53D8\u66F4");if(n.exitCode!==1)throw new Error(n.stderr||"git staged diff failed");const a=await Y(r),o=await h(r,[...a,"commit","-m",t,"--",...i]);if(o.exitCode!==0)throw new Error(o.stderr||o.stdout||"git commit failed");return{committedPaths:[...e],output:o.stdout}}S.post("/git/commit",k,async(r,t)=>{const{workspacePath:e,repositoryPath:i,message:s,filePaths:n}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}const a=jt(s);if(!a){t.status(400).json({error:"commit message is required"});return}try{const o=await j(String(i||e).trim());if(!o.hasRepository||!o.repositoryRootPath){t.status(400).json({error:"workspace is not inside a git repository"});return}const c=Array.isArray(n)?n.map(y=>R(String(y||"").trim())).filter(Boolean):[];if(c.length>0){const y=c.map(w=>E(w,o)),g=[];for(const w of y)if(w)try{M(w),g.push(w)}catch{t.status(400).json({error:`unsafe file path: ${w}`});return}if(g.length===0){t.status(400).json({error:"filePaths must be valid workspace-relative paths"});return}try{const w=await Et(o.repositoryRootPath,a,g);t.json({ok:!0,message:"\u63D0\u4EA4\u5B8C\u6210",committedPaths:c,output:w.output,workspacePath:o.workspacePath,repositoryRootPath:o.repositoryRootPath,relativeWorkspacePath:o.relativeWorkspacePath})}catch(w){const P=w;t.status(400).json({error:P.message||"git commit failed"})}return}const u=await Y(o.repositoryRootPath),l=Ct(o),f=await h(o.repositoryRootPath,["add","--all",...l]);if(f.exitCode!==0){t.status(400).json({error:f.stderr||"git add failed"});return}const d=await h(o.repositoryRootPath,["diff","--cached","--quiet",...l]);if(d.exitCode===0){t.status(400).json({error:"\u6CA1\u6709\u53EF\u63D0\u4EA4\u7684\u53D8\u66F4"});return}if(d.exitCode!==1){t.status(400).json({error:d.stderr||"git staged diff failed"});return}const p=await h(o.repositoryRootPath,[...u,"commit","-m",a,...l]);if(p.exitCode!==0){t.status(400).json({error:p.stderr||p.stdout||"git commit failed"});return}t.json({ok:!0,message:"\u63D0\u4EA4\u5B8C\u6210",output:p.stdout,workspacePath:o.workspacePath,repositoryRootPath:o.repositoryRootPath,relativeWorkspacePath:o.relativeWorkspacePath})}catch(o){const c=o;t.status(500).json({error:c.message||"git commit failed"})}}),S.post("/git/add-file",k,async(r,t)=>{const{workspacePath:e,repositoryPath:i,filePath:s}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}const n=R(String(s||"").trim());if(!n){t.status(400).json({error:"filePath is required"});return}try{const a=await j(String(i||e).trim());if(!a.hasRepository||!a.repositoryRootPath){t.status(400).json({error:"workspace is not inside a git repository"});return}const o=await h(a.repositoryRootPath,["add",...G(n,a)]);if(o.exitCode!==0){t.status(400).json({error:o.stderr||"git add file failed"});return}t.json({ok:!0,filePath:n,workspacePath:a.workspacePath,repositoryRootPath:a.repositoryRootPath,relativeWorkspacePath:a.relativeWorkspacePath})}catch(a){const o=a;t.status(500).json({error:o.message||"git add file failed"})}}),S.post("/git/add-files",k,async(r,t)=>{const{workspacePath:e,repositoryPath:i,filePaths:s}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}const a=(Array.isArray(s)?s:[]).map(o=>R(String(o||"").trim())).filter(o=>o.length>0);if(a.length===0){t.status(400).json({error:"filePaths is required and must be a non-empty array"});return}try{const o=await j(String(i||e).trim());if(!o.hasRepository||!o.repositoryRootPath){t.status(400).json({error:"workspace is not inside a git repository"});return}const c=a.flatMap(d=>G(d,o)),u=["add"],l=[];for(let d=0;d<c.length;d++)c[d]!=="--"&&l.push(c[d]);u.push("--",...l);const f=await h(o.repositoryRootPath,u);if(f.exitCode!==0){t.status(400).json({error:f.stderr||"git add files failed"});return}t.json({ok:!0,filePaths:a,workspacePath:o.workspacePath,repositoryRootPath:o.repositoryRootPath,relativeWorkspacePath:o.relativeWorkspacePath})}catch(o){const c=o;t.status(500).json({error:c.message||"git add files failed"})}}),S.post("/git/commits",k,async(r,t)=>{const{workspacePath:e,repositoryPath:i,limit:s,branch:n}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}try{const a=await j(String(i||e).trim());if(!a.hasRepository||!a.repositoryRootPath){t.json({ok:!0,commits:[],currentBranch:"",branchName:null,workspacePath:a.workspacePath,repositoryRootPath:null});return}const o=Number(s),c=!!r.body?.all,u=c?200:100,l=Number.isInteger(o)&&o>0&&o<=u?o:c?100:30,f=await N(a.repositoryRootPath),p=(n&&String(n).trim()?String(n).trim():null)||f||"HEAD",y="%H%x00%h%x00%an%x00%ad%x00%s%x00%P%x00%b%x00";let g;if(c?g=await h(a.repositoryRootPath,["log",`-${l}`,"--all","--topo-order","--date=iso-strict",`--pretty=format:${y}`]):g=await h(a.repositoryRootPath,["log",`-${l}`,"--date=iso-strict",`--pretty=format:${y}`,"--end-of-options",p,"--",a.relativeWorkspacePath||"."]),g.exitCode!==0){t.status(400).json({error:g.stderr||"git log failed"});return}const w=await U(a.repositoryRootPath),P=bt(g.stdout).map(v=>({...v,isRemote:w.has(v.hash)})),C=c?await J(a.repositoryRootPath):void 0;t.json({ok:!0,commits:P,currentBranch:f,branchName:c?null:p,...C?{branches:C}:{},workspacePath:a.workspacePath,repositoryRootPath:a.repositoryRootPath,relativeWorkspacePath:a.relativeWorkspacePath})}catch(a){const o=a;t.status(500).json({error:o.message||"git commits failed"})}}),S.post("/git/branches",k,async(r,t)=>{const{workspacePath:e,repositoryPath:i}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}try{const s=await j(String(i||e).trim());if(!s.hasRepository||!s.repositoryRootPath){t.json({ok:!0,branches:[],currentBranch:"",workspacePath:s.workspacePath,repositoryRootPath:null});return}const n=await J(s.repositoryRootPath),a=await N(s.repositoryRootPath);t.json({ok:!0,branches:n,currentBranch:a,workspacePath:s.workspacePath,repositoryRootPath:s.repositoryRootPath,relativeWorkspacePath:s.relativeWorkspacePath})}catch(s){const n=s;t.status(500).json({error:n.message||"git branches failed"})}}),S.post("/git/push",k,async(r,t)=>{const{workspacePath:e,repositoryPath:i}=r.body||{};if(!e||!String(e).trim()){t.status(400).json({error:"workspacePath is required"});return}try{const s=await j(String(i||e).trim());if(!s.hasRepository||!s.repositoryRootPath){t.status(400).json({error:"workspace is not inside a git repository"});return}const n=await vt(s.repositoryRootPath);t.json({ok:!0,branch:n.branch,pushed:n.pushed,message:n.message,output:n.output,workspacePath:s.workspacePath,repositoryRootPath:s.repositoryRootPath,relativeWorkspacePath:s.relativeWorkspacePath})}catch(s){const n=s;if(n.message&&/上游分支|detached HEAD|分离头指针|不存在|未配置/.test(n.message)){t.status(400).json({error:n.message});return}t.status(500).json({error:n.message||"git push failed"})}});const Gt=/^[0-9a-fA-F]{7,40}$/;function Tt(r){const t=String(r||"").trim();return Gt.test(t)?t:null}async function V(r,t){const e=await N(r);if(!e||e==="HEAD")throw new Error("\u5F53\u524D\u5904\u4E8E detached HEAD\uFF08\u5206\u79BB\u5934\u6307\u9488\uFF09\u72B6\u6001\uFF0C\u65E0\u6CD5\u56DE\u9000");const i=await h(r,["rev-parse","--verify","--quiet",`${t}^{commit}`]);if(i.exitCode!==0||!i.stdout.trim())throw new Error("\u76EE\u6807\u63D0\u4EA4\u4E0D\u5B58\u5728\uFF0C\u8BF7\u5237\u65B0\u63D0\u4EA4\u5217\u8868\u540E\u91CD\u8BD5");const s=i.stdout.trim();if((await h(r,["rev-parse","HEAD"])).stdout.trim()===s)throw new Error("\u76EE\u6807\u63D0\u4EA4\u5DF2\u662F\u5F53\u524D HEAD\uFF0C\u65E0\u9700\u56DE\u9000");if((await h(r,["merge-base","--is-ancestor",s,"HEAD"])).exitCode!==0)throw new Error("\u76EE\u6807\u63D0\u4EA4\u4E0D\u5728\u5F53\u524D\u5206\u652F\u5386\u53F2\u4E2D\uFF0C\u65E0\u6CD5\u56DE\u9000");const o=await h(r,["rev-list","--count",`${s}..HEAD`]),c=parseInt(o.stdout.trim()||"0",10);let u=!1;const l=await U(r);if(l.size>0){const f=await h(r,["rev-list",`${s}..HEAD`]);if(f.exitCode===0){for(const d of f.stdout.split(`
|
|
15
|
-
`))if(l.has(d.trim())){u=!0;break}}}return{resolvedHash:s,branch:
|
|
16
|
-
`).map(
|
|
17
|
-
`),
|
|
18
|
-
`).length;p={stdout:[`diff --git a/${c} b/${c}`,"new file mode 100644","--- /dev/null",`+++ b/${c}`,`@@ -0,0 +1,${
|
|
19
|
-
`),stderr:"",exitCode:0}}}catch{}}
|
|
20
|
-
`);function
|
|
21
|
-
`)}const
|
|
22
|
-
`)[0]?.trim()??"";if(!t||t.length<3)return!1;const
|
|
23
|
-
`).trim();const
|
|
24
|
-
`)[0]?.trim()??"",o=!!t.match(
|
|
25
|
-
`)[0]?.trim()??"";return
|
|
26
|
-
${f.stdout.trim()}`):m.info(`[Git/collectDiff] \u6587\u4EF6\u65E0\u6682\u5B58\u5DEE\u5F02\u6216\u83B7\u53D6\u5931\u8D25: ${u}, exitCode=${f.exitCode}, stdout.length=${f.stdout.length}`)}if(
|
|
1
|
+
import{spawn as it}from"node:child_process";import{promises as j}from"node:fs";import at from"node:os";import x from"node:path";import I from"axios";import{Router as nt}from"express";import{validateToken as k}from"../middleware/auth.js";import{logger as m}from"../utils/logger.js";import{noteDashboardActivity as ct}from"./dashboard.js";const S=nt(),ut=1e4;function lt(){const e=Number(process.env.AWS_RUNTIME_GIT_COMMAND_TIMEOUT_MS);return Number.isFinite(e)&&e>=1e3?e:ut}async function ft(e){let t;try{t=await j.stat(e)}catch(r){const i=r;throw i.code==="ENOENT"?new Error(`workspace path does not exist: ${e}`):new Error(`workspace path is not accessible: ${e}${i.message?` (${i.message})`:""}`)}if(!t.isDirectory())throw new Error(`workspace path is not a directory: ${e}`)}const ht=/^[0-9a-f]{40}$/i,O="%H%x00%h%x00%an%x00%ad%x00%s%x00%P%x00%b%x00",dt=100,pt=200,mt=30,gt=100;function yt(e,t){const r=t?pt:dt,i=t?gt:mt,s=Number(e);return Number.isInteger(s)&&s>0&&s<=r?s:i}function Pt(e){const t=Number(e);return Number.isInteger(t)&&t>0?t:0}function wt(e,t,r){const i=e.length>t,s=i?e.slice(0,t):e;return{page:s,hasMore:i,nextSkip:r+s.length}}const kt=["--branches","--remotes","--tags"];function St(e){const t=["log",`-${e.limit}`,`--skip=${e.skip}`];if(e.graphMode){const r=e.graphHeadRef??null;return[...t,...kt,...r?[r]:[],"--topo-order","--date=iso-strict",`--pretty=format:${O}`]}return[...t,"--date=iso-strict",`--pretty=format:${O}`,"--end-of-options",e.focusedRef,"--",e.relativeWorkspacePath||"."]}function Rt(e,t,r,i){return e==="added"||e==="deleted"||e==="renamed"?!0:i||t>0||r>0}function xt(e){const t=String(e||"").toLowerCase();return t.includes("you do not have the initial commit yet")||t.includes("bad revision")&&t.includes("head")||t.includes("ambiguous argument")&&t.includes("head")}function Ct(){return"\u5F53\u524D Git \u4ED3\u5E93\u8FD8\u6CA1\u6709\u521D\u59CB\u63D0\u4EA4\uFF0C\u65E0\u6CD5\u521B\u5EFA\u5FEB\u7167\u3002\u8BF7\u5148\u63D0\u4EA4\u4E00\u6B21\u521D\u59CB\u63D0\u4EA4\u540E\u518D\u521B\u5EFA\u5FEB\u7167\u3002"}function jt(e){const t=String(e||"").toLowerCase();return t.includes("no local changes to save")||t.includes("no changes to save")||t.includes("\u6CA1\u6709\u672C\u5730\u53D8\u66F4\u9700\u8981\u4FDD\u5B58")}function bt(e){const t=String(e||"").trim();return ht.test(t)?`refs/agentswork/stash-snapshots/${t.toLowerCase()}`:null}function z(e){const t=String(e||"").trim();if(!t)return null;const r=t.split("\0");if(r.length>=3){const n=r[0]?.trim(),o=r[1]?.trim(),c=r[2]?.trim()||o,u=r[3]?.trim()||null;if(!n||!o)return null;const l=B(c||"");return{ref:n,stashName:o,message:l.message,branch:l.branch,createdAt:u}}const i=t.match(/^(stash@\{\d+\})(?::\s*)?(.*)$/);if(!i)return null;const s=i[1],a=B(i[2]?.trim()||s);return{ref:s,stashName:s,message:a.message,branch:a.branch,createdAt:null}}function B(e){const t=String(e||"").trim(),r=t.match(/^(?:On|WIP on)\s+([^:]+):\s*(.*)$/);return r?{branch:r[1].trim(),message:r[2].trim()||t}:{branch:"",message:t}}function q(e){const t=String(e||"").split(`
|
|
2
|
+
`).find(i=>i.trim());if(!t)return null;const r=z(t);return r?{ref:r.ref,stashName:r.stashName||r.ref,message:r.message||r.ref}:null}async function h(e,t,r){return new Promise(i=>{let s=!1,a=null;const n=f=>{s||(s=!0,a&&clearTimeout(a),i(f))},o=it("git",t,{cwd:e,stdio:["ignore","pipe","pipe"]});let c="",u="";o.stdout?.on("data",f=>{c+=String(f||"")}),o.stderr?.on("data",f=>{u+=String(f||"")});const l=r??lt();a=setTimeout(()=>{m.warn(`[runtime-bridge] git command timed out after ${l}ms: git ${t.join(" ")} (cwd=${e})`),o.kill("SIGKILL"),n({stdout:c,stderr:u||`git command timed out after ${l}ms`,exitCode:124})},l),o.on("error",f=>{n({stdout:"",stderr:f.message,exitCode:1})}),o.on("close",f=>{n({stdout:c,stderr:u,exitCode:f??1})})})}const $t=6e4;function vt(){const e=Number(process.env.AWS_RUNTIME_GIT_PUSH_TIMEOUT_MS);return Number.isFinite(e)&&e>=1e3?e:$t}async function U(e,t){const r=bt(t);if(!r)return null;const i=await h(e,["update-ref",r,t]);return i.exitCode!==0?(m.warn(`[Git] \u6301\u4E45\u5316 stash \u5FEB\u7167 ref \u5931\u8D25: ref=${r}, error=${i.stderr||i.stdout}`),null):r}function R(e){return String(e||"").replace(/\\/g,"/").replace(/^\.\//,"").trim()}async function C(e){const t=x.resolve(String(e).trim());await ft(t);const r=await h(t,["rev-parse","--show-toplevel"]);if(r.exitCode!==0)return{workspacePath:t,repositoryRootPath:null,hasRepository:!1,isGitRepo:!1,usingParentGitRepo:!1,relativeWorkspacePath:""};const i=x.resolve(r.stdout.trim()),s=i===t,a=s?"":R(x.relative(i,t));return{workspacePath:t,repositoryRootPath:i,hasRepository:!0,isGitRepo:s,usingParentGitRepo:!s,relativeWorkspacePath:a}}function v(e,t){if(!t.relativeWorkspacePath)return[...e];const r=!e.includes("status");return[...e,...r?[`--relative=${t.relativeWorkspacePath}`]:[],"--",t.relativeWorkspacePath]}function G(e,t){const r=R(e);return t.relativeWorkspacePath?R(x.posix.join(t.relativeWorkspacePath,r)):r}function H(e){const t=R(e);if(x.posix.isAbsolute(t)||t.split("/").some(r=>r===".."||r==="."))throw new Error(`unsafe repository relative path: ${e}`)}function At(e){const t=String(e||"").trim().toUpperCase();return t.startsWith("A")?"added":t.startsWith("D")?"deleted":t.startsWith("R")?"renamed":"modified"}function _(e){const t=R(e);if(!t.includes("=>"))return t;const r=t.match(/^(.*)\{([^{}]+)=>([^{}]+)\}(.*)$/);if(r){const s=r[1],a=r[3],n=r[4];return R(`${s}${a}${n}`)}const i=t.lastIndexOf("=>");return i>=0?R(t.slice(i+2)):t}function Et(e){const t=new Set,r=String(e||"").split(`
|
|
3
|
+
`).filter(i=>i.trim());for(const i of r){const s=i[0]||" ";if(s===" "||s==="?")continue;const a=i.slice(3).trim(),n=a.split(" -> "),o=_(n[n.length-1]||a);o&&t.add(o)}return t}function F(e,t,r=new Set,i=!1){const s=new Map,a=e.split(`
|
|
4
|
+
`).filter(c=>c.trim());for(const c of a){const u=c.split(" ");if(u.length<2)continue;const l=At(u[0]),f=l==="renamed"&&u.length>=3?2:1,d=_(u[f]);d&&s.set(d,l)}const n=[],o=t.split(`
|
|
5
|
+
`).filter(c=>c.trim());for(const c of o){const u=c.split(" ");if(u.length<3)continue;const l=u[0]==="-"||u[1]==="-",f=u[0]==="-"?0:parseInt(u[0],10)||0,d=u[1]==="-"?0:parseInt(u[1],10)||0,p=_(u.slice(2).join(" ").trim()),y=s.get(p)||"modified";!p||!Rt(y,f,d,l)||n.push({path:p,status:y,additions:f,deletions:d,staged:i||r.has(p)})}return n}function Gt(e){const t=[],r=String(e||"").split(`
|
|
6
|
+
`).filter(i=>i.trim());for(const i of r)if(i.startsWith("?? ")){const s=i.slice(3).trim();s&&t.push(s.replace(/\/$/,""))}return t}function Mt(e){if(!e||e.includes("\0"))return 0;let t=0;for(let r=0;r<e.length;r++)e.charCodeAt(r)===10&&t++;return e.endsWith(`
|
|
7
|
+
`)?t:t+1}async function J(e){try{const t=await j.readFile(e,"utf8");return Mt(t)}catch{return 0}}async function Tt(e,t,r,i=10){const s=[];async function a(n,o){if(o>i)return;const c=x.join(e,n);let u;try{u=await j.readdir(c,{withFileTypes:!0})}catch{return}for(const l of u){if(l.name===".git"||l.name==="node_modules")continue;const f=n?`${n}/${l.name}`:l.name;if(l.isDirectory())await a(f,o+1);else if(l.isFile()){const d=R(f);d&&!r.has(d)&&s.push({path:d,status:"added",additions:await J(x.join(c,l.name)),deletions:0,staged:!1})}}}for(const n of t){const o=R(n);if(!o)continue;const c=x.join(e,n);let u;try{u=await j.stat(c)}catch{continue}u.isFile()?r.has(o)||s.push({path:o,status:"added",additions:await J(c),deletions:0,staged:!1}):u.isDirectory()&&await a(o,0)}return s}function Dt(e,t){const r=new Map;for(const i of e)r.set(i.path,{...i});for(const i of t){const s=r.get(i.path);if(!s){r.set(i.path,{...i,staged:!0});continue}r.set(i.path,{...s,status:s.status==="modified"?i.status:s.status,additions:s.additions+i.additions,deletions:s.deletions+i.deletions,staged:!0})}return[...r.values()]}function K(e){return String(e||"").trim()}function Ht(e){return String(e||"").trim()}function _t(e){return["--",e.relativeWorkspacePath||"."]}function T(e,t){return["--",G(e,t)]}function Ft(e){const t=e.split("\0"),r=[];for(let i=0;i+6<t.length;i+=7){const s=t[i]?.trim();if(!s)continue;const a=(t[i+5]?.trim()||"").split(/\s+/).filter(Boolean);r.push({hash:s,shortHash:t[i+1]?.trim()||s.slice(0,8),authorName:t[i+2]?.trim()||"unknown",authorDate:t[i+3]?.trim()||"",subject:t[i+4]?.trim()||s,body:t[i+6]?.trim()||"",parents:a})}return r}async function Y(e){const t=new Set,r=await h(e,["for-each-ref","--format=%(refname)","refs/remotes"]);if(r.exitCode!==0)return t;const i=r.stdout.split(`
|
|
8
|
+
`).map(a=>a.trim()).filter(Boolean);if(i.length===0)return t;const s=await h(e,["rev-list",...i]);if(s.exitCode!==0)return t;for(const a of s.stdout.split(`
|
|
9
|
+
`)){const n=a.trim();n&&t.add(n)}return t}async function N(e){const t=await h(e,["rev-parse","--abbrev-ref","HEAD"]);return t.exitCode!==0||!t.stdout.trim()?"":t.stdout.trim()}async function V(e){const t=await h(e,["for-each-ref","--format=%(refname)%00%(objectname)%00%(HEAD)%00%(upstream:short)","refs/heads","refs/remotes"]);if(t.exitCode!==0)return[];const r=[];for(const i of t.stdout.split(`
|
|
10
|
+
`)){const s=i.split("\0"),a=s[0]?.trim(),n=s[1]?.trim();if(!a||!n)continue;const o=a.startsWith("refs/remotes/"),c=o?a.slice(13):a.slice(11);r.push({name:c,ref:a,hash:n,isCurrent:s[2]?.trim()==="*",isRemote:o,upstream:s[3]?.trim()||""})}return r}function Nt(e,t){return["diff",t?e:`${e}^!`]}async function Wt(e){const t=await h(e,["rev-parse","--abbrev-ref","HEAD"]),r=t.stdout.trim();if(t.exitCode!==0||!r||r==="HEAD")throw new Error("\u5F53\u524D\u5904\u4E8E detached HEAD\uFF08\u5206\u79BB\u5934\u6307\u9488\uFF09\u72B6\u6001\uFF0C\u65E0\u6CD5\u63A8\u9001");const i=await h(e,["rev-parse","--abbrev-ref","--symbolic-full-name","@{u}"]);if(i.exitCode!==0||!i.stdout.trim())throw new Error(`\u5206\u652F ${r} \u672A\u914D\u7F6E\u4E0A\u6E38\u5206\u652F\uFF0C\u65E0\u6CD5\u63A8\u9001\u3002\u8BF7\u5148\u624B\u52A8\u6267\u884C\u4E00\u6B21 git push -u origin ${r} \u8BBE\u7F6E\u4E0A\u6E38\u5206\u652F`);const s=await h(e,["push"],vt());if(s.exitCode!==0)throw new Error(s.stderr||s.stdout||"git push failed");const a=`${s.stdout}
|
|
11
|
+
${s.stderr}`.trim(),n=/everything up-to-date|up to date/i.test(a);return{branch:r,pushed:!n,message:n?"\u6CA1\u6709\u9700\u8981\u63A8\u9001\u7684\u63D0\u4EA4":"\u63A8\u9001\u5B8C\u6210",output:a}}S.post("/git/check",k,async(e,t)=>{const{workspacePath:r}=e.body||{};if(!r||!String(r).trim()){t.status(400).json({error:"workspacePath is required"});return}try{const i=await C(String(r).trim());t.json({ok:!0,isGitRepo:i.isGitRepo,hasRepository:i.hasRepository,usingParentGitRepo:i.usingParentGitRepo,workspacePath:i.workspacePath,repositoryRootPath:i.repositoryRootPath,relativeWorkspacePath:i.relativeWorkspacePath});return}catch(i){const s=i;t.status(500).json({error:s.message||"check git repo failed"})}});async function Lt(e,t){const r=`\u5FEB\u7167\uFF08${new Date().toISOString()}\uFF09`,i=t&&String(t).trim()?`\u5FEB\u7167: ${String(t).trim()}`:r,s=await h(e,["stash","push","-m",i]);if(jt(`${s.stdout}
|
|
12
|
+
${s.stderr}`)){const u=await h(e,["stash","list","-n","1","--format=%H%x00%gd%x00%gs%x00%cr"]),l=u.exitCode===0?q(u.stdout):null;return{stashRef:(l?await U(e,l.ref):null)??l?.ref??null,stashName:l?.stashName??null,noChanges:!0,reusedLatestStash:!!l,message:l?`\u6CA1\u6709\u672C\u5730\u53D8\u66F4\u9700\u8981\u4FDD\u5B58\uFF0C\u5DF2\u590D\u7528\u6700\u8FD1\u5FEB\u7167 ${l.stashName}`:"\u6CA1\u6709\u672C\u5730\u53D8\u66F4\u9700\u8981\u4FDD\u5B58"}}if(s.exitCode!==0)throw xt(`${s.stdout}
|
|
13
|
+
${s.stderr}`)?new Error(Ct()):new Error(s.stderr||"git stash push failed");const a=await h(e,["stash","list","-n","1","--format=%H%x00%gd%x00%gs%x00%cr"]),n=a.exitCode===0?q(a.stdout):null;return{stashRef:(n?await U(e,n.ref):null)??n?.ref??"stash@{0}",stashName:n?.stashName??null,noChanges:!1,reusedLatestStash:!1,message:i}}S.post("/git/stash/create",k,async(e,t)=>{const{workspacePath:r,message:i}=e.body||{};if(!r||!String(r).trim()){t.status(400).json({error:"workspacePath is required"});return}try{const s=x.resolve(String(r).trim()),a=await Lt(s,i?String(i):void 0);t.json({ok:!0,stashRef:a.stashRef,stashName:a.stashName,message:a.message,noChanges:a.noChanges,reusedLatestStash:a.reusedLatestStash,workspacePath:s})}catch(s){const a=s;t.status(400).json({error:a.message||"create stash failed"})}}),S.post("/git/stash/list",k,async(e,t)=>{const{workspacePath:r,limit:i=20}=e.body||{};if(!r||!String(r).trim()){t.status(400).json({error:"workspacePath is required"});return}try{const s=x.resolve(String(r).trim()),a=Math.min(Math.max(1,Number(i)||20),100),n=await h(s,["stash","list","-n",String(a),"--format=%H%x00%gd%x00%gs%x00%cr"]);if(n.exitCode!==0){t.status(400).json({error:n.stderr||"git stash list failed"});return}const o=n.stdout.split(`
|
|
14
|
+
`).map(z).filter(c=>c!==null);t.json({ok:!0,stashes:o,count:o.length,workspacePath:s})}catch(s){const a=s;t.status(500).json({error:a.message||"list stash failed"})}});async function Z(e,t,r){const{workspacePath:i,ref:s}=e.body||{};if(!i||!String(i).trim()){t.status(400).json({error:"workspacePath is required"});return}try{const a=x.resolve(String(i).trim()),n=s&&String(s).trim()?String(s).trim():"stash@{0}";await h(a,["checkout","--","."]);const o=await h(a,["stash",r,n]);if(o.exitCode!==0){if(o.stdout.includes("CONFLICT")||o.stderr.includes("CONFLICT")){t.json({ok:!1,hasConflict:!0,message:"\u6062\u590D\u65F6\u53D1\u751F\u51B2\u7A81\uFF0C\u8BF7\u624B\u52A8\u89E3\u51B3",details:o.stdout||o.stderr,workspacePath:a});return}if(o.stderr.includes("is not a valid reference")){t.status(404).json({error:`\u5FEB\u7167 ${n} \u4E0D\u5B58\u5728`});return}t.status(400).json({error:o.stderr||`git stash ${r} failed`});return}t.json({ok:!0,stashRef:n,message:r==="apply"?`\u5DF2\u5E94\u7528 ${n}`:`\u5DF2\u6062\u590D ${n}`,preservedStash:r==="apply",workspacePath:a})}catch(a){const n=a;t.status(500).json({error:n.message||`${r} stash failed`})}}S.post("/git/stash/apply",k,async(e,t)=>{await Z(e,t,"apply")}),S.post("/git/stash/pop",k,async(e,t)=>{await Z(e,t,"pop")}),S.post("/git/stash/drop",k,async(e,t)=>{const{workspacePath:r,ref:i}=e.body||{};if(!r||!String(r).trim()){t.status(400).json({error:"workspacePath is required"});return}try{const s=x.resolve(String(r).trim()),a=i&&String(i).trim()?String(i).trim():"stash@{0}",n=await h(s,["stash","drop",a]);if(n.exitCode!==0){if(n.stderr.includes("is not a valid reference")){t.status(404).json({error:`\u5FEB\u7167 ${a} \u4E0D\u5B58\u5728`});return}t.status(400).json({error:n.stderr||"git stash drop failed"});return}t.json({ok:!0,stashRef:a,message:`\u5DF2\u5220\u9664 ${a}`,workspacePath:s})}catch(s){const a=s;t.status(500).json({error:a.message||"drop stash failed"})}}),S.post("/git/diff",k,async(e,t)=>{const{workspacePath:r,repositoryPath:i}=e.body||{};if(!r||!String(r).trim()){t.status(400).json({error:"workspacePath is required"});return}try{const s=await C(String(i||r).trim());if(!s.hasRepository||!s.repositoryRootPath){t.json({ok:!0,isGitRepo:s.isGitRepo,hasRepository:!1,usingParentGitRepo:!1,hasChanges:!1,files:[],workspacePath:s.workspacePath,repositoryRootPath:null,relativeWorkspacePath:""});return}const a=await h(s.repositoryRootPath,v(["diff","--name-status"],s));if(a.exitCode!==0){t.status(400).json({error:a.stderr||"git diff failed"});return}const n=await h(s.repositoryRootPath,v(["diff","--numstat"],s));if(n.exitCode!==0){t.status(400).json({error:n.stderr||"git diff failed"});return}const o=await h(s.repositoryRootPath,v(["diff","--cached","--name-status"],s));if(o.exitCode!==0){t.status(400).json({error:o.stderr||"git staged diff failed"});return}const c=await h(s.repositoryRootPath,v(["diff","--cached","--numstat"],s));if(c.exitCode!==0){t.status(400).json({error:c.stderr||"git staged diff failed"});return}const u=await h(s.repositoryRootPath,v(["status","--porcelain=v1"],s));if(u.exitCode!==0){t.status(400).json({error:u.stderr||"git status failed"});return}const l=Et(u.stdout),f=Dt(F(a.stdout,n.stdout,l),F(o.stdout,c.stdout,l,!0)),d=Gt(u.stdout);if(d.length>0){const p=new Set(f.map(g=>g.path)),y=await Tt(s.repositoryRootPath,d,p);f.push(...y)}t.json({ok:!0,isGitRepo:s.isGitRepo,hasRepository:!0,usingParentGitRepo:s.usingParentGitRepo,hasChanges:f.length>0,files:f,workspacePath:s.workspacePath,repositoryRootPath:s.repositoryRootPath,relativeWorkspacePath:s.relativeWorkspacePath})}catch(s){const a=s;t.status(500).json({error:a.message||"git diff failed"})}});async function X(e){const t=[],r=await h(e,["config","user.name"]);(r.exitCode!==0||!r.stdout.trim())&&t.push("-c","user.name=AgentsWork");const i=await h(e,["config","user.email"]);return(i.exitCode!==0||!i.stdout.trim())&&t.push("-c","user.email=agent@agentswork.com"),t}async function It(e,t,r){if(!t)throw new Error("commit message is required");if(r.length===0)throw new Error("filePaths must be valid workspace-relative paths");const i=r.map(c=>`:(literal)${c}`),s=await h(e,["add","--",...i]);if(s.exitCode!==0)throw new Error(s.stderr||"git add failed");const a=await h(e,["diff","--cached","--quiet","--",...i]);if(a.exitCode===0)throw new Error("\u6240\u9009\u6587\u4EF6\u6CA1\u6709\u53EF\u63D0\u4EA4\u7684\u53D8\u66F4");if(a.exitCode!==1)throw new Error(a.stderr||"git staged diff failed");const n=await X(e),o=await h(e,[...n,"commit","-m",t,"--",...i]);if(o.exitCode!==0)throw new Error(o.stderr||o.stdout||"git commit failed");return{committedPaths:[...r],output:o.stdout}}S.post("/git/commit",k,async(e,t)=>{const{workspacePath:r,repositoryPath:i,message:s,filePaths:a}=e.body||{};if(!r||!String(r).trim()){t.status(400).json({error:"workspacePath is required"});return}const n=Ht(s);if(!n){t.status(400).json({error:"commit message is required"});return}try{const o=await C(String(i||r).trim());if(!o.hasRepository||!o.repositoryRootPath){t.status(400).json({error:"workspace is not inside a git repository"});return}const c=Array.isArray(a)?a.map(y=>R(String(y||"").trim())).filter(Boolean):[];if(c.length>0){const y=c.map(w=>G(w,o)),g=[];for(const w of y)if(w)try{H(w),g.push(w)}catch{t.status(400).json({error:`unsafe file path: ${w}`});return}if(g.length===0){t.status(400).json({error:"filePaths must be valid workspace-relative paths"});return}try{const w=await It(o.repositoryRootPath,n,g);t.json({ok:!0,message:"\u63D0\u4EA4\u5B8C\u6210",committedPaths:c,output:w.output,workspacePath:o.workspacePath,repositoryRootPath:o.repositoryRootPath,relativeWorkspacePath:o.relativeWorkspacePath})}catch(w){const P=w;t.status(400).json({error:P.message||"git commit failed"})}return}const u=await X(o.repositoryRootPath),l=_t(o),f=await h(o.repositoryRootPath,["add","--all",...l]);if(f.exitCode!==0){t.status(400).json({error:f.stderr||"git add failed"});return}const d=await h(o.repositoryRootPath,["diff","--cached","--quiet",...l]);if(d.exitCode===0){t.status(400).json({error:"\u6CA1\u6709\u53EF\u63D0\u4EA4\u7684\u53D8\u66F4"});return}if(d.exitCode!==1){t.status(400).json({error:d.stderr||"git staged diff failed"});return}const p=await h(o.repositoryRootPath,[...u,"commit","-m",n,...l]);if(p.exitCode!==0){t.status(400).json({error:p.stderr||p.stdout||"git commit failed"});return}t.json({ok:!0,message:"\u63D0\u4EA4\u5B8C\u6210",output:p.stdout,workspacePath:o.workspacePath,repositoryRootPath:o.repositoryRootPath,relativeWorkspacePath:o.relativeWorkspacePath})}catch(o){const c=o;t.status(500).json({error:c.message||"git commit failed"})}}),S.post("/git/add-file",k,async(e,t)=>{const{workspacePath:r,repositoryPath:i,filePath:s}=e.body||{};if(!r||!String(r).trim()){t.status(400).json({error:"workspacePath is required"});return}const a=R(String(s||"").trim());if(!a){t.status(400).json({error:"filePath is required"});return}try{const n=await C(String(i||r).trim());if(!n.hasRepository||!n.repositoryRootPath){t.status(400).json({error:"workspace is not inside a git repository"});return}const o=await h(n.repositoryRootPath,["add",...T(a,n)]);if(o.exitCode!==0){t.status(400).json({error:o.stderr||"git add file failed"});return}t.json({ok:!0,filePath:a,workspacePath:n.workspacePath,repositoryRootPath:n.repositoryRootPath,relativeWorkspacePath:n.relativeWorkspacePath})}catch(n){const o=n;t.status(500).json({error:o.message||"git add file failed"})}}),S.post("/git/add-files",k,async(e,t)=>{const{workspacePath:r,repositoryPath:i,filePaths:s}=e.body||{};if(!r||!String(r).trim()){t.status(400).json({error:"workspacePath is required"});return}const n=(Array.isArray(s)?s:[]).map(o=>R(String(o||"").trim())).filter(o=>o.length>0);if(n.length===0){t.status(400).json({error:"filePaths is required and must be a non-empty array"});return}try{const o=await C(String(i||r).trim());if(!o.hasRepository||!o.repositoryRootPath){t.status(400).json({error:"workspace is not inside a git repository"});return}const c=n.flatMap(d=>T(d,o)),u=["add"],l=[];for(let d=0;d<c.length;d++)c[d]!=="--"&&l.push(c[d]);u.push("--",...l);const f=await h(o.repositoryRootPath,u);if(f.exitCode!==0){t.status(400).json({error:f.stderr||"git add files failed"});return}t.json({ok:!0,filePaths:n,workspacePath:o.workspacePath,repositoryRootPath:o.repositoryRootPath,relativeWorkspacePath:o.relativeWorkspacePath})}catch(o){const c=o;t.status(500).json({error:c.message||"git add files failed"})}}),S.post("/git/commits",k,async(e,t)=>{const{workspacePath:r,repositoryPath:i,limit:s,skip:a,branch:n}=e.body||{};if(!r||!String(r).trim()){t.status(400).json({error:"workspacePath is required"});return}try{const o=await C(String(i||r).trim());if(!o.hasRepository||!o.repositoryRootPath){t.json({ok:!0,commits:[],hasMore:!1,nextSkip:0,currentBranch:"",branchName:null,workspacePath:o.workspacePath,repositoryRootPath:null});return}const c=!!e.body?.all,u=yt(s,c),l=Pt(a),f=await N(o.repositoryRootPath),p=(n&&String(n).trim()?String(n).trim():null)||f||"HEAD",y=c&&f==="HEAD"?"HEAD":null,g=await h(o.repositoryRootPath,St({limit:u+1,skip:l,graphMode:c,focusedRef:p,graphHeadRef:y,relativeWorkspacePath:o.relativeWorkspacePath}));if(g.exitCode!==0){t.status(400).json({error:g.stderr||"git log failed"});return}const{page:w,hasMore:P,nextSkip:b}=wt(Ft(g.stdout),u,l),A=await Y(o.repositoryRootPath),M=w.map(L=>({...L,isRemote:A.has(L.hash)})),E=c?await V(o.repositoryRootPath):void 0;m.debug(`[Git/commits] page: graphMode=${c}, ref=${c?"branches+remotes+tags":p}, skip=${l}, limit=${u}, returned=${M.length}, hasMore=${P}`),t.json({ok:!0,commits:M,hasMore:P,nextSkip:b,currentBranch:f,branchName:c?null:p,...E?{branches:E}:{},workspacePath:o.workspacePath,repositoryRootPath:o.repositoryRootPath,relativeWorkspacePath:o.relativeWorkspacePath})}catch(o){const c=o;t.status(500).json({error:c.message||"git commits failed"})}}),S.post("/git/branches",k,async(e,t)=>{const{workspacePath:r,repositoryPath:i}=e.body||{};if(!r||!String(r).trim()){t.status(400).json({error:"workspacePath is required"});return}try{const s=await C(String(i||r).trim());if(!s.hasRepository||!s.repositoryRootPath){t.json({ok:!0,branches:[],currentBranch:"",workspacePath:s.workspacePath,repositoryRootPath:null});return}const a=await V(s.repositoryRootPath),n=await N(s.repositoryRootPath);t.json({ok:!0,branches:a,currentBranch:n,workspacePath:s.workspacePath,repositoryRootPath:s.repositoryRootPath,relativeWorkspacePath:s.relativeWorkspacePath})}catch(s){const a=s;t.status(500).json({error:a.message||"git branches failed"})}}),S.post("/git/push",k,async(e,t)=>{const{workspacePath:r,repositoryPath:i}=e.body||{};if(!r||!String(r).trim()){t.status(400).json({error:"workspacePath is required"});return}try{const s=await C(String(i||r).trim());if(!s.hasRepository||!s.repositoryRootPath){t.status(400).json({error:"workspace is not inside a git repository"});return}const a=await Wt(s.repositoryRootPath);t.json({ok:!0,branch:a.branch,pushed:a.pushed,message:a.message,output:a.output,workspacePath:s.workspacePath,repositoryRootPath:s.repositoryRootPath,relativeWorkspacePath:s.relativeWorkspacePath})}catch(s){const a=s;if(a.message&&/上游分支|detached HEAD|分离头指针|不存在|未配置/.test(a.message)){t.status(400).json({error:a.message});return}t.status(500).json({error:a.message||"git push failed"})}});const Ot=/^[0-9a-fA-F]{7,40}$/;function zt(e){const t=String(e||"").trim();return Ot.test(t)?t:null}async function Q(e,t){const r=await N(e);if(!r||r==="HEAD")throw new Error("\u5F53\u524D\u5904\u4E8E detached HEAD\uFF08\u5206\u79BB\u5934\u6307\u9488\uFF09\u72B6\u6001\uFF0C\u65E0\u6CD5\u56DE\u9000");const i=await h(e,["rev-parse","--verify","--quiet",`${t}^{commit}`]);if(i.exitCode!==0||!i.stdout.trim())throw new Error("\u76EE\u6807\u63D0\u4EA4\u4E0D\u5B58\u5728\uFF0C\u8BF7\u5237\u65B0\u63D0\u4EA4\u5217\u8868\u540E\u91CD\u8BD5");const s=i.stdout.trim();if((await h(e,["rev-parse","HEAD"])).stdout.trim()===s)throw new Error("\u76EE\u6807\u63D0\u4EA4\u5DF2\u662F\u5F53\u524D HEAD\uFF0C\u65E0\u9700\u56DE\u9000");if((await h(e,["merge-base","--is-ancestor",s,"HEAD"])).exitCode!==0)throw new Error("\u76EE\u6807\u63D0\u4EA4\u4E0D\u5728\u5F53\u524D\u5206\u652F\u5386\u53F2\u4E2D\uFF0C\u65E0\u6CD5\u56DE\u9000");const o=await h(e,["rev-list","--count",`${s}..HEAD`]),c=parseInt(o.stdout.trim()||"0",10);let u=!1;const l=await Y(e);if(l.size>0){const f=await h(e,["rev-list",`${s}..HEAD`]);if(f.exitCode===0){for(const d of f.stdout.split(`
|
|
15
|
+
`))if(l.has(d.trim())){u=!0;break}}}return{resolvedHash:s,branch:r,resetCount:c,affectsRemote:u}}async function tt(e){const t=await h(e,["status","--porcelain"]);return t.exitCode===0&&!!t.stdout.trim()}async function Bt(e,t,r){const i=await Q(e,t),s=await tt(e);return{mode:r,commitHash:i.resolvedHash,branch:i.branch,resetCount:i.resetCount,hasLocalChanges:s,affectsRemote:i.affectsRemote}}async function qt(e,t,r,i=""){const s=await Q(e,t),a=await tt(e),n=r==="hard"?["reset","--hard",s.resolvedHash]:["reset","--soft",s.resolvedHash],o=await h(e,n);if(o.exitCode!==0)throw new Error(o.stderr||o.stdout||"git reset \u6267\u884C\u5931\u8D25");if(r==="hard"){const c=["clean","-fd"];i&&c.push("--",i);const u=await h(e,c);u.exitCode!==0&&m.warn(`[Git] \u786C\u56DE\u9000\u540E\u6E05\u7406\u672A\u8DDF\u8E2A\u6587\u4EF6\u5931\u8D25: cwd=${e}, scope=${i||"(\u6574\u4ED3)"}, error=${u.stderr||u.stdout}`)}return{mode:r,commitHash:s.resolvedHash,branch:s.branch,resetCount:s.resetCount,hasLocalChanges:a,affectsRemote:s.affectsRemote}}S.post("/git/reset",k,async(e,t)=>{const{workspacePath:r,repositoryPath:i,commitHash:s,mode:a,preview:n}=e.body||{};if(!r||!String(r).trim()){t.status(400).json({error:"workspacePath is required"});return}const o=zt(s);if(!o){t.status(400).json({error:"commitHash \u683C\u5F0F\u975E\u6CD5\uFF0C\u65E0\u6CD5\u56DE\u9000"});return}const c=String(a||"").trim();if(c!=="soft"&&c!=="hard"){t.status(400).json({error:"mode must be soft or hard"});return}const u=n===!0||n==="true";try{const l=await C(String(i||r).trim());if(!l.hasRepository||!l.repositoryRootPath){t.status(400).json({error:"workspace is not inside a git repository"});return}if(u){const y=await Bt(l.repositoryRootPath,o,c);t.json({ok:!0,preview:!0,...y,workspacePath:l.workspacePath,repositoryRootPath:l.repositoryRootPath,relativeWorkspacePath:l.relativeWorkspacePath});return}const f=await qt(l.repositoryRootPath,o,c,l.relativeWorkspacePath),d=f.commitHash.slice(0,7),p=f.mode==="hard"?`\u5DF2\u786C\u56DE\u9000\u5230 ${d}\uFF0C\u5171\u56DE\u9000 ${f.resetCount} \u4E2A\u63D0\u4EA4\uFF0C\u5DE5\u4F5C\u533A\u5DF2\u5B8C\u5168\u6062\u590D\u5230\u8BE5\u63D0\u4EA4\u72B6\u6001`:`\u5DF2\u8F6F\u56DE\u9000\u5230 ${d}\uFF0C\u5171\u56DE\u9000 ${f.resetCount} \u4E2A\u63D0\u4EA4\uFF0C\u5DE5\u4F5C\u533A\u6587\u4EF6\u4FDD\u6301\u4E0D\u53D8`;t.json({ok:!0,...f,message:p,workspacePath:l.workspacePath,repositoryRootPath:l.repositoryRootPath,relativeWorkspacePath:l.relativeWorkspacePath})}catch(l){const f=l;if(f.message&&/回退|快照|detached HEAD|分离头指针|不存在|无需|HEAD/.test(f.message)){t.status(400).json({error:f.message});return}t.status(500).json({error:f.message||"git reset failed"})}}),S.post("/git/commit-diff",k,async(e,t)=>{const{workspacePath:r,repositoryPath:i,commitHash:s,aggregate:a}=e.body||{};if(!r||!String(r).trim()){t.status(400).json({error:"workspacePath is required"});return}const n=K(s);if(!n){t.status(400).json({error:"commitHash is required"});return}const o=a===!0||a==="true";try{const c=await C(String(i||r).trim());if(!c.hasRepository||!c.repositoryRootPath){t.status(400).json({error:"workspace is not inside a git repository"});return}const u=Nt(n,o),l=await h(c.repositoryRootPath,v([...u,"--name-status"],c));if(l.exitCode!==0){t.status(400).json({error:l.stderr||"git commit diff failed"});return}const f=await h(c.repositoryRootPath,v([...u,"--numstat"],c));if(f.exitCode!==0){t.status(400).json({error:f.stderr||"git commit diff failed"});return}const d=F(l.stdout,f.stdout);t.json({ok:!0,commitHash:n,isGitRepo:c.isGitRepo,hasRepository:!0,usingParentGitRepo:c.usingParentGitRepo,hasChanges:d.length>0,files:d,workspacePath:c.workspacePath,repositoryRootPath:c.repositoryRootPath,relativeWorkspacePath:c.relativeWorkspacePath})}catch(c){const u=c;t.status(500).json({error:u.message||"git commit diff failed"})}}),S.post("/git/diff-file",k,async(e,t)=>{const{workspacePath:r,filePath:i,commitHash:s,aggregate:a}=e.body||{};if(!r||!String(r).trim()){t.status(400).json({error:"workspacePath is required"});return}if(!i||!String(i).trim()){t.status(400).json({error:"filePath is required"});return}try{const n=await C(String(r).trim()),o=R(String(i).trim());if(!n.hasRepository||!n.repositoryRootPath){t.status(400).json({error:"workspace is not inside a git repository"});return}const c=G(o,n),u=K(s),l=!!(u&&(a===!0||a==="true")),f=!u||l,d=u?l?["diff",u,"--",c]:["show","--format=","--no-ext-diff",u,"--",c]:["diff","HEAD","--",c];let p=await h(n.repositoryRootPath,d);if(f&&p.exitCode!==0&&(p=await h(n.repositoryRootPath,["diff","--",c])),p.exitCode!==0){t.status(400).json({error:p.stderr||"git diff file failed"});return}if(!u&&!p.stdout.trim()){const g=await h(n.repositoryRootPath,["diff","--cached","--",c]);if(g.exitCode!==0){t.status(400).json({error:g.stderr||"git staged diff file failed"});return}p=g}if(f&&!p.stdout.trim()){const g=x.join(n.repositoryRootPath,c);try{const w=await j.readFile(g,"utf-8");if(w){const P=w.split(`
|
|
16
|
+
`).map(A=>`+${A}`).join(`
|
|
17
|
+
`),b=w.split(`
|
|
18
|
+
`).length;p={stdout:[`diff --git a/${c} b/${c}`,"new file mode 100644","--- /dev/null",`+++ b/${c}`,`@@ -0,0 +1,${b} @@`,P].join(`
|
|
19
|
+
`),stderr:"",exitCode:0}}}catch{}}H(c);let y="";if(f){const g=x.join(n.repositoryRootPath,c);try{y=await j.readFile(g,"utf-8")}catch{}}else{const g=await h(n.repositoryRootPath,["show",`${u}:${c}`]);g.exitCode===0&&(y=g.stdout)}t.json({ok:!0,filePath:o,repositoryRelativeFilePath:c,commitHash:u||null,rangeFromCommit:l,diffContent:p.stdout,fileContent:y,workspacePath:n.workspacePath,repositoryRootPath:n.repositoryRootPath,usingParentGitRepo:n.usingParentGitRepo})}catch(n){const o=n;t.status(500).json({error:o.message||"git diff file failed"})}});const et=["STRICTLY act as a Git commit message generator. NEVER explain, narrate, or describe your task.",'NEVER start your reply with phrases like "\u6211\u4EEC\u88AB\u95EE\u5230", "\u4EE5\u4E0B\u662F", "\u597D\u7684", "Sure", "Here is", "I will", "I can", "Let me", "\u6839\u636E", "\u4E0B\u9762".',"NEVER include any meta-language or thinking out loud. Output ONLY the commit message text.","","Format (Conventional Commits, 1 line, optional body):"," <type>(<scope>): <description>","","Allowed types: feat, fix, refactor, test, docs, style, chore, perf, ci, build, revert.","First line MUST be <= 72 characters. <description> MUST be in Chinese.","Add a body (separated by a blank line) ONLY when the change needs more context."].join(`
|
|
20
|
+
`);function rt(e){return["\u8BF7\u76F4\u63A5\u8F93\u51FA\u4E0A\u8FF0\u683C\u5F0F\u7684\u63D0\u4EA4\u4FE1\u606F,\u4E0D\u8981\u4EFB\u4F55\u989D\u5916\u6587\u5B57\u3002","\u4EE5\u4E0B\u662F\u6682\u5B58\u533A diff:","",e].join(`
|
|
21
|
+
`)}const st=/\b(?:feat|fix|chore|docs|style|refactor|perf|test|build|ci|revert)(?:\s*\([^)]*\))?\s*:/i,Ut=/^(?:feat|fix|chore|docs|style|refactor|perf|test|build|ci|revert)\s*:/i;function Jt(e){const t=e.split(`
|
|
22
|
+
`)[0]?.trim()??"";if(!t||t.length<3)return!1;const r=(t.match(/[\u4e00-\u9fa5]/g)||[]).length,i=(t.match(/[a-zA-Z]{2,}/g)||[]).length;if(r===0&&i===0)return!1;const s=[/^以下是/,/^上面是/,/^生成的/,/^这是/,/^下面/,/^(?:Here|This|This is|That is)/i];for(const a of s)if(a.test(t))return!1;return r>=2||i>=2}const Kt=[/^\s*(?:我们|我)\s*(?:被\s*)?(?:问[到过]?|需要|要|将|来|先)[^.:\n]{0,60}?(?:提交信息|commit\s*message|[::,,。])/i,/^\s*(?:好的|好)\s*[,,::]/i,/^\s*(?:好的|好)\s*(?:以下是|上面是|提供|生成|给出)[^.:\n]{0,20}?[::]?/i,/^\s*是\s*[::]/i,/^\s*(?:Sure|Here(?:'s|\s+is)|We(?:'re| are)|I\s+(?:will|can|'ll)|Let\s+me|Certainly|Of\s+course)[^.:\n]{0,40}?[,,::.]/i,/^\s*根据\s*(?:您|你|您所|你所|所)?\s*(?:提供|上述|给[我您你])?\s*(?:的)?\s*diff\s*[,,::]?/i,/^\s*(?:以下|下面)[^,,::\n]{0,30}?提交信息\s*[,,::]?/i,/^\s*(?:以下|下面)\s*是\s*[,,::]/i,/^\s*生成\s*(?:一条|一个|了)?\s*(?:符合|新的)?\s*(?:conventional\s*commit|git)?\s*提交信息\s*[,,::]?/i,/^\s*```(?:text|markdown|bash|shell)?\s*\n?/i];function D(e){if(!e)return m.info("[Git/sanitize] \u8F93\u5165\u4E3A\u7A7A, \u8FD4\u56DE fallback"),"chore: update staged changes";let t=e.replace(/\r\n/g,`
|
|
23
|
+
`).trim();const r=JSON.stringify(e.substring(0,120)),i=t.match(st);if(i&&i.index!==void 0&&i.index<=80)t=t.slice(i.index);else{for(let f=0;f<5;f+=1){const d=t;for(const p of Kt)t=t.replace(p,"");if(t=t.replace(/^[\s*\-`,,']+/,"").trim(),t===d)break}const n=t.split(`
|
|
24
|
+
`)[0]?.trim()??"",o=!!t.match(st),c=!!t.match(Ut),u=Jt(t),l=n.length>100;if(!o&&!c&&!u&&!l)return m.info(`[Git/sanitize] \u5265\u79BB\u540E\u65E0\u6709\u6548\u5185\u5BB9, \u8FD4\u56DE fallback. raw=${r}, afterStrip=${JSON.stringify(t.substring(0,100))}`),"chore: update staged changes";!o&&(c||u||l)&&(t=n||t)}const s=t.search(/\n\s*\n/);if(s>0&&(t=t.slice(0,s)),t=t.replace(/\n+\s*```\s*$/g,"").trim(),!t)return m.info(`[Git/sanitize] \u622A\u65AD\u540E\u4E3A\u7A7A, \u8FD4\u56DE fallback. raw=${r}`),"chore: update staged changes";const a=t.split(`
|
|
25
|
+
`)[0]?.trim()??"";return a.length>100?(m.info(`[Git/sanitize] \u9996\u884C\u8D85\u8FC7 100 \u5B57\u7B26, \u4EC5\u4FDD\u7559\u9996\u884C. raw=${r}, firstLine=${JSON.stringify(a.substring(0,100))}`),a):(m.info(`[Git/sanitize] \u7ED3\u679C: ${JSON.stringify(t.substring(0,120))} (from raw=${r})`),t)}function ot(e){const t=e.split(/\r?\n/);let r="";for(const i of t){const s=i.trim();if(!s.startsWith("data:"))continue;const a=s.slice(5).trim();if(!(!a||a==="[DONE]"))try{const o=JSON.parse(a)?.choices?.[0]?.delta?.content;typeof o=="string"&&o.length>0&&(r+=o)}catch{}}return r}function Yt(e,t){if(t>5)return;const r=e.split(/\r?\n/);for(const i of r){const s=i.trim();if(!s.startsWith("data:"))continue;const a=s.slice(5).trim();if(!(!a||a==="[DONE]"))try{const n=JSON.parse(a),o=Object.keys(n),c=n?.choices,u=Array.isArray(c)?c[0]:null,l=u?.delta?Object.keys(u.delta):null,f=u?.delta?.content;m.info(`[Git/extract] chunk#${t} structure: topKeys=${JSON.stringify(o)}, choicesLen=${Array.isArray(c)?c.length:typeof c}, deltaKeys=${JSON.stringify(l)}, content=${JSON.stringify(f?.substring(0,50))}, finishReason=${JSON.stringify(u?.finish_reason)}`)}catch{}}}async function Vt(e,t,r,i){const s=`${e.replace(/\/+$/,"")}/chat/completions`,o=(await I.post(s,{model:r,stream:!1,messages:[{role:"system",content:et},{role:"user",content:rt(i)}],max_tokens:200,temperature:.2},{headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`},timeout:3e4})).data?.choices?.[0]?.message?.content?.trim()||"";return D(o)}async function Zt(e,t,r,i,s,a){const n=`${e.replace(/\/+$/,"")}/chat/completions`,c=(await I.post(n,{model:r,stream:!0,messages:[{role:"system",content:et},{role:"user",content:rt(i)}],max_tokens:200,temperature:.2},{headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`,Accept:"text/event-stream"},timeout:Number(process.env.AWS_AI_STREAM_TIMEOUT_MS)||6e4,responseType:"stream",signal:a})).data;let u="",l="",f="",d=0,p=0;return m.info(`[Git/streamAi] AI \u6D41\u5F00\u59CB: aiModel=${r}`),await new Promise((y,g)=>{const w=P=>{c.removeAllListeners("data"),c.removeAllListeners("end"),c.removeAllListeners("error"),c.removeAllListeners("aborted"),m.info(`[Git/streamAi] AI \u6D41\u7ED3\u675F: totalChunks=${d}, totalDeltas=${p}, bufferLength=${u.length}, sanitized=${JSON.stringify(P).substring(0,200)}`),y(P)};c.on("data",P=>{const b=typeof P=="string"?P:P.toString("utf8");d++,f+=b,d<=3&&m.info(`[Git/streamAi] chunk#${d}: length=${b.length}, preview=${JSON.stringify(b.substring(0,80))}`),Yt(b,d);const A=f.split(/\n\n/);f=A.pop()??"";for(const M of A){const E=ot(M);E&&(p++,u+=E,l=D(u),s(E,l))}}),c.on("end",()=>{if(f.trim()){const P=ot(f);P?(m.info(`[Git/streamAi] \u5C3E\u90E8\u6B8B\u7559 delta: length=${P.length}`),u+=P):m.info(`[Git/streamAi] \u5C3E\u90E8\u6B8B\u7559\u65E0 delta, pendingData=${JSON.stringify(f.substring(0,100))}`)}w(D(u))}),c.on("error",P=>{m.error(`[Git/streamAi] \u6D41\u9519\u8BEF: ${P.message}`),g(P)}),c.on("aborted",()=>{m.info(`[Git/streamAi] \u6D41\u88AB\u4E2D\u6B62, bufferLength=${u.length}`),w(D(u))})})}async function W(e,t,r){const i=await C(String(e||t).trim());if(!i.hasRepository||!i.repositoryRootPath)return m.warn(`[Git/collectDiff] \u4E0D\u5728 Git \u4ED3\u5E93\u4E2D: workspacePath=${t}`),{truncatedDiff:"",error:{status:400,message:"workspace is not inside a git repository"}};const s=r.map(u=>u.path).filter(Boolean);m.info(`[Git/collectDiff] \u5F00\u59CB\u6536\u96C6 ${s.length} \u4E2A\u6682\u5B58\u6587\u4EF6\u5DEE\u5F02: ${s.join(", ").substring(0,200)}`);const a=[];for(const u of s){const l=G(u,i),f=await h(i.repositoryRootPath,["diff","--cached","--",l]);f.exitCode===0&&f.stdout.trim()?a.push(`--- ${u} ---
|
|
26
|
+
${f.stdout.trim()}`):m.info(`[Git/collectDiff] \u6587\u4EF6\u65E0\u6682\u5B58\u5DEE\u5F02\u6216\u83B7\u53D6\u5931\u8D25: ${u}, exitCode=${f.exitCode}, stdout.length=${f.stdout.length}`)}if(a.length===0){m.info("[Git/collectDiff] \u9010\u6587\u4EF6 diff \u4E3A\u7A7A, \u56DE\u9000\u5230\u5168\u91CF diff --cached");const u=await h(i.repositoryRootPath,["diff","--cached"]);u.exitCode===0&&u.stdout.trim()?(a.push(u.stdout.trim()),m.info(`[Git/collectDiff] \u5168\u91CF diff \u83B7\u53D6\u6210\u529F: length=${u.stdout.trim().length}`)):m.info(`[Git/collectDiff] \u5168\u91CF diff \u4E5F\u4E3A\u7A7A, exitCode=${u.exitCode}`)}if(a.length===0)return m.info("[Git/collectDiff] \u6682\u5B58\u533A\u6CA1\u6709\u5DEE\u5F02\u5185\u5BB9 (diffParts \u4E3A\u7A7A)"),{truncatedDiff:""};const n=a.join(`
|
|
27
27
|
|
|
28
|
-
`),o=15e3,c=
|
|
29
|
-
...(diff truncated due to length)`:
|
|
30
|
-
`),
|
|
28
|
+
`),o=15e3,c=n.length>o?`${n.slice(0,o)}
|
|
29
|
+
...(diff truncated due to length)`:n;return m.info(`[Git/collectDiff] \u6536\u96C6\u5B8C\u6210: diffContent.length=${n.length}, truncated=${c.length}`),{truncatedDiff:c}}function $(e,t,r){try{e.write(`event: ${t}
|
|
30
|
+
`),e.write(`data: ${JSON.stringify(r)}
|
|
31
31
|
|
|
32
|
-
`)}catch{}}S.post("/git/generate-commit-message",k,async(
|
|
33
|
-
`+p.stack:""}`),c||$(t,"error",{message:p.message||"generate commit message failed"}),t.end()}}),S.post("/git/unstage-files",k,async(
|
|
34
|
-
`),
|
|
35
|
-
`)}S.post("/git/revert-hunk",k,async(
|
|
36
|
-
`,"utf-8");const g=await h(o.repositoryRootPath,["apply","--reverse",y]);if(g.exitCode!==0){t.status(400).json({error:g.stderr||"git apply --reverse failed; \u8BE5\u5904\u5185\u5BB9\u53EF\u80FD\u5DF2\u53D8\u5316\uFF0C\u8BF7\u5237\u65B0\u5DEE\u5F02\u540E\u91CD\u8BD5"});return}t.json({ok:!0,filePath:c,repositoryRelativeFilePath:u,workspacePath:o.workspacePath,repositoryRootPath:o.repositoryRootPath})}finally{p&&await
|
|
32
|
+
`)}catch{}}S.post("/git/generate-commit-message",k,async(e,t)=>{const{workspacePath:r,repositoryPath:i,stagedFiles:s,aiBaseUrl:a,aiApiKey:n,aiModel:o}=e.body||{};if(!r||!String(r).trim()){t.status(400).json({error:"workspacePath is required"});return}if(!Array.isArray(s)||s.length===0){t.status(400).json({error:"stagedFiles is required and must be a non-empty array"});return}if(!a||!String(a).trim()||!n||!String(n).trim()||!o||!String(o).trim()){t.status(400).json({error:"aiBaseUrl, aiApiKey and aiModel are required"});return}try{const{truncatedDiff:c,error:u}=await W(String(i||""),String(r),s);if(u){t.status(u.status).json({error:u.message});return}if(!c){t.json({message:"\u6682\u5B58\u533A\u6CA1\u6709\u53D8\u66F4\u5185\u5BB9"});return}const l=await Vt(a,n,o,c);t.json({message:l})}catch(c){const u=c;m.error(`[Git] generate commit message failed: ${u.message}`),t.status(500).json({error:u.message||"generate commit message failed"})}}),S.post("/git/generate-commit-message-stream",k,async(e,t)=>{ct();const{workspacePath:r,repositoryPath:i,stagedFiles:s,aiBaseUrl:a,aiApiKey:n,aiModel:o}=e.body||{};if(!r||!String(r).trim()){t.status(400).json({error:"workspacePath is required"});return}if(!Array.isArray(s)||s.length===0){t.status(400).json({error:"stagedFiles is required and must be a non-empty array"});return}if(!a||!String(a).trim()||!n||!String(n).trim()||!o||!String(o).trim()){t.status(400).json({error:"aiBaseUrl, aiApiKey and aiModel are required"});return}m.info(`[Git/SSE] \u6536\u5230 generate-commit-message-stream \u8BF7\u6C42: workspacePath=${String(r||"").substring(0,40)}, stagedFiles=${s?.length??0}, aiModel=${o}, aiBaseUrl=${a}`),t.status(200),t.setHeader("Content-Type","text/event-stream; charset=utf-8"),t.setHeader("Cache-Control","no-cache, no-transform"),t.setHeader("Connection","keep-alive"),t.setHeader("X-Accel-Buffering","no"),e.socket?.setNoDelay(!0),t.flushHeaders?.();let c=!1,u=null;const l={delta:0},f=`${e.ip}:${e.socket?.remotePort??"?"}`;e.on("close",()=>{c||(c=!0,m.info(`[Git/SSE] \u5BA2\u6237\u7AEF\u65AD\u5F00\u8FDE\u63A5: workspacePath=${String(r||"").substring(0,40)}, deltaEventsSent=${l.delta}, peer=${f}`))}),$(t,"init",{status:"generating"});try{const{truncatedDiff:d,error:p}=await W(String(i||""),String(r),s);if(p){m.warn(`[Git/SSE] collectStagedDiffForAi \u8FD4\u56DE\u9519\u8BEF: ${p.message}`),$(t,"error",{message:p.message}),t.end();return}if(!d){m.info("[Git/SSE] \u6682\u5B58\u533A\u6CA1\u6709\u53D8\u66F4\u5185\u5BB9, \u76F4\u63A5\u8FD4\u56DE done"),$(t,"delta",{contentDelta:"",message:"\u6682\u5B58\u533A\u6CA1\u6709\u53D8\u66F4\u5185\u5BB9"}),$(t,"done",{message:"\u6682\u5B58\u533A\u6CA1\u6709\u53D8\u66F4\u5185\u5BB9"}),t.end();return}m.info(`[Git/SSE] \u6536\u96C6\u5230 diff: truncatedDiff.length=${d.length}`),c&&m.info("[Git/SSE] \u5BA2\u6237\u7AEF\u5728 collectDiff \u671F\u95F4\u65AD\u5F00, \u4F46\u4ECD\u5C1D\u8BD5 AI \u8C03\u7528"),m.info(`[Git/SSE] \u5F00\u59CB\u8C03\u7528 AI streamAiForCommitMessage: aiModel=${o}`),u=new AbortController;let y="";const g=await Zt(a,n,o,d,(w,P)=>{l.delta++,P!==y?(y=P,$(t,"delta",{contentDelta:w,message:P})):$(t,"delta",{contentDelta:w,message:P})},u.signal);m.info(`[Git/SSE] AI streamAiForCommitMessage \u5B8C\u6210: finalMessage=${JSON.stringify(g).substring(0,200)}, deltaEventsSent=${l.delta}`),c||$(t,"done",{message:g}),t.end()}catch(d){const p=d;m.error(`[Git/SSE] stream commit message failed: ${p.message}${p.stack?`
|
|
33
|
+
`+p.stack:""}`),c||$(t,"error",{message:p.message||"generate commit message failed"}),t.end()}}),S.post("/git/unstage-files",k,async(e,t)=>{const{workspacePath:r,repositoryPath:i,filePaths:s}=e.body||{};if(!r||!String(r).trim()){t.status(400).json({error:"workspacePath is required"});return}const n=(Array.isArray(s)?s:[]).map(o=>R(String(o||"").trim())).filter(o=>o.length>0);if(n.length===0){t.status(400).json({error:"filePaths is required and must be a non-empty array"});return}try{const o=await C(String(i||r).trim());if(!o.hasRepository||!o.repositoryRootPath){t.status(400).json({error:"workspace is not inside a git repository"});return}let c="";for(const u of n){const l=T(u,o),f=await h(o.repositoryRootPath,["restore","--staged",...l]);f.exitCode!==0&&(c=f.stderr||`git restore --staged failed for ${u}`)}if(c){t.status(400).json({error:c});return}t.json({ok:!0,filePaths:n,workspacePath:o.workspacePath,repositoryRootPath:o.repositoryRootPath,relativeWorkspacePath:o.relativeWorkspacePath})}catch(o){const c=o;t.status(500).json({error:c.message||"git unstage files failed"})}}),S.post("/git/restore-files",k,async(e,t)=>{const{workspacePath:r,repositoryPath:i,filePaths:s}=e.body||{};if(!r||!String(r).trim()){t.status(400).json({error:"workspacePath is required"});return}const n=(Array.isArray(s)?s:[]).map(o=>R(String(o||"").trim())).filter(o=>o.length>0);if(n.length===0){t.status(400).json({error:"filePaths is required and must be a non-empty array"});return}try{const o=await C(String(i||r).trim());if(!o.hasRepository||!o.repositoryRootPath){t.status(400).json({error:"workspace is not inside a git repository"});return}let c="";for(const u of n){const l=G(u,o),f=T(u,o),p=(await h(o.repositoryRootPath,["ls-files","--error-unmatch","--",l])).exitCode===0,y=p?await h(o.repositoryRootPath,["restore",...f]):await h(o.repositoryRootPath,["clean","-f",...f]);y.exitCode!==0&&(c=y.stderr||`${p?"git restore":"git clean -f"} failed for ${u}`)}if(c){t.status(400).json({error:c});return}t.json({ok:!0,filePaths:n,workspacePath:o.workspacePath,repositoryRootPath:o.repositoryRootPath,relativeWorkspacePath:o.relativeWorkspacePath})}catch(o){const c=o;t.status(500).json({error:c.message||"git restore files failed"})}});function Xt(e){const t=e.split(`
|
|
34
|
+
`),r=[];for(let i=0;i<t.length;i++){const s=t[i],a=s.endsWith("\r")?"\r":"",o=(a?s.slice(0,-1):s).match(/^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/);if(!o){r.push(s);continue}let c=0,u=0;for(let l=i+1;l<t.length;l++){const f=t[l].replace(/\r$/,"");if(/^diff --git /.test(f)||/^@@ /.test(f))break;f.startsWith("+")?u++:f.startsWith("-")?c++:f.startsWith(" ")&&(c++,u++)}r.push(`@@ -${o[1]},${c} +${o[2]},${u} @@${a}`)}return r.join(`
|
|
35
|
+
`)}S.post("/git/revert-hunk",k,async(e,t)=>{const{workspacePath:r,repositoryPath:i,filePath:s,patch:a}=e.body||{};if(!r||!String(r).trim()){t.status(400).json({error:"workspacePath is required"});return}if(!s||!String(s).trim()){t.status(400).json({error:"filePath is required"});return}const n=String(a||"").replace(/^\uFEFF/,"").trim();if(!n){t.status(400).json({error:"patch is required"});return}try{const o=await C(String(i||r).trim());if(!o.hasRepository||!o.repositoryRootPath){t.status(400).json({error:"workspace is not inside a git repository"});return}const c=R(String(s).trim()),u=G(c,o);if(H(u),(n.match(/^diff --git /gm)||[]).length!==1){t.status(400).json({error:"patch must contain exactly one file diff"});return}const f=n.match(/^\+\+\+ b\/(.+)$/m),d=f?R(f[1].trim()):"";if(!d||d!==u){t.status(400).json({error:"patch target path does not match filePath"});return}let p=null;try{p=await j.mkdtemp(x.join(at.tmpdir(),"agentswork-git-revert-"));const y=x.join(p,"revert.patch");await j.writeFile(y,Xt(n)+`
|
|
36
|
+
`,"utf-8");const g=await h(o.repositoryRootPath,["apply","--reverse",y]);if(g.exitCode!==0){t.status(400).json({error:g.stderr||"git apply --reverse failed; \u8BE5\u5904\u5185\u5BB9\u53EF\u80FD\u5DF2\u53D8\u5316\uFF0C\u8BF7\u5237\u65B0\u5DEE\u5F02\u540E\u91CD\u8BD5"});return}t.json({ok:!0,filePath:c,repositoryRelativeFilePath:u,workspacePath:o.workspacePath,repositoryRootPath:o.repositoryRootPath})}finally{p&&await j.rm(p,{recursive:!0,force:!0}).catch(()=>{})}}catch(o){const c=o;t.status(500).json({error:c.message||"git revert hunk failed"})}}),S.post("/git/diff-content",k,async(e,t)=>{const{workspacePath:r,repositoryPath:i,stagedFiles:s}=e.body||{};if(!r||!String(r).trim()){t.status(400).json({error:"workspacePath is required"});return}if(!Array.isArray(s)||s.length===0){t.status(400).json({error:"stagedFiles is required and must be a non-empty array"});return}try{const{truncatedDiff:a,error:n}=await W(String(i||""),String(r),s);if(n){t.status(n.status).json({error:n.message});return}t.json({ok:!0,diff:a||""})}catch(a){const n=a;m.error(`[Git/diff-content] \u83B7\u53D6\u6682\u5B58\u533A\u5DEE\u5F02\u5931\u8D25: ${n.message}`),t.status(500).json({error:n.message||"get diff content failed"})}});export{ft as assertGitWorkspacePathAccessible,Nt as buildCommitDiffBaseArgs,St as buildGitCommitLogArgs,Vt as callAiForCommitMessage,Tt as collectUntrackedFiles,It as commitSelectedFiles,Mt as countUntrackedFileAdditions,bt as createAgentsWorkStashSnapshotRef,Lt as createGitStashSnapshot,Ct as createMissingInitialCommitSnapshotError,T as createScopedFilePathspecArgs,_t as createScopedGitPathspecArgs,Yt as debugAiStreamChunk,ot as extractOpenAiStreamDelta,S as gitRouter,xt as isGitStashMissingInitialCommitError,jt as isGitStashNoChangesOutput,V as listGitBranches,Dt as mergeGitDiffSummaryFiles,Ht as normalizeGitCommitMessage,Ft as parseGitCommitLog,F as parseGitDiffSummary,z as parseGitStashListLine,Et as parseGitStatusStagedPaths,Gt as parseGitStatusUntrackedPaths,q as parseLatestGitStash,U as persistAgentsWorkStashSnapshotRef,Bt as previewRepositoryReset,Wt as pushBranchToRemote,qt as resetRepositoryTo,yt as resolveCommitPageLimit,Pt as resolveCommitPageSkip,N as resolveCurrentBranchName,lt as resolveGitCommandTimeoutMs,vt as resolveGitPushTimeoutMs,Y as resolveRemoteCommitHashes,D as sanitizeCommitMessage,Rt as shouldIncludeGitDiffSummaryFile,wt as sliceCommitPage,Zt as streamAiForCommitMessage};
|
|
37
37
|
|
|
@@ -30,9 +30,17 @@ interface AmbiguousSchedulerBaseUrlResponse {
|
|
|
30
30
|
hint: string;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
33
36
|
export declare function buildSchedulerPingFailureResponse(error: Error, configuredSchedulerBaseUrl: string): SchedulerPingFailureResponse;
|
|
34
37
|
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
35
41
|
export declare function buildAmbiguousSchedulerBaseUrlResponse(targetUrls: string[]): AmbiguousSchedulerBaseUrlResponse;
|
|
36
42
|
|
|
43
|
+
|
|
44
|
+
|
|
37
45
|
export declare function summarizeToolStatus(toolStatus: Record<string, ToolInstallStatus>): Record<string, Pick<ToolInstallStatus, "installed" | "version" | "executable" | "error">>;
|
|
38
46
|
export {};
|
package/dist/routes/instance.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{spawn as A}from"node:child_process";import{createHash as W,timingSafeEqual as G}from"node:crypto";import J from"node:fs/promises";import w from"node:path";import{fileURLToPath as _}from"node:url";import O from"axios";import{Router as Q}from"express";import{adapterRegistry as Y}from"../adapter/index.js";import{getBridgeVersion as R,resolveSchedulerBaseUrlFrom as Z}from"../config.js";import{validateToken as h,validateTokenOrPanelSession as T}from"../middleware/auth.js";import{getConfiguredConnectionKeys as C,getRegistrationState as ee,requestRuntimeAccessTokenRefreshForServer as te,resolveSchedulerBaseUrlFromToken as re}from"../services/auto-register.js";import{getEffective as v,getOverrides as j}from"../services/capability-config.js";import{appendChangelogSection as se,readChangelog as ne}from"../services/changelog.js";import{setAgentFeatures as oe,setAgentCapabilities as ae,setAgentInheritedFeatures as ie,setAgentCapabilitiesInherited as de,getAgentFeatures as x,getAgentInheritedFeatures as ce}from"../services/instance-features.js";import{initInstance as le}from"../services/instance-init-service.js";import{loadInstanceState as U,saveInstanceState as L}from"../services/instance-state.js";import{createBridgeLifecycleOperation as P,deliverPendingBridgeLifecycleNotifications as $,loadLifecycleState as ue,removeBridgeLifecycleOperation as ge,upsertBridgeLifecycleOperation as k}from"../services/lifecycle-state.js";import{mcpDeployManager as pe}from"../services/mcp-deploy.js";import{pluginManager as fe}from"../services/plugin-manager.js";import{getRuntimeAccessToken as me}from"../services/runtime-binding.js";import{skillManager as he}from"../services/skill-manager.js";import{detectToolStatuses as E,ensureToolsInstalled as Se,SUPPORTED_INSTALLABLE_TOOLS as F,SUPPORTED_UNINSTALLABLE_TOOLS as D,uninstallTools as ye}from"../services/tool-installer.js";import{appendUpdateLog as m,readUpdateLog as be}from"../services/update-log.js";import{createLogger as we}from"../utils/logger.js";import{checkBridgeUpdate as N}from"../utils/npm-version.js";const d=we("instance");import{buildToolStatusDetectionTargets as ke}from"./instance-tool-status.js";let I=null;function it(r){I=r}const g=Q();function M(r){return String(r||"").trim()||void 0}function K(r){const t=r.get("host");if(t)return`${r.protocol}://${t}`}function q(){const r=w.resolve(w.dirname(_(import.meta.url)),"..","..");return w.join(r,"dist","index.js")}function Be(){return new Promise((r,t)=>{const e=A(process.execPath,[q(),"update","--no-kill"],{shell:!1,windowsHide:!0});let s="",n="";e.stdout.on("data",o=>{s+=o.toString("utf8")}),e.stderr.on("data",o=>{n+=o.toString("utf8")}),e.on("error",t),e.on("close",o=>{r({exitCode:o,stdout:s,stderr:n})})})}function z(){const r=A(process.execPath,[q(),"service","restart"],{shell:!1,windowsHide:!0,stdio:["ignore","pipe","pipe"]});r.stdout.on("data",t=>{m(String(t)).catch(()=>{})}),r.stderr.on("data",t=>{m(String(t)).catch(()=>{})})}async function Ie(){try{const r=w.join(w.dirname(w.dirname(_(import.meta.url))),"package.json"),t=JSON.parse(await J.readFile(r,{encoding:"utf8"}));return(typeof t.version=="string"?t.version.trim():"")||null}catch{return null}}const V=3e3;async function Re(r){const t={...r,status:"running",message:"Bridge \u66F4\u65B0\u6B63\u5728\u540E\u53F0\u6267\u884C\u3002"};await k(t);try{d.info("[update-bridge] \u6B63\u5728\u540E\u53F0\u6267\u884C awsb update --yes"),await m(`===== Bridge \u66F4\u65B0\u5F00\u59CB\uFF1Av${R()} -> latest\uFF08\u64CD\u4F5C ${r.operationId}\uFF09 =====`);const e=await Be();if(e.stdout&&await m(e.stdout),e.stderr&&await m(e.stderr),e.exitCode===0){const s=await Ie(),n=r.latestVersion||s||R();await se(n,`Bridge \u5DF2\u66F4\u65B0\u81F3 ${n}\u3002`).catch(a=>{const i=a;d.warn(`[update-bridge] \u8FFD\u52A0 changelog \u6BB5\u5931\u8D25\uFF08\u5DF2\u5FFD\u7565\uFF09: ${i.message}`)}),await m("===== Bridge \u66F4\u65B0\u6210\u529F\uFF0C\u51C6\u5907\u81EA\u52A8\u91CD\u542F ===== ");const o={...t,status:"restarting",message:"Bridge \u66F4\u65B0\u5B8C\u6210\uFF0C\u6B63\u5728\u91CD\u542F..."};await k(o),d.info("[update-bridge] \u6B63\u5728\u6267\u884C awsb service restart"),z(),setTimeout(()=>{if(process.exitCode=42,I){I("BRIDGE_UPDATE_RESTART",!0).catch(a=>{const i=a;d.error("[update-bridge] graceful shutdown failed:",i),process.exit(42)});return}process.exit(42)},V)}else await m(`===== Bridge \u66F4\u65B0\u5931\u8D25\uFF1A\u9000\u51FA\u7801 ${e.exitCode??"unknown"} =====`),await k({...t,status:"failed",message:`Bridge \u66F4\u65B0\u5931\u8D25\uFF0Cnpm \u9000\u51FA\u7801: ${e.exitCode??"unknown"}`,error:e.stderr||e.stdout}),await $()}catch(e){const s=e;d.error("[update-bridge] background update failed:",s),await m(`===== Bridge \u66F4\u65B0\u5F02\u5E38\uFF1A${s.message} =====`).catch(()=>{}),await k({...t,status:"failed",message:"Bridge \u66F4\u65B0\u5931\u8D25\u3002",error:s.message||"update bridge failed"}),await $()}}async function X(r){const t=await import("node:fs/promises"),e=await import("node:path"),s=await import("node:os"),n=e.join(s.tmpdir(),"agentswork-runtime-bridge",".preserve-sessions");if(r){await t.mkdir(e.dirname(n),{recursive:!0}),await t.writeFile(n,JSON.stringify({preserveSessions:!0,timestamp:new Date().toISOString()}),"utf-8"),d.info(`[prepare-restart] \u5DF2\u521B\u5EFA\u4FDD\u7559\u4F1A\u8BDD\u6807\u8BB0\u6587\u4EF6: ${n}`);return}try{await t.unlink(n),d.info("[prepare-restart] \u5DF2\u5220\u9664\u4FDD\u7559\u4F1A\u8BDD\u6807\u8BB0\u6587\u4EF6")}catch{}}function Te(r){return W("md5").update(r,"utf8").digest("hex")}function ve(r){const t=r.trim().toLowerCase();if(!/^[a-f0-9]{32}$/.test(t))return!1;const e=Buffer.from(t,"hex");return C().map(s=>s.trim()).filter(Boolean).some(s=>{const n=Buffer.from(Te(s),"hex");return n.length===e.length&&G(n,e)})}function je(r){const e=C().length>0,s=R();return e?ve(r)?{status:200,body:{ok:!0,runtimeBridge:"healthy",version:s,connectionKeyMatched:!0,connectionKeyRequired:!0}}:{status:401,body:{ok:!1,error:"connection_key_mismatch",version:s,connectionKeyMatched:!1,connectionKeyRequired:!0}}:{status:200,body:{ok:!0,runtimeBridge:"healthy",version:s,connectionKeyMatched:!0,connectionKeyRequired:!1}}}function Ue(r,t){const s=t.trim()||"http://localhost:8080",n=/^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?(?:\/|$)/i.test(s);return{ok:!1,error:r.message||"scheduler ping failed",failureStage:"scheduler_ping",runtimeBridge:"healthy",schedulerBaseUrl:s,hint:n?"aws-runtime-bridge \u5DF2\u8FDE\u901A\uFF0C\u4F46\u5B83\u56DE\u8FDE\u8C03\u5EA6\u4E2D\u5FC3\u5931\u8D25\u3002\u8BF7\u5728 bridge \u542F\u52A8\u73AF\u5883\u4E2D\u5C06 AWS_RUNTIME_SCHEDULER_BASE_URL \u914D\u7F6E\u4E3A\u8BE5\u5B9E\u4F8B\u53EF\u8BBF\u95EE\u7684\u8C03\u5EA6\u4E2D\u5FC3\u5730\u5740\uFF0C\u4F8B\u5982 http://<server-host>:7380\u3002":"aws-runtime-bridge \u5DF2\u8FDE\u901A\uFF0C\u4F46\u5B83\u56DE\u8FDE\u8C03\u5EA6\u4E2D\u5FC3\u5931\u8D25\u3002\u8BF7\u786E\u8BA4 AWS_RUNTIME_SCHEDULER_BASE_URL \u6307\u5411\u7684\u8C03\u5EA6\u4E2D\u5FC3\u5730\u5740\u53EF\u4ECE bridge \u673A\u5668\u8BBF\u95EE\uFF0C\u5E76\u4E14\u8FD0\u884C\u65F6\u8BBF\u95EE\u4EE4\u724C\u4ECD\u6709\u6548\u3002"}}function Ee(r){if(typeof r!="object"||r===null)return;const t=Reflect.get(r,"response");if(typeof t!="object"||t===null)return;const e=Reflect.get(t,"status");return typeof e=="number"?e:void 0}async function Ae(r,t){try{const e=await O.get(`${r}/api/runtime/ping`,{headers:{"X-Runtime-Token":t}});return{schedulerReachable:e.status>=200&&e.status<300,tokenRefreshed:!1}}catch(e){if(Ee(e)!==401)throw e;const s=await te(r);if(!s.success||!s.runtimeAccessToken)throw e;const n=await O.get(`${r}/api/runtime/ping`,{headers:{"X-Runtime-Token":s.runtimeAccessToken}});return{schedulerReachable:n.status>=200&&n.status<300,tokenRefreshed:!0}}}function _e(r){return{ok:!1,error:"ambiguous_scheduler_base_url",failureStage:"scheduler_ping",runtimeBridge:"healthy",schedulerBaseUrl:"",autoRegisterTargetUrls:r,hint:"Multiple autoRegisterTargets.serverUrl values were found, so bridge cannot safely infer which scheduler /runtime/ping should call back. Set AWS_RUNTIME_SCHEDULER_BASE_URL explicitly, or use future schedulerId/token-bound multi-scheduler routing."}}function Oe(r){return Object.fromEntries(Object.entries(r||{}).map(([t,e])=>[t,{installed:!!e?.installed,version:e?.version||null,executable:e?.executable||null,error:e?.error||null}]))}g.get("/check-update",async(r,t)=>{t.json(await N())}),g.get("/update-log",T,async(r,t)=>{try{const e=await be();t.json({ok:!0,...e})}catch(e){const s=e;d.error("[update-log] Error:",s),t.status(500).json({ok:!1,error:s.message||"read update log failed"})}}),g.get("/changelog",T,async(r,t)=>{const e=String(r.query?.version||"").trim();if(!e){t.status(400).json({ok:!1,error:"version is required"});return}try{const s=await ne(e);t.json({ok:!0,version:e,...s})}catch(s){const n=s;d.error("[changelog] Error:",n),t.status(500).json({ok:!1,error:n.message||"read changelog failed"})}}),g.get("/healthz",(r,t)=>{t.json({ok:!0,runtimeBridge:"healthy"})}),g.get("/connection-check",(r,t)=>{const e=String(r.header("X-Bridge-Connection-Key-MD5")||""),s=je(e);t.status(s.status).json(s.body)}),g.get("/ping",h,async(r,t)=>{let e=Z(r.header("X-Scheduler-Base-Url"),r.header("X-Bridge-Instance-Id"),r.header("X-Bridge-Instance-Name"));if(e.source==="ambiguous-auto-register-targets"){const n=re(r.header("X-Runtime-Token"));if(n)e={url:n,source:"token-match",ambiguousTargetUrls:[]};else{const o=r.header("X-Bridge-Instance-Id");if(o){const a=ee(),i=Object.entries(a.registrations).find(([,l])=>l===o)?.[0];i&&(e={url:i,source:"instance-id-match",ambiguousTargetUrls:[]})}if(!e.url||e.source==="ambiguous-auto-register-targets"){t.status(400).json(_e(e.ambiguousTargetUrls));return}}}const s=e.url;try{const n=me(s);if(!n){t.status(401).json({ok:!1,error:"runtime_access_token_required"});return}const o=await Ae(s,n);t.json({ok:!0,runtimeBridge:"healthy",version:R(),schedulerReachable:o.schedulerReachable,tokenRefreshed:o.tokenRefreshed,schedulerBaseUrl:s,instanceId:e.instanceId,instanceName:e.instanceName})}catch(n){const o=n;t.status(502).json(Ue(o,s))}}),g.post("/init-instance",h,async(r,t)=>{const{agentId:e,workspacePath:s,skillEnabled:n,mcpEnabled:o,ccSwitchEnabledTools:a,skillPackage:i,skillPackages:l,pluginPackages:f,mcpServers:S}=r.body||{};if(!e||!s){t.status(400).json({error:"agentId and workspacePath are required"});return}try{d.info("[init-instance] Request received",{agentId:String(e),workspacePath:String(s),skillEnabled:n,mcpEnabled:o,ccSwitchEnabledTools:a});const u=await le(e,s,{skillEnabled:n,mcpEnabled:o,ccSwitchEnabledTools:a,skillPackage:i,skillPackages:l,pluginPackages:f,mcpServers:S});d.info("[init-instance] Initialization completed",{agentId:String(e),ok:u.ok,logCount:u.logs?.length||0,enabledTools:u.enabledTools}),t.json(u)}catch(u){const p=u;d.error("[init-instance] Initialization failed",{agentId:String(e),workspacePath:String(s),error:p?.message||String(u),stack:p?.stack||null}),t.status(400).json({error:p?.message||"initialize instance failed"})}}),g.post("/features",h,async(r,t)=>{const{agentId:e,features:s}=r.body||{};if(!e){t.status(400).json({error:"agentId is required"});return}oe(String(e),s),r.body.inheritedFeatures!==void 0&&ie(String(e),r.body.inheritedFeatures),r.body.capabilities!==void 0&&ae(String(e),r.body.capabilities),r.body.capabilitiesInherited!==void 0&&de(String(e),r.body.capabilitiesInherited),d.info("[instance/features] \u80FD\u529B\u5F00\u5173\u5DF2\u66F4\u65B0",{agentId:String(e),features:x(String(e)),inheritedFeatures:ce(String(e))});let n=0;for(const o of Y.getAll())if(typeof o.applyAgentFeatures=="function")try{await o.applyAgentFeatures(String(e)),n+=1}catch(a){d.error("[instance/features] adapter \u70ED\u540C\u6B65\u5931\u8D25",{agentId:String(e),providerId:o.providerId,error:a?.message||String(a)})}t.json({ok:!0,agentId:String(e),features:x(String(e)),hotSyncedAdapters:n})}),g.post("/cc-switch/state",h,async(r,t)=>{const{agentId:e}=r.body||{};if(!e){t.status(400).json({error:"agentId is required"});return}try{d.info("[cc-switch/state] Request received",{agentId:String(e)});const s=await U(e),n=ke(s.enabledTools||[]);d.info("[cc-switch/state] Detecting SDK statuses",{agentId:String(e),detectionTargets:n});const o=await E(n),a=await fe.listDeployed(),i=he.listDeployed(),l=pe.listDeployed(),f=j(e,"skills"),S=j(e,"mcp"),u=j(e,"plugins"),p=i.map(c=>({...c,enabled:v(e,"skills",c.skillName),source:f[c.skillName]!==void 0?"agent":"bridge"})),y=l.map(c=>({...c,enabled:v(e,"mcp",c.name),source:S[c.name]!==void 0?"agent":"bridge"})),b=a.map(c=>({...c,enabled:v(e,"plugins",c.pluginName),source:u[c.pluginName]!==void 0?"agent":"bridge"}));d.info("[cc-switch/state] SDK status detection completed",{agentId:String(e),toolStatus:Oe(o)}),t.json({ok:!0,agentId:String(e),imported:{mcpServers:0,skills:0},deployedPlugins:b,deployedSkills:p,deployedMcp:y,state:{...s,toolStatus:o}})}catch(s){const n=s;d.error("[cc-switch/state] Failed to load state",{agentId:String(e),error:n?.message||String(s),stack:n?.stack||null}),t.status(400).json({error:n?.message||"load state failed"})}}),g.post("/tools/install",h,async(r,t)=>{const{agentId:e,tools:s}=r.body||{};if(!e){t.status(400).json({error:"agentId is required"});return}const n=Array.isArray(s)?s.map(i=>String(i||"").trim().toLowerCase()).filter(Boolean):[],o=new Set(F),a=n.filter(i=>o.has(i));if(a.length===0){t.status(400).json({error:`tools must include ${F.join(", ")}`});return}try{const i=await U(e),l=Array.from(new Set([...i.enabledTools||[],...a])),f=await Se(a),u={...await E(l),...f},p=await L(e,{...i,enabledTools:l,toolStatus:u}),y=new Set(a),b=Object.entries(u).filter(([c])=>y.has(c)).filter(([,c])=>!c.installed).map(([c,B])=>({tool:c,error:B.error||null}));t.json({ok:b.length===0,agentId:String(e),toolStatus:u,failedTools:b,state:p})}catch(i){const l=i;t.status(400).json({error:l?.message||"install tools failed"})}}),g.post("/tools/uninstall",h,async(r,t)=>{const{agentId:e,tools:s}=r.body||{};if(!e){t.status(400).json({error:"agentId is required"});return}const n=Array.isArray(s)?s.map(i=>String(i||"").trim().toLowerCase()).filter(Boolean):[],o=new Set(D),a=n.filter(i=>o.has(i));if(a.length===0){t.status(400).json({error:`tools must include ${D.join(", ")}`});return}try{const i=await U(e),l=new Set(a),f=(i.enabledTools||[]).filter(B=>!l.has(String(B||"").trim().toLowerCase())),S=await ye(a),p={...await E([...f,...a]),...S},y=a.filter(B=>p[B]?.installed),b=y.length>0?i.enabledTools||[]:f,c=await L(e,{...i,enabledTools:b,toolStatus:p});if(y.length>0){t.status(400).json({ok:!1,agentId:String(e),error:"uninstall incomplete",toolStatus:p,state:c});return}t.json({ok:!0,agentId:String(e),toolStatus:p,state:c})}catch(i){const l=i;t.status(400).json({error:l?.message||"uninstall tools failed"})}}),g.post("/prepare-restart",h,async(r,t)=>{const{preserveSessions:e=!0}=r.body||{};try{d.info(`[prepare-restart] \u6536\u5230\u51C6\u5907\u91CD\u542F\u901A\u77E5\uFF0CpreserveSessions=${e}`),await X(!!e),t.json({ok:!0,preserveSessions:e,message:e?"Bridge will preserve sessions for aws-mcp-server restart":"Bridge will clean up sessions on shutdown"})}catch(s){const n=s;d.error("[prepare-restart] Error:",n),t.status(500).json({error:n.message})}});const Ce={update:15*60*1e3,restart:5*60*1e3};async function xe(r){try{const t=await ue(),e=Date.now(),s=Ce[r],n=[];for(const o of t.operations){if(o.operationType!==r||!["accepted","running","restarting"].includes(o.status))continue;const a=new Date(o.updatedAt).getTime();if(!Number.isNaN(a)&&e-a<=s)return{operationId:o.operationId};n.push(o.operationId)}for(const o of n)try{await ge(o),d.info(`[${r}-bridge] \u5DF2\u6E05\u7406\u8D85\u65F6\u672A\u4EA4\u4ED8\u7684\u9648\u65E7\u64CD\u4F5C: ${o}`)}catch(a){const i=a;d.warn(`[${r}-bridge] \u6E05\u7406\u9648\u65E7\u64CD\u4F5C\u5931\u8D25\uFF0C\u7EE7\u7EED\u653E\u884C: ${o}, ${i.message}`)}return null}catch(t){const e=t;return d.warn(`[${r}-bridge] \u5728\u9014\u64CD\u4F5C\u68C0\u67E5\u5931\u8D25\uFF0C\u653E\u884C\u8BF7\u6C42: ${e.message}`),null}}async function H(r,t,e){const s=await xe(r);return s?(e.status(409).json({error:t,operationId:s.operationId}),!0):!1}g.post("/update-bridge",T,async(r,t)=>{const e=M(r.body?.requestedByUserId);try{if(!e){t.status(400).json({error:"requestedByUserId is required"});return}if(await H("update","\u5B9E\u4F8B Bridge \u66F4\u65B0\u6B63\u5728\u6267\u884C\u4E2D\uFF0C\u8BF7\u52FF\u91CD\u590D\u89E6\u53D1",t))return;const s=await N(),n=P({operationType:"update",requestedByUserId:e,schedulerBaseUrl:r.body?.schedulerBaseUrl,runtimeBridgeBaseUrl:K(r),status:"accepted",message:"Bridge \u66F4\u65B0\u8BF7\u6C42\u5DF2\u6536\u5230\uFF0C\u5C06\u5728\u540E\u53F0\u6267\u884C\u5E76\u81EA\u52A8\u91CD\u542F\u3002",latestVersion:s.ok&&s.latestVersion||void 0});await k(n),Re(n),t.json({ok:!0,operationId:n.operationId,message:"Bridge \u66F4\u65B0\u5DF2\u5728\u540E\u53F0\u5F00\u59CB\uFF0C\u66F4\u65B0\u5B8C\u6210\u540E\u5C06\u81EA\u52A8\u91CD\u542F\u5B9E\u4F8B Bridge\u3002"})}catch(s){const n=s;d.error("[update-bridge] Error:",n),t.status(500).json({error:n.message||"update bridge failed"})}}),g.post("/restart-bridge",T,async(r,t)=>{const{preserveSessions:e=!0}=r.body||{},s=M(r.body?.requestedByUserId);try{if(!s){t.status(400).json({error:"requestedByUserId is required"});return}if(await H("restart","\u5B9E\u4F8B Bridge \u6B63\u5728\u91CD\u542F\u4E2D\uFF0C\u8BF7\u7A0D\u5019\u518D\u8BD5",t))return;d.info(`[restart-bridge] \u6536\u5230\u5B9E\u4F8B Bridge \u91CD\u542F\u8BF7\u6C42\uFF0CpreserveSessions=${e}`);const n=P({operationType:"restart",requestedByUserId:s,schedulerBaseUrl:r.body?.schedulerBaseUrl,runtimeBridgeBaseUrl:K(r),status:"accepted",message:"Bridge \u5DF2\u6536\u5230\u91CD\u542F\u8BF7\u6C42\uFF1B\u4E0B\u6B21\u542F\u52A8\u540E\u4F1A\u53D1\u9001\u786E\u8BA4\u901A\u77E5\u3002"});await k(n),await X(!!e),t.json({ok:!0,operationId:n.operationId,preserveSessions:!!e,message:"Bridge \u6B63\u5728\u91CD\u542F\uFF0C\u670D\u52A1\u7BA1\u7406\u5668\u4F1A\u81EA\u52A8\u62C9\u8D77\u5B9E\u4F8B\uFF1B\u4E0B\u6B21\u542F\u52A8\u540E\u4F1A\u901A\u8FC7\u6D88\u606F\u4E2D\u5FC3\u901A\u77E5\u7ED3\u679C\u3002"}),d.info("[restart-bridge] \u6B63\u5728\u6267\u884C awsb service restart"),z(),setTimeout(()=>{if(process.exitCode=42,I){I("PANEL_RESTART",!!e).catch(o=>{const a=o;d.error("[restart-bridge] graceful shutdown failed:",a),process.exit(42)});return}process.exit(42)},V)}catch(n){const o=n;d.error("[restart-bridge] Error:",o),t.status(500).json({error:o.message||"restart bridge failed"})}});export{_e as buildAmbiguousSchedulerBaseUrlResponse,je as buildConnectionCheckResponse,Ue as buildSchedulerPingFailureResponse,g as instanceRouter,it as setGracefulShutdownFn,Oe as summarizeToolStatus};
|
|
1
|
+
import{spawn as _}from"node:child_process";import{createHash as ee,timingSafeEqual as te}from"node:crypto";import re from"node:fs/promises";import k from"node:path";import{fileURLToPath as C}from"node:url";import O from"axios";import{Router as se}from"express";import{adapterRegistry as ne}from"../adapter/index.js";import{getBridgeVersion as T,isDesktopAppMode as oe,resolveSchedulerBaseUrlFrom as x}from"../config.js";import{validateToken as m,validateTokenOrPanelSession as R}from"../middleware/auth.js";import{registerAgentOwner as ae}from"../services/agent-ownership.js";import{getConfiguredConnectionKeys as L,getRegistrationState as ie,listConfiguredTargetSummaries as de,requestRuntimeAccessTokenRefreshForServer as ce,resolveTargetIdentityFromToken as P}from"../services/auto-register.js";import{getEffective as j,getOverrides as U}from"../services/capability-config.js";import{appendChangelogSection as le,readChangelog as ue}from"../services/changelog.js";import{rotateConnectionKeyFromServer as ge}from"../services/connection-key-rotation.js";import{setAgentFeatures as pe,setAgentCapabilities as fe,setAgentInheritedFeatures as me,setAgentCapabilitiesInherited as he,getAgentFeatures as F,getAgentInheritedFeatures as ye}from"../services/instance-features.js";import{initInstance as Se}from"../services/instance-init-service.js";import{loadInstanceState as A,saveInstanceState as $}from"../services/instance-state.js";import{createBridgeLifecycleOperation as D,deliverPendingBridgeLifecycleNotifications as K,loadLifecycleState as we,removeBridgeLifecycleOperation as be,upsertBridgeLifecycleOperation as B}from"../services/lifecycle-state.js";import{McpDeployManager as Ie}from"../services/mcp-deploy.js";import{pluginManager as ke}from"../services/plugin-manager.js";import{getRuntimeAccessToken as Be}from"../services/runtime-binding.js";import{SkillManager as ve}from"../services/skill-manager.js";import{detectToolStatuses as E,ensureToolsInstalled as Te,SUPPORTED_INSTALLABLE_TOOLS as M,SUPPORTED_UNINSTALLABLE_TOOLS as N,uninstallTools as Re}from"../services/tool-installer.js";import{appendUpdateLog as y,readUpdateLog as je}from"../services/update-log.js";import{getUserScopeOrThrow as X,requireUserScope as q}from"../services/user-scope.js";import{createLogger as Ue}from"../utils/logger.js";import{checkBridgeUpdate as z}from"../utils/npm-version.js";import{serverUrlOfTargetKey as Ae}from"../utils/target-identity.js";const d=Ue("instance");import{buildToolStatusDetectionTargets as Ee}from"./instance-tool-status.js";let v=null;function bt(r){v=r}const l=se();function H(r){return String(r||"").trim()||void 0}function V(r){const t=r.get("host");if(t)return`${r.protocol}://${t}`}function W(){const r=k.resolve(k.dirname(C(import.meta.url)),"..","..");return k.join(r,"dist","index.js")}function _e(){return new Promise((r,t)=>{const e=_(process.execPath,[W(),"update","--no-kill"],{shell:!1,windowsHide:!0});let s="",n="";e.stdout.on("data",o=>{s+=o.toString("utf8")}),e.stderr.on("data",o=>{n+=o.toString("utf8")}),e.on("error",t),e.on("close",o=>{r({exitCode:o,stdout:s,stderr:n})})})}function G(){const r=_(process.execPath,[W(),"service","restart"],{shell:!1,windowsHide:!0,stdio:["ignore","pipe","pipe"]});r.stdout.on("data",t=>{y(String(t)).catch(()=>{})}),r.stderr.on("data",t=>{y(String(t)).catch(()=>{})})}async function Ce(){try{const r=k.join(k.dirname(k.dirname(C(import.meta.url))),"package.json"),t=JSON.parse(await re.readFile(r,{encoding:"utf8"}));return(typeof t.version=="string"?t.version.trim():"")||null}catch{return null}}const J=3e3;async function Oe(r){const t={...r,status:"running",message:"Bridge \u66F4\u65B0\u6B63\u5728\u540E\u53F0\u6267\u884C\u3002"};await B(t);try{d.info("[update-bridge] \u6B63\u5728\u540E\u53F0\u6267\u884C awsb update --yes"),await y(`===== Bridge \u66F4\u65B0\u5F00\u59CB\uFF1Av${T()} -> latest\uFF08\u64CD\u4F5C ${r.operationId}\uFF09 =====`);const e=await _e();if(e.stdout&&await y(e.stdout),e.stderr&&await y(e.stderr),e.exitCode===0){const s=await Ce(),n=r.latestVersion||s||T();await le(n,`Bridge \u5DF2\u66F4\u65B0\u81F3 ${n}\u3002`).catch(a=>{const i=a;d.warn(`[update-bridge] \u8FFD\u52A0 changelog \u6BB5\u5931\u8D25\uFF08\u5DF2\u5FFD\u7565\uFF09: ${i.message}`)}),await y("===== Bridge \u66F4\u65B0\u6210\u529F\uFF0C\u51C6\u5907\u81EA\u52A8\u91CD\u542F ===== ");const o={...t,status:"restarting",message:"Bridge \u66F4\u65B0\u5B8C\u6210\uFF0C\u6B63\u5728\u91CD\u542F..."};await B(o),d.info("[update-bridge] \u6B63\u5728\u6267\u884C awsb service restart"),G(),setTimeout(()=>{if(process.exitCode=42,v){v("BRIDGE_UPDATE_RESTART",!0).catch(a=>{const i=a;d.error("[update-bridge] graceful shutdown failed:",i),process.exit(42)});return}process.exit(42)},J)}else await y(`===== Bridge \u66F4\u65B0\u5931\u8D25\uFF1A\u9000\u51FA\u7801 ${e.exitCode??"unknown"} =====`),await B({...t,status:"failed",message:`Bridge \u66F4\u65B0\u5931\u8D25\uFF0Cnpm \u9000\u51FA\u7801: ${e.exitCode??"unknown"}`,error:e.stderr||e.stdout}),await K()}catch(e){const s=e;d.error("[update-bridge] background update failed:",s),await y(`===== Bridge \u66F4\u65B0\u5F02\u5E38\uFF1A${s.message} =====`).catch(()=>{}),await B({...t,status:"failed",message:"Bridge \u66F4\u65B0\u5931\u8D25\u3002",error:s.message||"update bridge failed"}),await K()}}async function Q(r){const t=await import("node:fs/promises"),e=await import("node:path"),s=await import("node:os"),n=e.join(s.tmpdir(),"agentswork-runtime-bridge",".preserve-sessions");if(r){await t.mkdir(e.dirname(n),{recursive:!0}),await t.writeFile(n,JSON.stringify({preserveSessions:!0,timestamp:new Date().toISOString()}),"utf-8"),d.info(`[prepare-restart] \u5DF2\u521B\u5EFA\u4FDD\u7559\u4F1A\u8BDD\u6807\u8BB0\u6587\u4EF6: ${n}`);return}try{await t.unlink(n),d.info("[prepare-restart] \u5DF2\u5220\u9664\u4FDD\u7559\u4F1A\u8BDD\u6807\u8BB0\u6587\u4EF6")}catch{}}function xe(r){return ee("md5").update(r,"utf8").digest("hex")}function Le(r){const t=r.trim().toLowerCase();if(!/^[a-f0-9]{32}$/.test(t))return!1;const e=Buffer.from(t,"hex");return L().map(s=>s.trim()).filter(Boolean).some(s=>{const n=Buffer.from(xe(s),"hex");return n.length===e.length&&te(n,e)})}function Pe(r){const e=L().length>0,s=T();return e?Le(r)?{status:200,body:{ok:!0,runtimeBridge:"healthy",version:s,connectionKeyMatched:!0,connectionKeyRequired:!0}}:{status:401,body:{ok:!1,error:"connection_key_mismatch",version:s,connectionKeyMatched:!1,connectionKeyRequired:!0}}:{status:200,body:{ok:!0,runtimeBridge:"healthy",version:s,connectionKeyMatched:!0,connectionKeyRequired:!1}}}function Fe(r,t){const s=t.trim()||"http://localhost:8080",n=/^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?(?:\/|$)/i.test(s);return{ok:!1,error:r.message||"scheduler ping failed",failureStage:"scheduler_ping",runtimeBridge:"healthy",schedulerBaseUrl:s,hint:n?"aws-runtime-bridge \u5DF2\u8FDE\u901A\uFF0C\u4F46\u5B83\u56DE\u8FDE\u8C03\u5EA6\u4E2D\u5FC3\u5931\u8D25\u3002\u8BF7\u5728 bridge \u542F\u52A8\u73AF\u5883\u4E2D\u5C06 AWS_RUNTIME_SCHEDULER_BASE_URL \u914D\u7F6E\u4E3A\u8BE5\u5B9E\u4F8B\u53EF\u8BBF\u95EE\u7684\u8C03\u5EA6\u4E2D\u5FC3\u5730\u5740\uFF0C\u4F8B\u5982 http://<server-host>:7380\u3002":"aws-runtime-bridge \u5DF2\u8FDE\u901A\uFF0C\u4F46\u5B83\u56DE\u8FDE\u8C03\u5EA6\u4E2D\u5FC3\u5931\u8D25\u3002\u8BF7\u786E\u8BA4 AWS_RUNTIME_SCHEDULER_BASE_URL \u6307\u5411\u7684\u8C03\u5EA6\u4E2D\u5FC3\u5730\u5740\u53EF\u4ECE bridge \u673A\u5668\u8BBF\u95EE\uFF0C\u5E76\u4E14\u8FD0\u884C\u65F6\u8BBF\u95EE\u4EE4\u724C\u4ECD\u6709\u6548\u3002"}}function $e(r){if(typeof r!="object"||r===null)return;const t=Reflect.get(r,"response");if(typeof t!="object"||t===null)return;const e=Reflect.get(t,"status");return typeof e=="number"?e:void 0}async function De(r,t){try{const e=await O.get(`${r}/api/runtime/ping`,{headers:{"X-Runtime-Token":t}});return{schedulerReachable:e.status>=200&&e.status<300,tokenRefreshed:!1}}catch(e){if($e(e)!==401)throw e;const s=await ce(r,P(t)?.identityKey);if(!s.success||!s.runtimeAccessToken)throw e;const n=await O.get(`${r}/api/runtime/ping`,{headers:{"X-Runtime-Token":s.runtimeAccessToken}});return{schedulerReachable:n.status>=200&&n.status<300,tokenRefreshed:!0}}}function Ke(r){return{ok:!1,error:"ambiguous_scheduler_base_url",failureStage:"scheduler_ping",runtimeBridge:"healthy",schedulerBaseUrl:"",autoRegisterTargetUrls:r,hint:"Multiple autoRegisterTargets.serverUrl values were found, so bridge cannot safely infer which scheduler /runtime/ping should call back. Set AWS_RUNTIME_SCHEDULER_BASE_URL explicitly, or use future schedulerId/token-bound multi-scheduler routing."}}function Me(r){return Object.fromEntries(Object.entries(r||{}).map(([t,e])=>[t,{installed:!!e?.installed,version:e?.version||null,executable:e?.executable||null,error:e?.error||null}]))}l.get("/check-update",async(r,t)=>{t.json(await z())}),l.get("/update-log",R,async(r,t)=>{try{const e=await je();t.json({ok:!0,...e})}catch(e){const s=e;d.error("[update-log] Error:",s),t.status(500).json({ok:!1,error:s.message||"read update log failed"})}}),l.get("/changelog",R,async(r,t)=>{const e=String(r.query?.version||"").trim();if(!e){t.status(400).json({ok:!1,error:"version is required"});return}try{const s=await ue(e);t.json({ok:!0,version:e,...s})}catch(s){const n=s;d.error("[changelog] Error:",n),t.status(500).json({ok:!1,error:n.message||"read changelog failed"})}}),l.get("/healthz",(r,t)=>{t.json({ok:!0,runtimeBridge:"healthy"})}),l.get("/connection-check",(r,t)=>{const e=String(r.header("X-Bridge-Connection-Key-MD5")||""),s=Pe(e);t.status(s.status).json(s.body)}),l.get("/server-config/summary",m,(r,t)=>{try{t.setHeader("Cache-Control","no-store"),t.json({ok:!0,targets:de()})}catch(e){const s=e;t.status(500).json({ok:!1,error:s.message||"read targets failed"})}}),l.post("/connection-key/rotate",m,async(r,t)=>{const e=r.body||{},s=x(e.serverUrl||r.header("X-Scheduler-Base-Url"),e.instanceId||r.header("X-Bridge-Instance-Id"),r.header("X-Bridge-Instance-Name")),n=await ge(e.connectionKey,{serverUrl:s.url,instanceId:String(e.instanceId||r.header("X-Bridge-Instance-Id")||"").trim(),credentialId:String(e.credentialId||"").trim()});t.status(n.status).json(n.body)}),l.get("/ping",m,async(r,t)=>{let e=x(r.header("X-Scheduler-Base-Url"),r.header("X-Bridge-Instance-Id"),r.header("X-Bridge-Instance-Name"));if(e.source==="ambiguous-auto-register-targets"){const n=P(r.header("X-Runtime-Token"));if(n)e={url:n.serverUrl,source:"token-match",ambiguousTargetUrls:[],userKey:n.userKey};else{const o=r.header("X-Bridge-Instance-Id");if(o){const a=ie(),i=Object.entries(a.registrations).find(([,u])=>u===o)?.[0];i&&(e={url:Ae(i),source:"instance-id-match",ambiguousTargetUrls:[]})}if(!e.url||e.source==="ambiguous-auto-register-targets"){t.status(400).json(Ke(e.ambiguousTargetUrls));return}}}const s=e.url;try{const n=Be(s,e.userKey);if(!n){t.status(401).json({ok:!1,error:"runtime_access_token_required"});return}const o=await De(s,n);t.json({ok:!0,runtimeBridge:"healthy",version:T(),schedulerReachable:o.schedulerReachable,tokenRefreshed:o.tokenRefreshed,schedulerBaseUrl:s,instanceId:e.instanceId,instanceName:e.instanceName})}catch(n){const o=n;t.status(502).json(Fe(o,s))}}),l.post("/init-instance",m,q,async(r,t)=>{const{agentId:e,workspacePath:s,skillEnabled:n,mcpEnabled:o,ccSwitchEnabledTools:a,skillPackage:i,skillPackages:u,pluginPackages:h,mcpServers:w}=r.body||{};if(!e||!s){t.status(400).json({error:"agentId and workspacePath are required"});return}try{const g=X(r);ae(e,g),d.info("[init-instance] Request received",{agentId:String(e),workspacePath:String(s),tenantId:g.tenantId,userId:g.userId,skillEnabled:n,mcpEnabled:o,ccSwitchEnabledTools:a});const c=await Se(e,s,{skillEnabled:n,mcpEnabled:o,ccSwitchEnabledTools:a,skillPackage:i,skillPackages:u,pluginPackages:h,mcpServers:w,scope:g});d.info("[init-instance] Initialization completed",{agentId:String(e),ok:c.ok,logCount:c.logs?.length||0,enabledTools:c.enabledTools}),t.json(c)}catch(g){const c=g;if(d.error("[init-instance] Initialization failed",{agentId:String(e),workspacePath:String(s),error:c?.message||String(g),stack:c?.stack||null}),c?.message.includes("agent-ownership-mismatch")){t.status(403).json({ok:!1,error:"agent-ownership-mismatch"});return}t.status(400).json({error:c?.message||"initialize instance failed"})}}),l.post("/features",m,async(r,t)=>{const{agentId:e,features:s}=r.body||{};if(!e){t.status(400).json({error:"agentId is required"});return}pe(String(e),s),r.body.inheritedFeatures!==void 0&&me(String(e),r.body.inheritedFeatures),r.body.capabilities!==void 0&&fe(String(e),r.body.capabilities),r.body.capabilitiesInherited!==void 0&&he(String(e),r.body.capabilitiesInherited),d.info("[instance/features] \u80FD\u529B\u5F00\u5173\u5DF2\u66F4\u65B0",{agentId:String(e),features:F(String(e)),inheritedFeatures:ye(String(e))});let n=0;for(const o of ne.getAll())if(typeof o.applyAgentFeatures=="function")try{await o.applyAgentFeatures(String(e)),n+=1}catch(a){d.error("[instance/features] adapter \u70ED\u540C\u6B65\u5931\u8D25",{agentId:String(e),providerId:o.providerId,error:a?.message||String(a)})}t.json({ok:!0,agentId:String(e),features:F(String(e)),hotSyncedAdapters:n})}),l.post("/cc-switch/state",m,q,async(r,t)=>{const{agentId:e}=r.body||{};if(!e){t.status(400).json({error:"agentId is required"});return}try{const s=X(r);d.info("[cc-switch/state] Request received",{agentId:String(e)});const n=await A(e),o=Ee(n.enabledTools||[]);d.info("[cc-switch/state] Detecting SDK statuses",{agentId:String(e),detectionTargets:o});const a=await E(o),i=ve.forScope(s),u=Ie.forScope(s),h=await ke.listDeployed(s),w=i.listDeployed(),g=u.listDeployed(),c=U(s,e,"skills"),b=U(s,e,"mcp"),I=U(s,e,"plugins"),f=w.map(p=>({...p,enabled:j(s,e,"skills",p.skillName),source:c[p.skillName]!==void 0?"agent":"bridge"})),S=g.map(p=>({...p,enabled:j(s,e,"mcp",p.name),source:b[p.name]!==void 0?"agent":"bridge"})),Z=h.map(p=>({...p,enabled:j(s,e,"plugins",p.pluginName),source:I[p.pluginName]!==void 0?"agent":"bridge"}));d.info("[cc-switch/state] SDK status detection completed",{agentId:String(e),toolStatus:Me(a)}),t.json({ok:!0,agentId:String(e),imported:{mcpServers:0,skills:0},deployedPlugins:Z,deployedSkills:f,deployedMcp:S,state:{...n,toolStatus:a}})}catch(s){const n=s;d.error("[cc-switch/state] Failed to load state",{agentId:String(e),error:n?.message||String(s),stack:n?.stack||null}),t.status(400).json({error:n?.message||"load state failed"})}}),l.post("/tools/install",m,async(r,t)=>{const{agentId:e,tools:s}=r.body||{};if(!e){t.status(400).json({error:"agentId is required"});return}const n=Array.isArray(s)?s.map(i=>String(i||"").trim().toLowerCase()).filter(Boolean):[],o=new Set(M),a=n.filter(i=>o.has(i));if(a.length===0){t.status(400).json({error:`tools must include ${M.join(", ")}`});return}try{const i=await A(e),u=Array.from(new Set([...i.enabledTools||[],...a])),h=await Te(a),g={...await E(u),...h},c=await $(e,{...i,enabledTools:u,toolStatus:g}),b=new Set(a),I=Object.entries(g).filter(([f])=>b.has(f)).filter(([,f])=>!f.installed).map(([f,S])=>({tool:f,error:S.error||null}));t.json({ok:I.length===0,agentId:String(e),toolStatus:g,failedTools:I,state:c})}catch(i){const u=i;t.status(400).json({error:u?.message||"install tools failed"})}}),l.post("/tools/uninstall",m,async(r,t)=>{const{agentId:e,tools:s}=r.body||{};if(!e){t.status(400).json({error:"agentId is required"});return}const n=Array.isArray(s)?s.map(i=>String(i||"").trim().toLowerCase()).filter(Boolean):[],o=new Set(N),a=n.filter(i=>o.has(i));if(a.length===0){t.status(400).json({error:`tools must include ${N.join(", ")}`});return}try{const i=await A(e),u=new Set(a),h=(i.enabledTools||[]).filter(S=>!u.has(String(S||"").trim().toLowerCase())),w=await Re(a),c={...await E([...h,...a]),...w},b=a.filter(S=>c[S]?.installed),I=b.length>0?i.enabledTools||[]:h,f=await $(e,{...i,enabledTools:I,toolStatus:c});if(b.length>0){t.status(400).json({ok:!1,agentId:String(e),error:"uninstall incomplete",toolStatus:c,state:f});return}t.json({ok:!0,agentId:String(e),toolStatus:c,state:f})}catch(i){const u=i;t.status(400).json({error:u?.message||"uninstall tools failed"})}}),l.post("/prepare-restart",m,async(r,t)=>{const{preserveSessions:e=!0}=r.body||{};try{d.info(`[prepare-restart] \u6536\u5230\u51C6\u5907\u91CD\u542F\u901A\u77E5\uFF0CpreserveSessions=${e}`),await Q(!!e),t.json({ok:!0,preserveSessions:e,message:e?"Bridge will preserve sessions for aws-mcp-server restart":"Bridge will clean up sessions on shutdown"})}catch(s){const n=s;d.error("[prepare-restart] Error:",n),t.status(500).json({error:n.message})}});const Ne={update:15*60*1e3,restart:5*60*1e3};async function Xe(r){try{const t=await we(),e=Date.now(),s=Ne[r],n=[];for(const o of t.operations){if(o.operationType!==r||!["accepted","running","restarting"].includes(o.status))continue;const a=new Date(o.updatedAt).getTime();if(!Number.isNaN(a)&&e-a<=s)return{operationId:o.operationId};n.push(o.operationId)}for(const o of n)try{await be(o),d.info(`[${r}-bridge] \u5DF2\u6E05\u7406\u8D85\u65F6\u672A\u4EA4\u4ED8\u7684\u9648\u65E7\u64CD\u4F5C: ${o}`)}catch(a){const i=a;d.warn(`[${r}-bridge] \u6E05\u7406\u9648\u65E7\u64CD\u4F5C\u5931\u8D25\uFF0C\u7EE7\u7EED\u653E\u884C: ${o}, ${i.message}`)}return null}catch(t){const e=t;return d.warn(`[${r}-bridge] \u5728\u9014\u64CD\u4F5C\u68C0\u67E5\u5931\u8D25\uFF0C\u653E\u884C\u8BF7\u6C42: ${e.message}`),null}}async function Y(r,t,e){const s=await Xe(r);return s?(e.status(409).json({error:t,operationId:s.operationId}),!0):!1}l.post("/update-bridge",R,async(r,t)=>{const e=H(r.body?.requestedByUserId);try{if(!e){t.status(400).json({error:"requestedByUserId is required"});return}if(oe()){t.status(409).json({error:"desktop_app_managed_update",message:"\u684C\u9762\u7248\u7531\u5E94\u7528\u81EA\u8EAB\u7684\u66F4\u65B0\u673A\u5236\u7BA1\u7406\uFF0C\u8BF7\u901A\u8FC7\u6258\u76D8\u83DC\u5355\u6216\u66F4\u65B0\u63D0\u793A\u5B8C\u6210\u5347\u7EA7\u3002"});return}if(await Y("update","\u5B9E\u4F8B Bridge \u66F4\u65B0\u6B63\u5728\u6267\u884C\u4E2D\uFF0C\u8BF7\u52FF\u91CD\u590D\u89E6\u53D1",t))return;const s=await z(),n=D({operationType:"update",requestedByUserId:e,schedulerBaseUrl:r.body?.schedulerBaseUrl,runtimeBridgeBaseUrl:V(r),status:"accepted",message:"Bridge \u66F4\u65B0\u8BF7\u6C42\u5DF2\u6536\u5230\uFF0C\u5C06\u5728\u540E\u53F0\u6267\u884C\u5E76\u81EA\u52A8\u91CD\u542F\u3002",latestVersion:s.ok&&s.latestVersion||void 0});await B(n),Oe(n),t.json({ok:!0,operationId:n.operationId,message:"Bridge \u66F4\u65B0\u5DF2\u5728\u540E\u53F0\u5F00\u59CB\uFF0C\u66F4\u65B0\u5B8C\u6210\u540E\u5C06\u81EA\u52A8\u91CD\u542F\u5B9E\u4F8B Bridge\u3002"})}catch(s){const n=s;d.error("[update-bridge] Error:",n),t.status(500).json({error:n.message||"update bridge failed"})}}),l.post("/restart-bridge",R,async(r,t)=>{const{preserveSessions:e=!0}=r.body||{},s=H(r.body?.requestedByUserId);try{if(!s){t.status(400).json({error:"requestedByUserId is required"});return}if(await Y("restart","\u5B9E\u4F8B Bridge \u6B63\u5728\u91CD\u542F\u4E2D\uFF0C\u8BF7\u7A0D\u5019\u518D\u8BD5",t))return;d.info(`[restart-bridge] \u6536\u5230\u5B9E\u4F8B Bridge \u91CD\u542F\u8BF7\u6C42\uFF0CpreserveSessions=${e}`);const n=D({operationType:"restart",requestedByUserId:s,schedulerBaseUrl:r.body?.schedulerBaseUrl,runtimeBridgeBaseUrl:V(r),status:"accepted",message:"Bridge \u5DF2\u6536\u5230\u91CD\u542F\u8BF7\u6C42\uFF1B\u4E0B\u6B21\u542F\u52A8\u540E\u4F1A\u53D1\u9001\u786E\u8BA4\u901A\u77E5\u3002"});await B(n),await Q(!!e),t.json({ok:!0,operationId:n.operationId,preserveSessions:!!e,message:"Bridge \u6B63\u5728\u91CD\u542F\uFF0C\u670D\u52A1\u7BA1\u7406\u5668\u4F1A\u81EA\u52A8\u62C9\u8D77\u5B9E\u4F8B\uFF1B\u4E0B\u6B21\u542F\u52A8\u540E\u4F1A\u901A\u8FC7\u6D88\u606F\u4E2D\u5FC3\u901A\u77E5\u7ED3\u679C\u3002"}),d.info("[restart-bridge] \u6B63\u5728\u6267\u884C awsb service restart"),G(),setTimeout(()=>{if(process.exitCode=42,v){v("PANEL_RESTART",!!e).catch(o=>{const a=o;d.error("[restart-bridge] graceful shutdown failed:",a),process.exit(42)});return}process.exit(42)},J)}catch(n){const o=n;d.error("[restart-bridge] Error:",o),t.status(500).json({error:o.message||"restart bridge failed"})}});export{Ke as buildAmbiguousSchedulerBaseUrlResponse,Pe as buildConnectionCheckResponse,Fe as buildSchedulerPingFailureResponse,l as instanceRouter,bt as setGracefulShutdownFn,Me as summarizeToolStatus};
|
|
2
2
|
|
package/dist/routes/jdks.d.ts
CHANGED
package/dist/routes/launch.d.ts
CHANGED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{Router as a}from"express";import{validateToken as e}from"../middleware/auth.js";import{listLspProcesses as n}from"../routes/lsp.js";import{getLspInstallTask as p,installLspProvider as d,isInstallBusy as l,listLspInstallTasks as u,uninstallLspProvider as m}from"../services/lsp/install-manager.js";import{listProviderSummaries as c}from"../services/lsp/provider-registry.js";import{createLogger as v}from"../utils/logger.js";const g=v("lsp-providers"),i=a();i.get("/lsp/providers",e,async(t,r)=>{try{const s=await c();r.json({ok:!0,providers:s,installTasks:u(),busy:l()})}catch(s){r.status(500).json({error:s.message})}}),i.post("/lsp/providers/install",e,async(t,r)=>{const s=String(t.body?.providerId||"").trim().toLowerCase();if(!s){r.status(400).json({error:"providerId is required"});return}try{const o=await d(s,{registry:String(t.body?.registry||"").trim()||void 0});r.json({ok:o.phase==="done",task:o})}catch(o){g.warn(`[lsp-providers] \u5B89\u88C5\u5931\u8D25 provider=${s}: ${o.message}`),r.status(400).json({error:o.message})}}),i.post("/lsp/providers/uninstall",e,async(t,r)=>{const s=String(t.body?.providerId||"").trim().toLowerCase();if(!s){r.status(400).json({error:"providerId is required"});return}try{const o=await m(s);r.json({ok:!0,task:o})}catch(o){r.status(400).json({error:o.message})}}),i.get("/lsp/providers/task/:providerId",e,(t,r)=>{const s=p(t.params.providerId);if(!s){r.status(404).json({error:"task not found"});return}r.json({ok:!0,task:s})}),i.get("/lsp/processes",e,(t,r)=>{r.json({ok:!0,processes:n()})});export{i as lspProvidersRouter};
|
|
2
|
+
|
package/dist/routes/lsp.d.ts
CHANGED
|
@@ -1,6 +1,47 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
1
15
|
import type { IncomingMessage } from "node:http";
|
|
2
16
|
import type { Socket } from "node:net";
|
|
17
|
+
import { type LspProviderSpec } from "../services/lsp/provider-registry.js";
|
|
18
|
+
interface LspSocketContext {
|
|
19
|
+
workspacePath: string;
|
|
20
|
+
filePath: string;
|
|
21
|
+
language: string;
|
|
22
|
+
providerId: string;
|
|
23
|
+
clientId: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export declare function resolveLspSocketContext(requestUrl: URL): {
|
|
27
|
+
ctx: LspSocketContext;
|
|
28
|
+
provider: LspProviderSpec | null;
|
|
29
|
+
error?: string;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
3
34
|
|
|
4
35
|
export declare function attachLspWebSocketServer(server: {
|
|
5
36
|
on: (event: "upgrade", listener: (request: IncomingMessage, socket: Socket, head: Buffer) => void) => unknown;
|
|
6
37
|
}): void;
|
|
38
|
+
|
|
39
|
+
export declare function listLspProcesses(): Array<{
|
|
40
|
+
key: string;
|
|
41
|
+
providerId: string;
|
|
42
|
+
running: boolean;
|
|
43
|
+
connected: boolean;
|
|
44
|
+
}>;
|
|
45
|
+
|
|
46
|
+
export declare function disposeAllLspProcesses(): void;
|
|
47
|
+
export {};
|