@alan-ai-hq/agent-manager 0.1.113 → 0.1.115
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/LICENSE +88 -0
- package/dist/__tests__/binding-switch-git.test.d.ts +2 -0
- package/dist/__tests__/binding-switch-git.test.d.ts.map +1 -0
- package/dist/__tests__/binding-switch-git.test.js +555 -0
- package/dist/__tests__/binding-switch-git.test.js.map +1 -0
- package/dist/__tests__/cli-ensure-install.test.d.ts +2 -0
- package/dist/__tests__/cli-ensure-install.test.d.ts.map +1 -0
- package/dist/__tests__/cli-ensure-install.test.js +177 -0
- package/dist/__tests__/cli-ensure-install.test.js.map +1 -0
- package/dist/__tests__/cli-executable.test.d.ts +2 -0
- package/dist/__tests__/cli-executable.test.d.ts.map +1 -0
- package/dist/__tests__/cli-executable.test.js +155 -0
- package/dist/__tests__/cli-executable.test.js.map +1 -0
- package/dist/__tests__/cli-executable.windows.test.d.ts +2 -0
- package/dist/__tests__/cli-executable.windows.test.d.ts.map +1 -0
- package/dist/__tests__/cli-executable.windows.test.js +123 -0
- package/dist/__tests__/cli-executable.windows.test.js.map +1 -0
- package/dist/__tests__/cli-help.test.d.ts +2 -0
- package/dist/__tests__/cli-help.test.d.ts.map +1 -0
- package/dist/__tests__/cli-help.test.js +47 -0
- package/dist/__tests__/cli-help.test.js.map +1 -0
- package/dist/__tests__/computer-readiness.test.d.ts +2 -0
- package/dist/__tests__/computer-readiness.test.d.ts.map +1 -0
- package/dist/__tests__/computer-readiness.test.js +63 -0
- package/dist/__tests__/computer-readiness.test.js.map +1 -0
- package/dist/__tests__/core-agent-provider-auth-env.test.d.ts +2 -0
- package/dist/__tests__/core-agent-provider-auth-env.test.d.ts.map +1 -0
- package/dist/__tests__/core-agent-provider-auth-env.test.js +93 -0
- package/dist/__tests__/core-agent-provider-auth-env.test.js.map +1 -0
- package/dist/__tests__/core-agent-workflow-context.test.d.ts +2 -0
- package/dist/__tests__/core-agent-workflow-context.test.d.ts.map +1 -0
- package/dist/__tests__/core-agent-workflow-context.test.js +139 -0
- package/dist/__tests__/core-agent-workflow-context.test.js.map +1 -0
- package/dist/__tests__/daemon-native-hooks.test.d.ts +2 -0
- package/dist/__tests__/daemon-native-hooks.test.d.ts.map +1 -0
- package/dist/__tests__/daemon-native-hooks.test.js +94 -0
- package/dist/__tests__/daemon-native-hooks.test.js.map +1 -0
- package/dist/__tests__/daemon-reconcile-ask.test.d.ts +2 -0
- package/dist/__tests__/daemon-reconcile-ask.test.d.ts.map +1 -0
- package/dist/__tests__/daemon-reconcile-ask.test.js +100 -0
- package/dist/__tests__/daemon-reconcile-ask.test.js.map +1 -0
- package/dist/__tests__/daemon-runner-status.test.d.ts +2 -0
- package/dist/__tests__/daemon-runner-status.test.d.ts.map +1 -0
- package/dist/__tests__/daemon-runner-status.test.js +102 -0
- package/dist/__tests__/daemon-runner-status.test.js.map +1 -0
- package/dist/__tests__/daemon-socket.integration.test.d.ts +2 -0
- package/dist/__tests__/daemon-socket.integration.test.d.ts.map +1 -0
- package/dist/__tests__/daemon-socket.integration.test.js +435 -0
- package/dist/__tests__/daemon-socket.integration.test.js.map +1 -0
- package/dist/__tests__/daemon.test.d.ts +2 -0
- package/dist/__tests__/daemon.test.d.ts.map +1 -0
- package/dist/__tests__/daemon.test.js +4884 -0
- package/dist/__tests__/daemon.test.js.map +1 -0
- package/dist/__tests__/ensure-shared-checkout-branch.test.d.ts +2 -0
- package/dist/__tests__/ensure-shared-checkout-branch.test.d.ts.map +1 -0
- package/dist/__tests__/ensure-shared-checkout-branch.test.js +129 -0
- package/dist/__tests__/ensure-shared-checkout-branch.test.js.map +1 -0
- package/dist/__tests__/event-journal-stress.test.d.ts +2 -0
- package/dist/__tests__/event-journal-stress.test.d.ts.map +1 -0
- package/dist/__tests__/event-journal-stress.test.js +124 -0
- package/dist/__tests__/event-journal-stress.test.js.map +1 -0
- package/dist/__tests__/event-journal.test.d.ts +2 -0
- package/dist/__tests__/event-journal.test.d.ts.map +1 -0
- package/dist/__tests__/event-journal.test.js +264 -0
- package/dist/__tests__/event-journal.test.js.map +1 -0
- package/dist/__tests__/event-outbox-persist-failure.test.d.ts +2 -0
- package/dist/__tests__/event-outbox-persist-failure.test.d.ts.map +1 -0
- package/dist/__tests__/event-outbox-persist-failure.test.js +143 -0
- package/dist/__tests__/event-outbox-persist-failure.test.js.map +1 -0
- package/dist/__tests__/event-outbox.test.d.ts +2 -0
- package/dist/__tests__/event-outbox.test.d.ts.map +1 -0
- package/dist/__tests__/event-outbox.test.js +602 -0
- package/dist/__tests__/event-outbox.test.js.map +1 -0
- package/dist/__tests__/execution-activity-tracker.test.d.ts +2 -0
- package/dist/__tests__/execution-activity-tracker.test.d.ts.map +1 -0
- package/dist/__tests__/execution-activity-tracker.test.js +64 -0
- package/dist/__tests__/execution-activity-tracker.test.js.map +1 -0
- package/dist/__tests__/execution-lease-client.test.d.ts +2 -0
- package/dist/__tests__/execution-lease-client.test.d.ts.map +1 -0
- package/dist/__tests__/execution-lease-client.test.js +274 -0
- package/dist/__tests__/execution-lease-client.test.js.map +1 -0
- package/dist/__tests__/fake-provider-cli.test.d.ts +2 -0
- package/dist/__tests__/fake-provider-cli.test.d.ts.map +1 -0
- package/dist/__tests__/fake-provider-cli.test.js +46 -0
- package/dist/__tests__/fake-provider-cli.test.js.map +1 -0
- package/dist/__tests__/generated-images.test.d.ts +2 -0
- package/dist/__tests__/generated-images.test.d.ts.map +1 -0
- package/dist/__tests__/generated-images.test.js +132 -0
- package/dist/__tests__/generated-images.test.js.map +1 -0
- package/dist/__tests__/helpers/sandbox-outbox.d.ts +3 -0
- package/dist/__tests__/helpers/sandbox-outbox.d.ts.map +1 -0
- package/dist/__tests__/helpers/sandbox-outbox.js +23 -0
- package/dist/__tests__/helpers/sandbox-outbox.js.map +1 -0
- package/dist/__tests__/install-command.test.d.ts +2 -0
- package/dist/__tests__/install-command.test.d.ts.map +1 -0
- package/dist/__tests__/install-command.test.js +185 -0
- package/dist/__tests__/install-command.test.js.map +1 -0
- package/dist/__tests__/integration-cli.test.d.ts +2 -0
- package/dist/__tests__/integration-cli.test.d.ts.map +1 -0
- package/dist/__tests__/integration-cli.test.js +150 -0
- package/dist/__tests__/integration-cli.test.js.map +1 -0
- package/dist/__tests__/lifecycle-contracts.test.d.ts +2 -0
- package/dist/__tests__/lifecycle-contracts.test.d.ts.map +1 -0
- package/dist/__tests__/lifecycle-contracts.test.js +66 -0
- package/dist/__tests__/lifecycle-contracts.test.js.map +1 -0
- package/dist/__tests__/lifecycle-logger.test.d.ts +2 -0
- package/dist/__tests__/lifecycle-logger.test.d.ts.map +1 -0
- package/dist/__tests__/lifecycle-logger.test.js +49 -0
- package/dist/__tests__/lifecycle-logger.test.js.map +1 -0
- package/dist/__tests__/local-daemon-lifecycle.test.d.ts +2 -0
- package/dist/__tests__/local-daemon-lifecycle.test.d.ts.map +1 -0
- package/dist/__tests__/local-daemon-lifecycle.test.js +594 -0
- package/dist/__tests__/local-daemon-lifecycle.test.js.map +1 -0
- package/dist/__tests__/local-git-status.test.d.ts +2 -0
- package/dist/__tests__/local-git-status.test.d.ts.map +1 -0
- package/dist/__tests__/local-git-status.test.js +31 -0
- package/dist/__tests__/local-git-status.test.js.map +1 -0
- package/dist/__tests__/mcp-config-adapters-files.test.d.ts +2 -0
- package/dist/__tests__/mcp-config-adapters-files.test.d.ts.map +1 -0
- package/dist/__tests__/mcp-config-adapters-files.test.js +486 -0
- package/dist/__tests__/mcp-config-adapters-files.test.js.map +1 -0
- package/dist/__tests__/mcp-config-adapters.test.d.ts +2 -0
- package/dist/__tests__/mcp-config-adapters.test.d.ts.map +1 -0
- package/dist/__tests__/mcp-config-adapters.test.js +492 -0
- package/dist/__tests__/mcp-config-adapters.test.js.map +1 -0
- package/dist/__tests__/mcp-sync.test.d.ts +2 -0
- package/dist/__tests__/mcp-sync.test.d.ts.map +1 -0
- package/dist/__tests__/mcp-sync.test.js +21 -0
- package/dist/__tests__/mcp-sync.test.js.map +1 -0
- package/dist/__tests__/model-scan-runner.test.d.ts +2 -0
- package/dist/__tests__/model-scan-runner.test.d.ts.map +1 -0
- package/dist/__tests__/model-scan-runner.test.js +80 -0
- package/dist/__tests__/model-scan-runner.test.js.map +1 -0
- package/dist/__tests__/native-hook-command.test.d.ts +2 -0
- package/dist/__tests__/native-hook-command.test.d.ts.map +1 -0
- package/dist/__tests__/native-hook-command.test.js +386 -0
- package/dist/__tests__/native-hook-command.test.js.map +1 -0
- package/dist/__tests__/native-hook-config.test.d.ts +2 -0
- package/dist/__tests__/native-hook-config.test.d.ts.map +1 -0
- package/dist/__tests__/native-hook-config.test.js +374 -0
- package/dist/__tests__/native-hook-config.test.js.map +1 -0
- package/dist/__tests__/native-hook-drain.test.d.ts +2 -0
- package/dist/__tests__/native-hook-drain.test.d.ts.map +1 -0
- package/dist/__tests__/native-hook-drain.test.js +197 -0
- package/dist/__tests__/native-hook-drain.test.js.map +1 -0
- package/dist/__tests__/native-hook-inbox.test.d.ts +2 -0
- package/dist/__tests__/native-hook-inbox.test.d.ts.map +1 -0
- package/dist/__tests__/native-hook-inbox.test.js +139 -0
- package/dist/__tests__/native-hook-inbox.test.js.map +1 -0
- package/dist/__tests__/native-session.test.d.ts +2 -0
- package/dist/__tests__/native-session.test.d.ts.map +1 -0
- package/dist/__tests__/native-session.test.js +41 -0
- package/dist/__tests__/native-session.test.js.map +1 -0
- package/dist/__tests__/native-stream-outbox.test.d.ts +2 -0
- package/dist/__tests__/native-stream-outbox.test.d.ts.map +1 -0
- package/dist/__tests__/native-stream-outbox.test.js +231 -0
- package/dist/__tests__/native-stream-outbox.test.js.map +1 -0
- package/dist/__tests__/native-stream-platform.test.d.ts +2 -0
- package/dist/__tests__/native-stream-platform.test.d.ts.map +1 -0
- package/dist/__tests__/native-stream-platform.test.js +20 -0
- package/dist/__tests__/native-stream-platform.test.js.map +1 -0
- package/dist/__tests__/native-transcript-cursor.test.d.ts +2 -0
- package/dist/__tests__/native-transcript-cursor.test.d.ts.map +1 -0
- package/dist/__tests__/native-transcript-cursor.test.js +60 -0
- package/dist/__tests__/native-transcript-cursor.test.js.map +1 -0
- package/dist/__tests__/native-transcript-stream.test.d.ts +2 -0
- package/dist/__tests__/native-transcript-stream.test.d.ts.map +1 -0
- package/dist/__tests__/native-transcript-stream.test.js +378 -0
- package/dist/__tests__/native-transcript-stream.test.js.map +1 -0
- package/dist/__tests__/network-diagnostics.test.d.ts +2 -0
- package/dist/__tests__/network-diagnostics.test.d.ts.map +1 -0
- package/dist/__tests__/network-diagnostics.test.js +26 -0
- package/dist/__tests__/network-diagnostics.test.js.map +1 -0
- package/dist/__tests__/observable-map.test.d.ts +2 -0
- package/dist/__tests__/observable-map.test.d.ts.map +1 -0
- package/dist/__tests__/observable-map.test.js +54 -0
- package/dist/__tests__/observable-map.test.js.map +1 -0
- package/dist/__tests__/outbox-baseline.bench.test.d.ts +2 -0
- package/dist/__tests__/outbox-baseline.bench.test.d.ts.map +1 -0
- package/dist/__tests__/outbox-baseline.bench.test.js +108 -0
- package/dist/__tests__/outbox-baseline.bench.test.js.map +1 -0
- package/dist/__tests__/provider-auth-preflight.test.d.ts +2 -0
- package/dist/__tests__/provider-auth-preflight.test.d.ts.map +1 -0
- package/dist/__tests__/provider-auth-preflight.test.js +123 -0
- package/dist/__tests__/provider-auth-preflight.test.js.map +1 -0
- package/dist/__tests__/provider-model-discovery.test.d.ts +2 -0
- package/dist/__tests__/provider-model-discovery.test.d.ts.map +1 -0
- package/dist/__tests__/provider-model-discovery.test.js +1071 -0
- package/dist/__tests__/provider-model-discovery.test.js.map +1 -0
- package/dist/__tests__/resource-diagnostics.test.d.ts +2 -0
- package/dist/__tests__/resource-diagnostics.test.d.ts.map +1 -0
- package/dist/__tests__/resource-diagnostics.test.js +138 -0
- package/dist/__tests__/resource-diagnostics.test.js.map +1 -0
- package/dist/__tests__/resume-fallback-env.test.d.ts +2 -0
- package/dist/__tests__/resume-fallback-env.test.d.ts.map +1 -0
- package/dist/__tests__/resume-fallback-env.test.js +28 -0
- package/dist/__tests__/resume-fallback-env.test.js.map +1 -0
- package/dist/__tests__/run-journal.test.d.ts +2 -0
- package/dist/__tests__/run-journal.test.d.ts.map +1 -0
- package/dist/__tests__/run-journal.test.js +121 -0
- package/dist/__tests__/run-journal.test.js.map +1 -0
- package/dist/__tests__/run-start-gate.test.d.ts +2 -0
- package/dist/__tests__/run-start-gate.test.d.ts.map +1 -0
- package/dist/__tests__/run-start-gate.test.js +18 -0
- package/dist/__tests__/run-start-gate.test.js.map +1 -0
- package/dist/__tests__/runtime-capabilities-sync.test.d.ts +2 -0
- package/dist/__tests__/runtime-capabilities-sync.test.d.ts.map +1 -0
- package/dist/__tests__/runtime-capabilities-sync.test.js +66 -0
- package/dist/__tests__/runtime-capabilities-sync.test.js.map +1 -0
- package/dist/__tests__/runtime-config.test.d.ts +2 -0
- package/dist/__tests__/runtime-config.test.d.ts.map +1 -0
- package/dist/__tests__/runtime-config.test.js +137 -0
- package/dist/__tests__/runtime-config.test.js.map +1 -0
- package/dist/__tests__/runtime-presenter-heartbeat.test.d.ts +2 -0
- package/dist/__tests__/runtime-presenter-heartbeat.test.d.ts.map +1 -0
- package/dist/__tests__/runtime-presenter-heartbeat.test.js +253 -0
- package/dist/__tests__/runtime-presenter-heartbeat.test.js.map +1 -0
- package/dist/__tests__/runtime-registration.test.d.ts +2 -0
- package/dist/__tests__/runtime-registration.test.d.ts.map +1 -0
- package/dist/__tests__/runtime-registration.test.js +109 -0
- package/dist/__tests__/runtime-registration.test.js.map +1 -0
- package/dist/__tests__/sandbox-outbox-storage.test.d.ts +2 -0
- package/dist/__tests__/sandbox-outbox-storage.test.d.ts.map +1 -0
- package/dist/__tests__/sandbox-outbox-storage.test.js +81 -0
- package/dist/__tests__/sandbox-outbox-storage.test.js.map +1 -0
- package/dist/__tests__/sandbox-outbox.test.d.ts +2 -0
- package/dist/__tests__/sandbox-outbox.test.d.ts.map +1 -0
- package/dist/__tests__/sandbox-outbox.test.js +300 -0
- package/dist/__tests__/sandbox-outbox.test.js.map +1 -0
- package/dist/__tests__/sandbox-workflow-context.test.d.ts +2 -0
- package/dist/__tests__/sandbox-workflow-context.test.d.ts.map +1 -0
- package/dist/__tests__/sandbox-workflow-context.test.js +600 -0
- package/dist/__tests__/sandbox-workflow-context.test.js.map +1 -0
- package/dist/__tests__/service-manager-deadline.test.d.ts +2 -0
- package/dist/__tests__/service-manager-deadline.test.d.ts.map +1 -0
- package/dist/__tests__/service-manager-deadline.test.js +36 -0
- package/dist/__tests__/service-manager-deadline.test.js.map +1 -0
- package/dist/__tests__/service-manager.test.d.ts +2 -0
- package/dist/__tests__/service-manager.test.d.ts.map +1 -0
- package/dist/__tests__/service-manager.test.js +769 -0
- package/dist/__tests__/service-manager.test.js.map +1 -0
- package/dist/__tests__/service-stop-ownership.test.d.ts +2 -0
- package/dist/__tests__/service-stop-ownership.test.d.ts.map +1 -0
- package/dist/__tests__/service-stop-ownership.test.js +89 -0
- package/dist/__tests__/service-stop-ownership.test.js.map +1 -0
- package/dist/__tests__/skill-invocation.test.d.ts +2 -0
- package/dist/__tests__/skill-invocation.test.d.ts.map +1 -0
- package/dist/__tests__/skill-invocation.test.js +40 -0
- package/dist/__tests__/skill-invocation.test.js.map +1 -0
- package/dist/__tests__/sleep-inhibitor-real.test.d.ts +2 -0
- package/dist/__tests__/sleep-inhibitor-real.test.d.ts.map +1 -0
- package/dist/__tests__/sleep-inhibitor-real.test.js +68 -0
- package/dist/__tests__/sleep-inhibitor-real.test.js.map +1 -0
- package/dist/__tests__/sleep-inhibitor.test.d.ts +2 -0
- package/dist/__tests__/sleep-inhibitor.test.d.ts.map +1 -0
- package/dist/__tests__/sleep-inhibitor.test.js +236 -0
- package/dist/__tests__/sleep-inhibitor.test.js.map +1 -0
- package/dist/__tests__/status-local-workspace.test.d.ts +2 -0
- package/dist/__tests__/status-local-workspace.test.d.ts.map +1 -0
- package/dist/__tests__/status-local-workspace.test.js +121 -0
- package/dist/__tests__/status-local-workspace.test.js.map +1 -0
- package/dist/__tests__/suspension-detector.test.d.ts +2 -0
- package/dist/__tests__/suspension-detector.test.d.ts.map +1 -0
- package/dist/__tests__/suspension-detector.test.js +112 -0
- package/dist/__tests__/suspension-detector.test.js.map +1 -0
- package/dist/__tests__/web-presenter-parent-events.test.d.ts +2 -0
- package/dist/__tests__/web-presenter-parent-events.test.d.ts.map +1 -0
- package/dist/__tests__/web-presenter-parent-events.test.js +168 -0
- package/dist/__tests__/web-presenter-parent-events.test.js.map +1 -0
- package/dist/__tests__/workspace-probe-concurrency.test.d.ts +2 -0
- package/dist/__tests__/workspace-probe-concurrency.test.d.ts.map +1 -0
- package/dist/__tests__/workspace-probe-concurrency.test.js +54 -0
- package/dist/__tests__/workspace-probe-concurrency.test.js.map +1 -0
- package/dist/__tests__/workspace-relocation.test.d.ts +2 -0
- package/dist/__tests__/workspace-relocation.test.d.ts.map +1 -0
- package/dist/__tests__/workspace-relocation.test.js +66 -0
- package/dist/__tests__/workspace-relocation.test.js.map +1 -0
- package/dist/__tests__/workspace-rpc.test.d.ts +2 -0
- package/dist/__tests__/workspace-rpc.test.d.ts.map +1 -0
- package/dist/__tests__/workspace-rpc.test.js +90 -0
- package/dist/__tests__/workspace-rpc.test.js.map +1 -0
- package/dist/__tests__/workspace-run-lease.test.d.ts +2 -0
- package/dist/__tests__/workspace-run-lease.test.d.ts.map +1 -0
- package/dist/__tests__/workspace-run-lease.test.js +331 -0
- package/dist/__tests__/workspace-run-lease.test.js.map +1 -0
- package/dist/__tests__/ws-client-connection.test.d.ts +2 -0
- package/dist/__tests__/ws-client-connection.test.d.ts.map +1 -0
- package/dist/__tests__/ws-client-connection.test.js +295 -0
- package/dist/__tests__/ws-client-connection.test.js.map +1 -0
- package/dist/__tests__/ws-client-heartbeat-volatile.test.d.ts +2 -0
- package/dist/__tests__/ws-client-heartbeat-volatile.test.d.ts.map +1 -0
- package/dist/__tests__/ws-client-heartbeat-volatile.test.js +63 -0
- package/dist/__tests__/ws-client-heartbeat-volatile.test.js.map +1 -0
- package/dist/__tests__/ws-client-heartbeat.integration.test.d.ts +2 -0
- package/dist/__tests__/ws-client-heartbeat.integration.test.d.ts.map +1 -0
- package/dist/__tests__/ws-client-heartbeat.integration.test.js +236 -0
- package/dist/__tests__/ws-client-heartbeat.integration.test.js.map +1 -0
- package/dist/__tests__/ws-client-user-message-ack.integration.test.d.ts +2 -0
- package/dist/__tests__/ws-client-user-message-ack.integration.test.d.ts.map +1 -0
- package/dist/__tests__/ws-client-user-message-ack.integration.test.js +211 -0
- package/dist/__tests__/ws-client-user-message-ack.integration.test.js.map +1 -0
- package/dist/antigravity-api-key-config.d.ts +2 -0
- package/dist/antigravity-api-key-config.d.ts.map +1 -0
- package/dist/antigravity-api-key-config.js +79 -0
- package/dist/antigravity-api-key-config.js.map +1 -0
- package/dist/binding-switch-git.d.ts +26 -0
- package/dist/binding-switch-git.d.ts.map +1 -0
- package/dist/binding-switch-git.js +871 -0
- package/dist/binding-switch-git.js.map +1 -0
- package/dist/cli-ensure-install.d.ts +34 -0
- package/dist/cli-ensure-install.d.ts.map +1 -0
- package/dist/cli-ensure-install.js +154 -0
- package/dist/cli-ensure-install.js.map +1 -0
- package/dist/cli-executable.d.ts +44 -0
- package/dist/cli-executable.d.ts.map +1 -0
- package/dist/cli-executable.js +435 -0
- package/dist/cli-executable.js.map +1 -0
- package/dist/cli-help.d.ts +11 -0
- package/dist/cli-help.d.ts.map +1 -0
- package/dist/cli-help.js +65 -0
- package/dist/cli-help.js.map +1 -0
- package/dist/computer-readiness.d.ts +41 -0
- package/dist/computer-readiness.d.ts.map +1 -0
- package/dist/computer-readiness.js +191 -0
- package/dist/computer-readiness.js.map +1 -0
- package/dist/core-agent.d.ts +32 -0
- package/dist/core-agent.d.ts.map +1 -0
- package/dist/core-agent.js +331 -0
- package/dist/core-agent.js.map +1 -0
- package/dist/daemon-execute.d.ts +42 -0
- package/dist/daemon-execute.d.ts.map +1 -0
- package/dist/daemon-execute.js +666 -0
- package/dist/daemon-execute.js.map +1 -0
- package/dist/daemon-helpers.d.ts +150 -0
- package/dist/daemon-helpers.d.ts.map +1 -0
- package/dist/daemon-helpers.js +157 -0
- package/dist/daemon-helpers.js.map +1 -0
- package/dist/daemon-local-control.d.ts +208 -0
- package/dist/daemon-local-control.d.ts.map +1 -0
- package/dist/daemon-local-control.js +826 -0
- package/dist/daemon-local-control.js.map +1 -0
- package/dist/daemon-local-http.d.ts +57 -0
- package/dist/daemon-local-http.d.ts.map +1 -0
- package/dist/daemon-local-http.js +375 -0
- package/dist/daemon-local-http.js.map +1 -0
- package/dist/daemon-native-hooks.d.ts +17 -0
- package/dist/daemon-native-hooks.d.ts.map +1 -0
- package/dist/daemon-native-hooks.js +54 -0
- package/dist/daemon-native-hooks.js.map +1 -0
- package/dist/daemon-provider-scan.d.ts +33 -0
- package/dist/daemon-provider-scan.d.ts.map +1 -0
- package/dist/daemon-provider-scan.js +187 -0
- package/dist/daemon-provider-scan.js.map +1 -0
- package/dist/daemon-runner-status.d.ts +164 -0
- package/dist/daemon-runner-status.d.ts.map +1 -0
- package/dist/daemon-runner-status.js +424 -0
- package/dist/daemon-runner-status.js.map +1 -0
- package/dist/daemon-start.d.ts +5 -0
- package/dist/daemon-start.d.ts.map +1 -0
- package/dist/daemon-start.js +1313 -0
- package/dist/daemon-start.js.map +1 -0
- package/dist/daemon-workspace.d.ts +36 -0
- package/dist/daemon-workspace.d.ts.map +1 -0
- package/dist/daemon-workspace.js +154 -0
- package/dist/daemon-workspace.js.map +1 -0
- package/dist/daemon.d.ts +59 -0
- package/dist/daemon.d.ts.map +1 -0
- package/dist/daemon.js +681 -0
- package/dist/daemon.js.map +1 -0
- package/dist/event-journal-crypto.d.ts +14 -0
- package/dist/event-journal-crypto.d.ts.map +1 -0
- package/dist/event-journal-crypto.js +89 -0
- package/dist/event-journal-crypto.js.map +1 -0
- package/dist/event-journal-paths.d.ts +10 -0
- package/dist/event-journal-paths.d.ts.map +1 -0
- package/dist/event-journal-paths.js +29 -0
- package/dist/event-journal-paths.js.map +1 -0
- package/dist/event-journal-recovery.d.ts +83 -0
- package/dist/event-journal-recovery.d.ts.map +1 -0
- package/dist/event-journal-recovery.js +333 -0
- package/dist/event-journal-recovery.js.map +1 -0
- package/dist/event-journal-types.d.ts +54 -0
- package/dist/event-journal-types.d.ts.map +1 -0
- package/dist/event-journal-types.js +10 -0
- package/dist/event-journal-types.js.map +1 -0
- package/dist/event-journal-writer.d.ts +44 -0
- package/dist/event-journal-writer.d.ts.map +1 -0
- package/dist/event-journal-writer.js +182 -0
- package/dist/event-journal-writer.js.map +1 -0
- package/dist/event-outbox.d.ts +113 -0
- package/dist/event-outbox.d.ts.map +1 -0
- package/dist/event-outbox.js +707 -0
- package/dist/event-outbox.js.map +1 -0
- package/dist/execution-activity-tracker.d.ts +43 -0
- package/dist/execution-activity-tracker.d.ts.map +1 -0
- package/dist/execution-activity-tracker.js +116 -0
- package/dist/execution-activity-tracker.js.map +1 -0
- package/dist/execution-lease-client.d.ts +62 -0
- package/dist/execution-lease-client.d.ts.map +1 -0
- package/dist/execution-lease-client.js +271 -0
- package/dist/execution-lease-client.js.map +1 -0
- package/dist/fake-provider-cli.d.ts +13 -0
- package/dist/fake-provider-cli.d.ts.map +1 -0
- package/dist/fake-provider-cli.js +53 -0
- package/dist/fake-provider-cli.js.map +1 -0
- package/dist/generated-images.d.ts +2 -0
- package/dist/generated-images.d.ts.map +1 -0
- package/dist/generated-images.js +2 -0
- package/dist/generated-images.js.map +1 -0
- package/dist/index.cjs +47033 -45514
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +224 -0
- package/dist/index.js.map +1 -0
- package/dist/install-command.d.ts +2 -0
- package/dist/install-command.d.ts.map +1 -0
- package/dist/install-command.js +96 -0
- package/dist/install-command.js.map +1 -0
- package/dist/integration-cli.d.ts +14 -0
- package/dist/integration-cli.d.ts.map +1 -0
- package/dist/integration-cli.js +80 -0
- package/dist/integration-cli.js.map +1 -0
- package/dist/lifecycle-logger.d.ts +25 -0
- package/dist/lifecycle-logger.d.ts.map +1 -0
- package/dist/lifecycle-logger.js +73 -0
- package/dist/lifecycle-logger.js.map +1 -0
- package/dist/local-daemon-lifecycle.d.ts +112 -0
- package/dist/local-daemon-lifecycle.d.ts.map +1 -0
- package/dist/local-daemon-lifecycle.js +433 -0
- package/dist/local-daemon-lifecycle.js.map +1 -0
- package/dist/local-git-status.d.ts +14 -0
- package/dist/local-git-status.d.ts.map +1 -0
- package/dist/local-git-status.js +43 -0
- package/dist/local-git-status.js.map +1 -0
- package/dist/managed-mcp.d.ts +7 -0
- package/dist/managed-mcp.d.ts.map +1 -0
- package/dist/managed-mcp.js +16 -0
- package/dist/managed-mcp.js.map +1 -0
- package/dist/mcp-config-adapters.d.ts +57 -0
- package/dist/mcp-config-adapters.d.ts.map +1 -0
- package/dist/mcp-config-adapters.js +929 -0
- package/dist/mcp-config-adapters.js.map +1 -0
- package/dist/mcp-sync.d.ts +44 -0
- package/dist/mcp-sync.d.ts.map +1 -0
- package/dist/mcp-sync.js +68 -0
- package/dist/mcp-sync.js.map +1 -0
- package/dist/model-scan-runner.d.ts +15 -0
- package/dist/model-scan-runner.d.ts.map +1 -0
- package/dist/model-scan-runner.js +56 -0
- package/dist/model-scan-runner.js.map +1 -0
- package/dist/native-hook-command.d.ts +31 -0
- package/dist/native-hook-command.d.ts.map +1 -0
- package/dist/native-hook-command.js +289 -0
- package/dist/native-hook-command.js.map +1 -0
- package/dist/native-hook-config.d.ts +43 -0
- package/dist/native-hook-config.d.ts.map +1 -0
- package/dist/native-hook-config.js +601 -0
- package/dist/native-hook-config.js.map +1 -0
- package/dist/native-hook-drain.d.ts +34 -0
- package/dist/native-hook-drain.d.ts.map +1 -0
- package/dist/native-hook-drain.js +83 -0
- package/dist/native-hook-drain.js.map +1 -0
- package/dist/native-hook-inbox.d.ts +40 -0
- package/dist/native-hook-inbox.d.ts.map +1 -0
- package/dist/native-hook-inbox.js +217 -0
- package/dist/native-hook-inbox.js.map +1 -0
- package/dist/native-project-config.d.ts +11 -0
- package/dist/native-project-config.d.ts.map +1 -0
- package/dist/native-project-config.js +49 -0
- package/dist/native-project-config.js.map +1 -0
- package/dist/native-session.d.ts +20 -0
- package/dist/native-session.d.ts.map +1 -0
- package/dist/native-session.js +213 -0
- package/dist/native-session.js.map +1 -0
- package/dist/native-stream-journal.d.ts +22 -0
- package/dist/native-stream-journal.d.ts.map +1 -0
- package/dist/native-stream-journal.js +98 -0
- package/dist/native-stream-journal.js.map +1 -0
- package/dist/native-stream-outbox.d.ts +40 -0
- package/dist/native-stream-outbox.d.ts.map +1 -0
- package/dist/native-stream-outbox.js +358 -0
- package/dist/native-stream-outbox.js.map +1 -0
- package/dist/native-stream-platform.d.ts +4 -0
- package/dist/native-stream-platform.d.ts.map +1 -0
- package/dist/native-stream-platform.js +11 -0
- package/dist/native-stream-platform.js.map +1 -0
- package/dist/native-transcript-cursor.d.ts +33 -0
- package/dist/native-transcript-cursor.d.ts.map +1 -0
- package/dist/native-transcript-cursor.js +108 -0
- package/dist/native-transcript-cursor.js.map +1 -0
- package/dist/native-transcript-stream.d.ts +20 -0
- package/dist/native-transcript-stream.d.ts.map +1 -0
- package/dist/native-transcript-stream.js +438 -0
- package/dist/native-transcript-stream.js.map +1 -0
- package/dist/network-diagnostics.d.ts +8 -0
- package/dist/network-diagnostics.d.ts.map +1 -0
- package/dist/network-diagnostics.js +56 -0
- package/dist/network-diagnostics.js.map +1 -0
- package/dist/observable-map.d.ts +13 -0
- package/dist/observable-map.d.ts.map +1 -0
- package/dist/observable-map.js +30 -0
- package/dist/observable-map.js.map +1 -0
- package/dist/provider-auth-preflight.d.ts +19 -0
- package/dist/provider-auth-preflight.d.ts.map +1 -0
- package/dist/provider-auth-preflight.js +107 -0
- package/dist/provider-auth-preflight.js.map +1 -0
- package/dist/provider-model-discovery.d.ts +33 -0
- package/dist/provider-model-discovery.d.ts.map +1 -0
- package/dist/provider-model-discovery.js +731 -0
- package/dist/provider-model-discovery.js.map +1 -0
- package/dist/resource-diagnostics.d.ts +21 -0
- package/dist/resource-diagnostics.d.ts.map +1 -0
- package/dist/resource-diagnostics.js +161 -0
- package/dist/resource-diagnostics.js.map +1 -0
- package/dist/resume-fallback-env.d.ts +3 -0
- package/dist/resume-fallback-env.d.ts.map +1 -0
- package/dist/resume-fallback-env.js +14 -0
- package/dist/resume-fallback-env.js.map +1 -0
- package/dist/run-journal.d.ts +51 -0
- package/dist/run-journal.d.ts.map +1 -0
- package/dist/run-journal.js +193 -0
- package/dist/run-journal.js.map +1 -0
- package/dist/runtime-backends.d.ts +2 -0
- package/dist/runtime-backends.d.ts.map +1 -0
- package/dist/runtime-backends.js +2 -0
- package/dist/runtime-backends.js.map +1 -0
- package/dist/runtime-capabilities-sync.d.ts +40 -0
- package/dist/runtime-capabilities-sync.d.ts.map +1 -0
- package/dist/runtime-capabilities-sync.js +56 -0
- package/dist/runtime-capabilities-sync.js.map +1 -0
- package/dist/runtime-config.d.ts +26 -0
- package/dist/runtime-config.d.ts.map +1 -0
- package/dist/runtime-config.js +170 -0
- package/dist/runtime-config.js.map +1 -0
- package/dist/runtime-presenter.d.ts +81 -0
- package/dist/runtime-presenter.d.ts.map +1 -0
- package/dist/runtime-presenter.js +422 -0
- package/dist/runtime-presenter.js.map +1 -0
- package/dist/runtime-registration.d.ts +40 -0
- package/dist/runtime-registration.d.ts.map +1 -0
- package/dist/runtime-registration.js +143 -0
- package/dist/runtime-registration.js.map +1 -0
- package/dist/sandbox-outbox-storage.d.ts +13 -0
- package/dist/sandbox-outbox-storage.d.ts.map +1 -0
- package/dist/sandbox-outbox-storage.js +98 -0
- package/dist/sandbox-outbox-storage.js.map +1 -0
- package/dist/sandbox-outbox.d.ts +115 -0
- package/dist/sandbox-outbox.d.ts.map +1 -0
- package/dist/sandbox-outbox.js +213 -0
- package/dist/sandbox-outbox.js.map +1 -0
- package/dist/sandbox.d.ts +34 -0
- package/dist/sandbox.d.ts.map +1 -0
- package/dist/sandbox.js +469 -0
- package/dist/sandbox.js.map +1 -0
- package/dist/service-manager.d.ts +113 -0
- package/dist/service-manager.d.ts.map +1 -0
- package/dist/service-manager.js +1065 -0
- package/dist/service-manager.js.map +1 -0
- package/dist/skills/__tests__/local-skill-source.test.d.ts +2 -0
- package/dist/skills/__tests__/local-skill-source.test.d.ts.map +1 -0
- package/dist/skills/__tests__/local-skill-source.test.js +373 -0
- package/dist/skills/__tests__/local-skill-source.test.js.map +1 -0
- package/dist/skills/__tests__/managed-skill-store.test.d.ts +2 -0
- package/dist/skills/__tests__/managed-skill-store.test.d.ts.map +1 -0
- package/dist/skills/__tests__/managed-skill-store.test.js +114 -0
- package/dist/skills/__tests__/managed-skill-store.test.js.map +1 -0
- package/dist/skills/__tests__/runtime-skill-coordinator.test.d.ts +2 -0
- package/dist/skills/__tests__/runtime-skill-coordinator.test.d.ts.map +1 -0
- package/dist/skills/__tests__/runtime-skill-coordinator.test.js +251 -0
- package/dist/skills/__tests__/runtime-skill-coordinator.test.js.map +1 -0
- package/dist/skills/__tests__/runtime-skill-manager.test.d.ts +2 -0
- package/dist/skills/__tests__/runtime-skill-manager.test.d.ts.map +1 -0
- package/dist/skills/__tests__/runtime-skill-manager.test.js +664 -0
- package/dist/skills/__tests__/runtime-skill-manager.test.js.map +1 -0
- package/dist/skills/__tests__/skill-bundle.test.d.ts +2 -0
- package/dist/skills/__tests__/skill-bundle.test.d.ts.map +1 -0
- package/dist/skills/__tests__/skill-bundle.test.js +42 -0
- package/dist/skills/__tests__/skill-bundle.test.js.map +1 -0
- package/dist/skills/__tests__/skill-control-client.test.d.ts +2 -0
- package/dist/skills/__tests__/skill-control-client.test.d.ts.map +1 -0
- package/dist/skills/__tests__/skill-control-client.test.js +49 -0
- package/dist/skills/__tests__/skill-control-client.test.js.map +1 -0
- package/dist/skills/__tests__/skill-source-config.test.d.ts +2 -0
- package/dist/skills/__tests__/skill-source-config.test.d.ts.map +1 -0
- package/dist/skills/__tests__/skill-source-config.test.js +295 -0
- package/dist/skills/__tests__/skill-source-config.test.js.map +1 -0
- package/dist/skills/local-skill-source.d.ts +77 -0
- package/dist/skills/local-skill-source.d.ts.map +1 -0
- package/dist/skills/local-skill-source.js +660 -0
- package/dist/skills/local-skill-source.js.map +1 -0
- package/dist/skills/managed-skill-store.d.ts +44 -0
- package/dist/skills/managed-skill-store.d.ts.map +1 -0
- package/dist/skills/managed-skill-store.js +160 -0
- package/dist/skills/managed-skill-store.js.map +1 -0
- package/dist/skills/runtime-skill-coordinator.d.ts +35 -0
- package/dist/skills/runtime-skill-coordinator.d.ts.map +1 -0
- package/dist/skills/runtime-skill-coordinator.js +166 -0
- package/dist/skills/runtime-skill-coordinator.js.map +1 -0
- package/dist/skills/runtime-skill-manager.d.ts +89 -0
- package/dist/skills/runtime-skill-manager.d.ts.map +1 -0
- package/dist/skills/runtime-skill-manager.js +934 -0
- package/dist/skills/runtime-skill-manager.js.map +1 -0
- package/dist/skills/skill-bundle.d.ts +2 -0
- package/dist/skills/skill-bundle.d.ts.map +1 -0
- package/dist/skills/skill-bundle.js +2 -0
- package/dist/skills/skill-bundle.js.map +1 -0
- package/dist/skills/skill-cli.d.ts +2 -0
- package/dist/skills/skill-cli.d.ts.map +1 -0
- package/dist/skills/skill-cli.js +81 -0
- package/dist/skills/skill-cli.js.map +1 -0
- package/dist/skills/skill-control-client.d.ts +20 -0
- package/dist/skills/skill-control-client.d.ts.map +1 -0
- package/dist/skills/skill-control-client.js +56 -0
- package/dist/skills/skill-control-client.js.map +1 -0
- package/dist/skills/skill-invocation.d.ts +10 -0
- package/dist/skills/skill-invocation.d.ts.map +1 -0
- package/dist/skills/skill-invocation.js +24 -0
- package/dist/skills/skill-invocation.js.map +1 -0
- package/dist/sleep-inhibitor.d.ts +32 -0
- package/dist/sleep-inhibitor.d.ts.map +1 -0
- package/dist/sleep-inhibitor.js +297 -0
- package/dist/sleep-inhibitor.js.map +1 -0
- package/dist/suspension-detector.d.ts +24 -0
- package/dist/suspension-detector.d.ts.map +1 -0
- package/dist/suspension-detector.js +59 -0
- package/dist/suspension-detector.js.map +1 -0
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +2 -0
- package/dist/version.js.map +1 -0
- package/dist/web-presenter.d.ts +52 -0
- package/dist/web-presenter.d.ts.map +1 -0
- package/dist/web-presenter.js +264 -0
- package/dist/web-presenter.js.map +1 -0
- package/dist/workspace-relocation.d.ts +22 -0
- package/dist/workspace-relocation.d.ts.map +1 -0
- package/dist/workspace-relocation.js +90 -0
- package/dist/workspace-relocation.js.map +1 -0
- package/dist/workspace-rpc.d.ts +2 -0
- package/dist/workspace-rpc.d.ts.map +1 -0
- package/dist/workspace-rpc.js +15 -0
- package/dist/workspace-rpc.js.map +1 -0
- package/dist/workspace-run-lease.d.ts +116 -0
- package/dist/workspace-run-lease.d.ts.map +1 -0
- package/dist/workspace-run-lease.js +320 -0
- package/dist/workspace-run-lease.js.map +1 -0
- package/dist/ws-client.d.ts +168 -0
- package/dist/ws-client.d.ts.map +1 -0
- package/dist/ws-client.js +440 -0
- package/dist/ws-client.js.map +1 -0
- package/package.json +4 -3
|
@@ -0,0 +1,1313 @@
|
|
|
1
|
+
// AL-2472: daemon start loop — WS connect, agent.execute, local HTTP API.
|
|
2
|
+
import { randomBytes, randomUUID } from "node:crypto";
|
|
3
|
+
import { appendFileSync, mkdirSync, renameSync, statSync } from "node:fs";
|
|
4
|
+
import http from "node:http";
|
|
5
|
+
import { homedir } from "node:os";
|
|
6
|
+
import { dirname, join, resolve } from "node:path";
|
|
7
|
+
import { io } from "socket.io-client";
|
|
8
|
+
import { BindingSwitchGitError, executeBindingSwitchGitRpc, isBindingSwitchGitMethod, } from "./binding-switch-git.js";
|
|
9
|
+
import { WorkspaceAccessTracker } from "./computer-readiness.js";
|
|
10
|
+
import { handleAgentExecute } from "./daemon-execute.js";
|
|
11
|
+
import { argValue, checkStoredEndpointConsistency, isLocalEndpoint, resolveEndpointProfile, resolveEndpoints, scheduleRuntimeModelScan, sleep, } from "./daemon-helpers.js";
|
|
12
|
+
import { acquireLocalDaemonMaintenanceLease, acquireLocalDaemonOwnerLease, bindConfigPath, clearPendingSetupIntent, describeOccupiedLocalDaemon, getConfigPath, getLocalApiPort, probeLocalDaemonEndpoint, readConfig, readEndpointDefaults, readLocalDaemonMaintenanceLease, releaseLocalDaemonMaintenanceLease, releaseLocalDaemonOwnerLease, updateLocalDaemonOwnerEndpoint, writeConfig, } from "./daemon-local-control.js";
|
|
13
|
+
import { createLocalDaemonHttpHandler } from "./daemon-local-http.js";
|
|
14
|
+
import { deliverNativeHookWithOwnership } from "./daemon-native-hooks.js";
|
|
15
|
+
import { discoverCapabilities, refreshProviderModels, refreshProviderVersions, VERSION_REFRESH_INTERVAL_MS, } from "./daemon-provider-scan.js";
|
|
16
|
+
import { abortActiveAgent, collectRuntimeMetadataWithProviderLimits, DurablePendingRunStarts, flushPendingDeliveryLostEvents, reconcileActiveRuns, summarizeRunnerStatus, } from "./daemon-runner-status.js";
|
|
17
|
+
import { EncryptedEventOutbox } from "./event-outbox.js";
|
|
18
|
+
import { runRuntimeModelScan } from "./model-scan-runner.js";
|
|
19
|
+
import { drainNativeHookInbox } from "./native-hook-drain.js";
|
|
20
|
+
import { EncryptedNativeHookInbox, nativeHookInboxPath } from "./native-hook-inbox.js";
|
|
21
|
+
import { createNativeStreamOutbox } from "./native-stream-platform.js";
|
|
22
|
+
import { NativeTranscriptCursorStore } from "./native-transcript-cursor.js";
|
|
23
|
+
import { NativeTranscriptStreamer } from "./native-transcript-stream.js";
|
|
24
|
+
import { classifyTransportFailure } from "./network-diagnostics.js";
|
|
25
|
+
import { ObservableMap } from "./observable-map.js";
|
|
26
|
+
import { RuntimeRunJournal } from "./run-journal.js";
|
|
27
|
+
import { buildModelsScanMetadata, getModelScanInFlight, setConnectedRuntimeSocket, } from "./runtime-capabilities-sync.js";
|
|
28
|
+
import { exchangeRuntimeSetupGrant, } from "./runtime-registration.js";
|
|
29
|
+
import { resolveSkillScanInput } from "./skills/local-skill-source.js";
|
|
30
|
+
import { RuntimeSkillCoordinator } from "./skills/runtime-skill-coordinator.js";
|
|
31
|
+
import { RuntimeSkillManager } from "./skills/runtime-skill-manager.js";
|
|
32
|
+
import { HttpSkillControlClient } from "./skills/skill-control-client.js";
|
|
33
|
+
import { SleepInhibitor } from "./sleep-inhibitor.js";
|
|
34
|
+
import { SuspensionDetector } from "./suspension-detector.js";
|
|
35
|
+
import { AGENT_VERSION } from "./version.js";
|
|
36
|
+
import { executeWorkspaceRpc } from "./workspace-rpc.js";
|
|
37
|
+
import { applyLeaseCheckResult, FRESH_LEASE_MONITOR_STATE, isLeaseCheckDue, WORKSPACE_LEASE_CHECK_INTERVAL_MS, } from "./workspace-run-lease.js";
|
|
38
|
+
export const RUNTIME_TOKEN_RENEWAL_WINDOW_MS = 24 * 60 * 60 * 1000;
|
|
39
|
+
export function runtimeTokenNeedsRenewal(expiresAt, nowMs = Date.now()) {
|
|
40
|
+
if (!expiresAt)
|
|
41
|
+
return true;
|
|
42
|
+
const expiresAtMs = Date.parse(expiresAt);
|
|
43
|
+
return !Number.isFinite(expiresAtMs) || expiresAtMs <= nowMs + RUNTIME_TOKEN_RENEWAL_WINDOW_MS;
|
|
44
|
+
}
|
|
45
|
+
export async function startDaemon(args) {
|
|
46
|
+
bindConfigPath(args);
|
|
47
|
+
const stored = readConfig();
|
|
48
|
+
const installationId = stored.installationId ?? randomUUID();
|
|
49
|
+
const configPath = getConfigPath();
|
|
50
|
+
const endpointProfile = stored.endpointProfile ?? resolveEndpointProfile(args);
|
|
51
|
+
const daemonSessionId = randomUUID();
|
|
52
|
+
let heartbeatSeq = 0;
|
|
53
|
+
const runtimeId = argValue(args, "--runtime-id") ?? process.env.ALAN_RUNTIME_ID ?? stored.runtimeId;
|
|
54
|
+
const runtimeTokenOverride = argValue(args, "--token") ?? process.env.ALAN_RUNTIME_TOKEN;
|
|
55
|
+
let runtimeToken = runtimeTokenOverride ?? stored.runtimeToken;
|
|
56
|
+
let runtimeRenewalToken = runtimeTokenOverride ? undefined : stored.runtimeRenewalToken;
|
|
57
|
+
let runtimeTokenExpiresAt = runtimeTokenOverride ? undefined : stored.runtimeTokenExpiresAt;
|
|
58
|
+
const wsUrlOverride = argValue(args, "--ws-url") ?? process.env.ALAN_WS_URL;
|
|
59
|
+
// CLI/env overrides are trusted; stored config is self-healed when profile disagrees.
|
|
60
|
+
const endpointMismatch = wsUrlOverride
|
|
61
|
+
? null
|
|
62
|
+
: checkStoredEndpointConsistency(stored, readEndpointDefaults());
|
|
63
|
+
let wsUrl = wsUrlOverride ?? stored.wsUrl;
|
|
64
|
+
if (endpointMismatch && stored.wsUrl && stored.wsUrl !== endpointMismatch.expectedWsUrl) {
|
|
65
|
+
console.warn("[alan-agent] Stored config does not match its endpoint profile — connecting with the profile endpoints instead. Re-run setup to repair the stored config.", { configPath: getConfigPath(), detail: endpointMismatch.detail });
|
|
66
|
+
wsUrl = endpointMismatch.expectedWsUrl;
|
|
67
|
+
}
|
|
68
|
+
let localApiPort = getLocalApiPort(args, stored);
|
|
69
|
+
const localApiToken = stored.localApiToken ?? randomBytes(24).toString("hex");
|
|
70
|
+
const localServiceId = stored.localServiceId ?? randomUUID();
|
|
71
|
+
const localServiceSecret = stored.localServiceSecret ?? randomBytes(32).toString("base64url");
|
|
72
|
+
const localServiceStartedAtMs = Date.now();
|
|
73
|
+
const eventSpoolKey = stored.eventSpoolKey ?? randomBytes(32).toString("base64url");
|
|
74
|
+
if (stored.daemonEpoch !== undefined &&
|
|
75
|
+
(!Number.isSafeInteger(stored.daemonEpoch) ||
|
|
76
|
+
stored.daemonEpoch < 0 ||
|
|
77
|
+
stored.daemonEpoch >= Number.MAX_SAFE_INTEGER)) {
|
|
78
|
+
throw new Error("daemon config contains an invalid durable epoch; repair the runtime config");
|
|
79
|
+
}
|
|
80
|
+
const daemonEpoch = (stored.daemonEpoch ?? 0) + 1;
|
|
81
|
+
if (!runtimeId || !runtimeToken || !wsUrl) {
|
|
82
|
+
throw new Error("daemon requires runtimeId, runtime token, and wsUrl. Run setup first or pass --runtime-id/--token/--ws-url.");
|
|
83
|
+
}
|
|
84
|
+
const explicitApiUrl = argValue(args, "--api-url") ?? process.env.ALAN_API_URL;
|
|
85
|
+
const apiUrl = explicitApiUrl ?? endpointMismatch?.expectedApiUrl ?? resolveEndpoints(args, stored).apiUrl;
|
|
86
|
+
const skillControl = new HttpSkillControlClient({
|
|
87
|
+
apiUrl,
|
|
88
|
+
runtimeId,
|
|
89
|
+
getRuntimeToken: () => runtimeToken,
|
|
90
|
+
});
|
|
91
|
+
const runtimeSkillManager = await RuntimeSkillManager.open({
|
|
92
|
+
homeDirectory: homedir(),
|
|
93
|
+
endpointProfile,
|
|
94
|
+
profileStorageIdentity: configPath,
|
|
95
|
+
controlClient: skillControl,
|
|
96
|
+
});
|
|
97
|
+
const runtimeSkillCoordinator = new RuntimeSkillCoordinator(runtimeSkillManager, skillControl, (input) => resolveSkillScanInput(homedir(), input));
|
|
98
|
+
const runSkillSync = async (reason) => {
|
|
99
|
+
const operation = reason === "startup"
|
|
100
|
+
? runtimeSkillCoordinator.runStartup()
|
|
101
|
+
: reason === "interval"
|
|
102
|
+
? runtimeSkillCoordinator.runIncremental()
|
|
103
|
+
: reason === "refresh"
|
|
104
|
+
? runtimeSkillCoordinator.runAudit()
|
|
105
|
+
: runtimeSkillCoordinator.run();
|
|
106
|
+
try {
|
|
107
|
+
await operation;
|
|
108
|
+
return { status: "ready" };
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
console.warn("[alan-agent] Skill synchronization failed", {
|
|
112
|
+
runtimeId,
|
|
113
|
+
reason,
|
|
114
|
+
error: error instanceof Error ? error.message : String(error),
|
|
115
|
+
});
|
|
116
|
+
return {
|
|
117
|
+
status: "failed",
|
|
118
|
+
message: error instanceof Error ? error.message : String(error),
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
let initialSkillReadiness = null;
|
|
123
|
+
const waitForSkillReadiness = async (readiness, timeoutMs) => {
|
|
124
|
+
if (!readiness) {
|
|
125
|
+
return { status: "failed", message: "initial Skill synchronization has not started" };
|
|
126
|
+
}
|
|
127
|
+
let timeout;
|
|
128
|
+
try {
|
|
129
|
+
return await Promise.race([
|
|
130
|
+
readiness,
|
|
131
|
+
new Promise((resolveTimeout) => {
|
|
132
|
+
timeout = setTimeout(() => resolveTimeout({ status: "timeout" }), timeoutMs);
|
|
133
|
+
timeout.unref?.();
|
|
134
|
+
}),
|
|
135
|
+
]);
|
|
136
|
+
}
|
|
137
|
+
finally {
|
|
138
|
+
if (timeout)
|
|
139
|
+
clearTimeout(timeout);
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
const startupLeaseResult = acquireLocalDaemonMaintenanceLease(configPath, "daemon_startup", 60_000);
|
|
143
|
+
if (!startupLeaseResult.acquired) {
|
|
144
|
+
throw new Error(startupLeaseResult.lease.reason === "credential_cleanup"
|
|
145
|
+
? "Cannot start the daemon while credential cleanup is in progress"
|
|
146
|
+
: "Another process is already starting or maintaining this daemon config");
|
|
147
|
+
}
|
|
148
|
+
let ownerLeaseResult;
|
|
149
|
+
try {
|
|
150
|
+
ownerLeaseResult = await acquireLocalDaemonOwnerLease({
|
|
151
|
+
configPath,
|
|
152
|
+
daemonSessionId,
|
|
153
|
+
runtimeId,
|
|
154
|
+
localServiceId,
|
|
155
|
+
localApiPort,
|
|
156
|
+
localApiToken,
|
|
157
|
+
storedLocalApiPort: stored.localApiPort,
|
|
158
|
+
storedLocalApiToken: stored.localApiToken,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
releaseLocalDaemonMaintenanceLease(configPath, startupLeaseResult.lease.id);
|
|
163
|
+
throw error;
|
|
164
|
+
}
|
|
165
|
+
if (!ownerLeaseResult.acquired) {
|
|
166
|
+
releaseLocalDaemonMaintenanceLease(configPath, startupLeaseResult.lease.id);
|
|
167
|
+
throw new Error(`Another process already owns this daemon config (pid ${ownerLeaseResult.lease.pid})`);
|
|
168
|
+
}
|
|
169
|
+
let ownerLease = ownerLeaseResult.lease;
|
|
170
|
+
let startupLeaseHeld = true;
|
|
171
|
+
let ownerLeaseHeld = true;
|
|
172
|
+
const releaseStartupLease = () => {
|
|
173
|
+
if (!startupLeaseHeld)
|
|
174
|
+
return;
|
|
175
|
+
startupLeaseHeld = false;
|
|
176
|
+
releaseLocalDaemonMaintenanceLease(configPath, startupLeaseResult.lease.id);
|
|
177
|
+
};
|
|
178
|
+
const releaseOwnerLease = () => {
|
|
179
|
+
if (!ownerLeaseHeld)
|
|
180
|
+
return;
|
|
181
|
+
ownerLeaseHeld = false;
|
|
182
|
+
releaseLocalDaemonOwnerLease(configPath, ownerLease.id);
|
|
183
|
+
};
|
|
184
|
+
// ObservableMap keeps derived "any run active?" state in sync across dispatch/abort/reconnect.
|
|
185
|
+
const activeAgents = new ObservableMap();
|
|
186
|
+
const pendingRunStarts = new DurablePendingRunStarts(`${configPath}.pending-runs.json`, daemonSessionId);
|
|
187
|
+
const maintenanceReason = () => readLocalDaemonMaintenanceLease(configPath)?.reason;
|
|
188
|
+
let disconnectedSinceMs = null;
|
|
189
|
+
let lastConnectError = null;
|
|
190
|
+
// Machine-readable code from the backend's socket.io middleware error
|
|
191
|
+
// (err.data.code), e.g. "invalid_runtime_token". Preferred over regex on the
|
|
192
|
+
// human-readable message; older servers that don't set it leave this null.
|
|
193
|
+
let lastConnectErrorCode = null;
|
|
194
|
+
// Sticky identity failures must not be overwritten by a later transport code
|
|
195
|
+
// or by renewal's own rejection code racing the connect_error that triggered it.
|
|
196
|
+
// Cleared only on successful connect / successful renewal.
|
|
197
|
+
const TERMINAL_RUNTIME_AUTH_CODES = new Set([
|
|
198
|
+
"invalid_runtime_token",
|
|
199
|
+
"runtime_renewal_rejected",
|
|
200
|
+
"runtime_deleted",
|
|
201
|
+
"runtime_disabled",
|
|
202
|
+
]);
|
|
203
|
+
let terminalAuthFailureCode = null;
|
|
204
|
+
const rememberConnectErrorCode = (code) => {
|
|
205
|
+
if (code && TERMINAL_RUNTIME_AUTH_CODES.has(code)) {
|
|
206
|
+
terminalAuthFailureCode = code;
|
|
207
|
+
}
|
|
208
|
+
lastConnectErrorCode = terminalAuthFailureCode ?? code;
|
|
209
|
+
};
|
|
210
|
+
const clearConnectErrors = () => {
|
|
211
|
+
lastConnectError = null;
|
|
212
|
+
lastConnectErrorCode = null;
|
|
213
|
+
terminalAuthFailureCode = null;
|
|
214
|
+
};
|
|
215
|
+
const pendingDeliveryLost = [];
|
|
216
|
+
const currentCapabilities = () => discoverCapabilities();
|
|
217
|
+
const currentMetadata = async () => ({
|
|
218
|
+
...(await collectRuntimeMetadataWithProviderLimits()),
|
|
219
|
+
installationId,
|
|
220
|
+
});
|
|
221
|
+
const recentLogs = [];
|
|
222
|
+
const daemonLogPath = join(dirname(configPath), "logs", "daemon.log");
|
|
223
|
+
const persistDaemonLog = (line) => {
|
|
224
|
+
try {
|
|
225
|
+
mkdirSync(dirname(daemonLogPath), { recursive: true });
|
|
226
|
+
try {
|
|
227
|
+
if (statSync(daemonLogPath).size > 5 * 1024 * 1024) {
|
|
228
|
+
renameSync(daemonLogPath, `${daemonLogPath}.1`);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
// Missing file is fine — append creates it.
|
|
233
|
+
}
|
|
234
|
+
appendFileSync(daemonLogPath, `${line}\n`, "utf8");
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
// Logging must never take down the daemon.
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
const pushLog = (message) => {
|
|
241
|
+
const line = `${new Date().toISOString()} ${message}`;
|
|
242
|
+
recentLogs.push(line);
|
|
243
|
+
if (recentLogs.length > 200)
|
|
244
|
+
recentLogs.splice(0, recentLogs.length - 200);
|
|
245
|
+
persistDaemonLog(line);
|
|
246
|
+
};
|
|
247
|
+
const sleepInhibitor = new SleepInhibitor({ log: pushLog });
|
|
248
|
+
sleepInhibitor.setActive(true);
|
|
249
|
+
if (sleepInhibitor.backendNames().length > 0) {
|
|
250
|
+
pushLog(`sleep-inhibitor backends=${sleepInhibitor.backendNames().join(",")}`);
|
|
251
|
+
}
|
|
252
|
+
const workspaceAccessTracker = new WorkspaceAccessTracker();
|
|
253
|
+
const recheckWorkspaceIssue = () => {
|
|
254
|
+
const previousPath = workspaceAccessTracker.recoveryPath();
|
|
255
|
+
const readiness = workspaceAccessTracker.recheck();
|
|
256
|
+
if (previousPath && readiness?.ready)
|
|
257
|
+
pushLog(`workspace-access-restored path=${previousPath}`);
|
|
258
|
+
return workspaceAccessTracker.recoveryPath() ?? undefined;
|
|
259
|
+
};
|
|
260
|
+
let nativeStreamOutbox;
|
|
261
|
+
let nativeStreamEnrollment = false;
|
|
262
|
+
let nativeDeliveryReady = false;
|
|
263
|
+
let eventOutbox;
|
|
264
|
+
let nativeHookInbox;
|
|
265
|
+
let nativeTranscriptStreamer;
|
|
266
|
+
let runJournal;
|
|
267
|
+
try {
|
|
268
|
+
nativeStreamOutbox = createNativeStreamOutbox(`${configPath}.native-streams`, eventSpoolKey, pushLog);
|
|
269
|
+
eventOutbox = new EncryptedEventOutbox(`${configPath}.event-outbox.enc`, eventSpoolKey, pushLog,
|
|
270
|
+
// Keep replay below the legacy runtime-socket ceiling during rolling
|
|
271
|
+
// upgrades. Stable acknowledgements still make the stream eventually
|
|
272
|
+
// consistent; pacing only trades catch-up latency for connection health.
|
|
273
|
+
{ deliveryIntervalMs: 50, maxConcurrentDeliveries: 2 });
|
|
274
|
+
nativeHookInbox = new EncryptedNativeHookInbox(nativeHookInboxPath(configPath), eventSpoolKey, {
|
|
275
|
+
recoverClaims: true,
|
|
276
|
+
concurrency: 2,
|
|
277
|
+
onQuarantine: ({ fileName, error }) => pushLog(`native-hook-quarantined file=${fileName} error=${error.message}`),
|
|
278
|
+
});
|
|
279
|
+
nativeTranscriptStreamer = new NativeTranscriptStreamer(new NativeTranscriptCursorStore(`${configPath}.native-transcript-cursors.json`));
|
|
280
|
+
runJournal = new RuntimeRunJournal(`${configPath}.run-journal.json`);
|
|
281
|
+
}
|
|
282
|
+
catch (error) {
|
|
283
|
+
releaseOwnerLease();
|
|
284
|
+
releaseStartupLease();
|
|
285
|
+
throw error;
|
|
286
|
+
}
|
|
287
|
+
const workspaceRelocations = new Map();
|
|
288
|
+
const shutdownAbort = new AbortController();
|
|
289
|
+
let nativeHookDrainInFlight = null;
|
|
290
|
+
const drainNativeHooks = async () => {
|
|
291
|
+
try {
|
|
292
|
+
const result = await drainNativeHookInbox({
|
|
293
|
+
inbox: nativeHookInbox,
|
|
294
|
+
transcriptStreamer: nativeTranscriptStreamer,
|
|
295
|
+
sink: {
|
|
296
|
+
deliver: (envelope, sourceHook) => deliverNativeHookWithOwnership({
|
|
297
|
+
socket,
|
|
298
|
+
envelope,
|
|
299
|
+
sourceHook,
|
|
300
|
+
outbox: nativeStreamOutbox,
|
|
301
|
+
enrollment: nativeStreamEnrollment,
|
|
302
|
+
signal: shutdownAbort.signal,
|
|
303
|
+
pushLog,
|
|
304
|
+
}),
|
|
305
|
+
deliverCanonical: async (binding, source) => {
|
|
306
|
+
if (shutdownAbort.signal.aborted)
|
|
307
|
+
return false;
|
|
308
|
+
eventOutbox.enqueueSource(binding.conversationId, binding.runId, source.eventId, source.event);
|
|
309
|
+
eventOutbox.flush(socket);
|
|
310
|
+
try {
|
|
311
|
+
await eventOutbox.waitForAcknowledgement(source.eventId, shutdownAbort.signal);
|
|
312
|
+
}
|
|
313
|
+
catch (error) {
|
|
314
|
+
if (shutdownAbort.signal.aborted)
|
|
315
|
+
return false;
|
|
316
|
+
throw error;
|
|
317
|
+
}
|
|
318
|
+
return true;
|
|
319
|
+
},
|
|
320
|
+
},
|
|
321
|
+
onTranscriptError: ({ envelope, error }) => pushLog(`native-transcript-unavailable event=${envelope.eventId} provider=${envelope.provider} error=${error.message}`),
|
|
322
|
+
});
|
|
323
|
+
if (result.attempted > 0) {
|
|
324
|
+
pushLog(`native-hook-drain attempted=${result.attempted} acknowledged=${result.acknowledged} deferred=${result.deferred}`);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
catch (error) {
|
|
328
|
+
pushLog(`native-hook-drain-failed ${error.message}`);
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
const flushNativeHookInbox = () => {
|
|
332
|
+
if (nativeHookDrainInFlight)
|
|
333
|
+
return nativeHookDrainInFlight;
|
|
334
|
+
if (shutdownAbort.signal.aborted || !socket.connected || !nativeDeliveryReady)
|
|
335
|
+
return Promise.resolve();
|
|
336
|
+
nativeHookDrainInFlight = drainNativeHooks().finally(() => {
|
|
337
|
+
nativeHookDrainInFlight = null;
|
|
338
|
+
});
|
|
339
|
+
return nativeHookDrainInFlight;
|
|
340
|
+
};
|
|
341
|
+
for (const relocation of stored.workspaceRelocations ?? []) {
|
|
342
|
+
if (relocation?.conversationId &&
|
|
343
|
+
relocation.runId &&
|
|
344
|
+
relocation.previousPath &&
|
|
345
|
+
relocation.canonicalPath) {
|
|
346
|
+
workspaceRelocations.set(resolve(relocation.previousPath), relocation);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
if (stored.localApiPort !== localApiPort ||
|
|
350
|
+
stored.localApiToken !== localApiToken ||
|
|
351
|
+
stored.localServiceId !== localServiceId ||
|
|
352
|
+
stored.localServiceSecret !== localServiceSecret ||
|
|
353
|
+
stored.eventSpoolKey !== eventSpoolKey ||
|
|
354
|
+
stored.daemonEpoch !== daemonEpoch ||
|
|
355
|
+
stored.installationId !== installationId) {
|
|
356
|
+
// Persist only local API details plus first-run identity. Identity/wsUrl
|
|
357
|
+
// overrides from CLI args or env are per-invocation and must never clobber
|
|
358
|
+
// an existing stored config for the active profile.
|
|
359
|
+
try {
|
|
360
|
+
writeConfig({
|
|
361
|
+
...stored,
|
|
362
|
+
runtimeId: stored.runtimeId ?? runtimeId,
|
|
363
|
+
runtimeToken: stored.runtimeToken ?? runtimeToken,
|
|
364
|
+
wsUrl: stored.wsUrl ?? wsUrl,
|
|
365
|
+
localApiPort,
|
|
366
|
+
localApiToken,
|
|
367
|
+
localServiceId,
|
|
368
|
+
localServiceSecret,
|
|
369
|
+
eventSpoolKey,
|
|
370
|
+
daemonEpoch,
|
|
371
|
+
installationId,
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
catch (error) {
|
|
375
|
+
releaseOwnerLease();
|
|
376
|
+
releaseStartupLease();
|
|
377
|
+
throw error;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
let socket;
|
|
381
|
+
try {
|
|
382
|
+
socket = io(wsUrl, {
|
|
383
|
+
query: { type: "runtime", runtimeId, hostKind: "daemon", agentVersion: AGENT_VERSION },
|
|
384
|
+
auth: { token: runtimeToken },
|
|
385
|
+
transports: ["websocket"],
|
|
386
|
+
reconnection: true,
|
|
387
|
+
reconnectionAttempts: Infinity,
|
|
388
|
+
reconnectionDelay: 1000,
|
|
389
|
+
reconnectionDelayMax: 5000,
|
|
390
|
+
timeout: 20_000,
|
|
391
|
+
upgrade: false,
|
|
392
|
+
extraHeaders: { "ngrok-skip-browser-warning": "1" },
|
|
393
|
+
});
|
|
394
|
+
setConnectedRuntimeSocket(socket);
|
|
395
|
+
}
|
|
396
|
+
catch (error) {
|
|
397
|
+
releaseOwnerLease();
|
|
398
|
+
releaseStartupLease();
|
|
399
|
+
throw error;
|
|
400
|
+
}
|
|
401
|
+
socket.on("runtime.ready", (ready) => {
|
|
402
|
+
nativeDeliveryReady = true;
|
|
403
|
+
nativeStreamEnrollment =
|
|
404
|
+
Boolean(nativeStreamOutbox) &&
|
|
405
|
+
ready?.nativeStreamVersion === 2 &&
|
|
406
|
+
ready.nativeStreamEnrollment === true;
|
|
407
|
+
if (ready?.nativeStreamVersion === 2)
|
|
408
|
+
nativeStreamOutbox?.connect(socket);
|
|
409
|
+
void flushNativeHookInbox();
|
|
410
|
+
});
|
|
411
|
+
let runtimeCredentialOperationTail = Promise.resolve();
|
|
412
|
+
const runCredentialOperation = (operation) => {
|
|
413
|
+
const result = runtimeCredentialOperationTail.catch(() => { }).then(operation);
|
|
414
|
+
runtimeCredentialOperationTail = result.then(() => { }, () => { });
|
|
415
|
+
return result;
|
|
416
|
+
};
|
|
417
|
+
let runtimeRenewalInFlight = null;
|
|
418
|
+
const renewRuntimeLease = () => {
|
|
419
|
+
if (runtimeRenewalInFlight)
|
|
420
|
+
return runtimeRenewalInFlight;
|
|
421
|
+
const attempt = runCredentialOperation(async () => {
|
|
422
|
+
const renewalToken = runtimeRenewalToken;
|
|
423
|
+
if (!renewalToken) {
|
|
424
|
+
return false;
|
|
425
|
+
}
|
|
426
|
+
let response = null;
|
|
427
|
+
let transportError = null;
|
|
428
|
+
for (let retry = 1; retry <= 3; retry += 1) {
|
|
429
|
+
try {
|
|
430
|
+
response = await fetch(`${apiUrl}/public/runtimes/${runtimeId}/tokens/renew`, {
|
|
431
|
+
method: "POST",
|
|
432
|
+
headers: {
|
|
433
|
+
authorization: `Bearer ${renewalToken}`,
|
|
434
|
+
"content-type": "application/json",
|
|
435
|
+
},
|
|
436
|
+
body: "{}",
|
|
437
|
+
signal: AbortSignal.timeout(10_000),
|
|
438
|
+
});
|
|
439
|
+
transportError = null;
|
|
440
|
+
}
|
|
441
|
+
catch (error) {
|
|
442
|
+
response = null;
|
|
443
|
+
transportError = error;
|
|
444
|
+
}
|
|
445
|
+
const retriable = !response || response.status === 408 || response.status === 429 || response.status >= 500;
|
|
446
|
+
if (!retriable || retry === 3)
|
|
447
|
+
break;
|
|
448
|
+
await sleep(retry * 250);
|
|
449
|
+
}
|
|
450
|
+
if (!response) {
|
|
451
|
+
rememberConnectErrorCode("backend_unreachable");
|
|
452
|
+
lastConnectError = `Runtime renewal could not reach Alan: ${transportError instanceof Error ? transportError.message : String(transportError)}`;
|
|
453
|
+
pushLog(`runtime-renewal-transport-failed ${lastConnectError}`);
|
|
454
|
+
return false;
|
|
455
|
+
}
|
|
456
|
+
if (!response.ok) {
|
|
457
|
+
const code = response.status === 401
|
|
458
|
+
? "runtime_renewal_rejected"
|
|
459
|
+
: response.status === 403
|
|
460
|
+
? "runtime_disabled"
|
|
461
|
+
: response.status === 410
|
|
462
|
+
? "runtime_deleted"
|
|
463
|
+
: response.status >= 500
|
|
464
|
+
? "backend_unreachable"
|
|
465
|
+
: "runtime_renewal_failed";
|
|
466
|
+
rememberConnectErrorCode(code);
|
|
467
|
+
lastConnectError = `Runtime renewal failed: ${response.status} ${await response.text()}`;
|
|
468
|
+
pushLog(`runtime-renewal-rejected ${lastConnectError}`);
|
|
469
|
+
return false;
|
|
470
|
+
}
|
|
471
|
+
const body = (await response.json());
|
|
472
|
+
if (typeof body.runtimeToken !== "string" ||
|
|
473
|
+
!body.runtimeToken.startsWith("alan_runtime_") ||
|
|
474
|
+
typeof body.runtimeTokenExpiresAt !== "string" ||
|
|
475
|
+
!Number.isFinite(Date.parse(body.runtimeTokenExpiresAt))) {
|
|
476
|
+
rememberConnectErrorCode("runtime_renewal_failed");
|
|
477
|
+
lastConnectError = "Runtime renewal returned an invalid access token";
|
|
478
|
+
pushLog("runtime-renewal-invalid-response");
|
|
479
|
+
return false;
|
|
480
|
+
}
|
|
481
|
+
const current = readConfig();
|
|
482
|
+
if (current.runtimeId !== runtimeId || current.runtimeRenewalToken !== renewalToken) {
|
|
483
|
+
rememberConnectErrorCode("runtime_renewal_superseded");
|
|
484
|
+
lastConnectError = "Runtime credentials changed while renewal was in progress";
|
|
485
|
+
pushLog("runtime-renewal-superseded");
|
|
486
|
+
return false;
|
|
487
|
+
}
|
|
488
|
+
const replacementExpiresAt = body.runtimeTokenExpiresAt;
|
|
489
|
+
try {
|
|
490
|
+
writeConfig({
|
|
491
|
+
...current,
|
|
492
|
+
runtimeToken: body.runtimeToken,
|
|
493
|
+
runtimeTokenExpiresAt: replacementExpiresAt,
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
catch (error) {
|
|
497
|
+
rememberConnectErrorCode("config_write_failed");
|
|
498
|
+
lastConnectError = `Runtime renewal could not save its replacement: ${error.message}`;
|
|
499
|
+
pushLog(`runtime-renewal-config-write-failed ${error.message}`);
|
|
500
|
+
return false;
|
|
501
|
+
}
|
|
502
|
+
runtimeToken = body.runtimeToken;
|
|
503
|
+
runtimeTokenExpiresAt = replacementExpiresAt;
|
|
504
|
+
socket.auth = { token: body.runtimeToken };
|
|
505
|
+
clearConnectErrors();
|
|
506
|
+
pushLog("runtime-renewal-succeeded");
|
|
507
|
+
socket.connect();
|
|
508
|
+
return true;
|
|
509
|
+
});
|
|
510
|
+
runtimeRenewalInFlight = attempt;
|
|
511
|
+
void attempt
|
|
512
|
+
.finally(() => {
|
|
513
|
+
if (runtimeRenewalInFlight === attempt)
|
|
514
|
+
runtimeRenewalInFlight = null;
|
|
515
|
+
})
|
|
516
|
+
.catch(() => { });
|
|
517
|
+
return attempt;
|
|
518
|
+
};
|
|
519
|
+
const ensureRuntimeTokenForLaunch = () => {
|
|
520
|
+
if (!runtimeRenewalToken || !runtimeTokenNeedsRenewal(runtimeTokenExpiresAt))
|
|
521
|
+
return true;
|
|
522
|
+
return renewRuntimeLease().then((renewed) => {
|
|
523
|
+
if (renewed)
|
|
524
|
+
return true;
|
|
525
|
+
// A transient renewal failure must not interrupt a credential that is
|
|
526
|
+
// still valid. Unknown expiry belongs to an older config; its authenticated
|
|
527
|
+
// WebSocket is the live validity signal until a later renewal succeeds.
|
|
528
|
+
if (!runtimeTokenExpiresAt)
|
|
529
|
+
return socket.connected;
|
|
530
|
+
return Date.parse(runtimeTokenExpiresAt) > Date.now();
|
|
531
|
+
});
|
|
532
|
+
};
|
|
533
|
+
let queueIdentityRestart = () => { };
|
|
534
|
+
let runtimeSetupInFlight = null;
|
|
535
|
+
const adoptRuntimeSetupGrant = (input) => {
|
|
536
|
+
if (runtimeSetupInFlight)
|
|
537
|
+
return runtimeSetupInFlight;
|
|
538
|
+
const attempt = runCredentialOperation(async () => {
|
|
539
|
+
const activeRunCount = activeAgents.size + pendingRunStarts.size;
|
|
540
|
+
if (activeRunCount > 0) {
|
|
541
|
+
throw new Error(`credential setup is deferred while ${activeRunCount} active run${activeRunCount === 1 ? " finishes" : "s finish"}`);
|
|
542
|
+
}
|
|
543
|
+
const current = readConfig();
|
|
544
|
+
if (current.runtimeId !== runtimeId ||
|
|
545
|
+
current.installationId !== installationId ||
|
|
546
|
+
input.installationId !== installationId) {
|
|
547
|
+
throw new Error("credential setup does not belong to this daemon installation");
|
|
548
|
+
}
|
|
549
|
+
if (input.apiUrl !== stored.apiUrl || input.wsUrl !== wsUrl) {
|
|
550
|
+
throw new Error("credential setup cannot change a running daemon's endpoints");
|
|
551
|
+
}
|
|
552
|
+
const bundle = await exchangeRuntimeSetupGrant(input);
|
|
553
|
+
if (bundle.runtimeId !== runtimeId) {
|
|
554
|
+
writeConfig({
|
|
555
|
+
...current,
|
|
556
|
+
runtimeId: bundle.runtimeId,
|
|
557
|
+
runtimeToken: bundle.runtimeToken,
|
|
558
|
+
runtimeRenewalToken: bundle.runtimeRenewalToken,
|
|
559
|
+
runtimeTokenExpiresAt: bundle.runtimeTokenExpiresAt,
|
|
560
|
+
displayName: bundle.displayName,
|
|
561
|
+
});
|
|
562
|
+
clearPendingSetupIntent();
|
|
563
|
+
pushLog(`runtime-identity-switch-queued from=${runtimeId} to=${bundle.runtimeId} generation=${bundle.credentialGeneration}`);
|
|
564
|
+
const restartTimer = setTimeout(queueIdentityRestart, 100);
|
|
565
|
+
restartTimer.unref?.();
|
|
566
|
+
return {
|
|
567
|
+
runtimeId: bundle.runtimeId,
|
|
568
|
+
credentialGeneration: bundle.credentialGeneration,
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
writeConfig({
|
|
572
|
+
...current,
|
|
573
|
+
runtimeToken: bundle.runtimeToken,
|
|
574
|
+
runtimeRenewalToken: bundle.runtimeRenewalToken,
|
|
575
|
+
runtimeTokenExpiresAt: bundle.runtimeTokenExpiresAt,
|
|
576
|
+
displayName: bundle.displayName,
|
|
577
|
+
});
|
|
578
|
+
clearPendingSetupIntent();
|
|
579
|
+
runtimeToken = bundle.runtimeToken;
|
|
580
|
+
runtimeRenewalToken = bundle.runtimeRenewalToken;
|
|
581
|
+
runtimeTokenExpiresAt = bundle.runtimeTokenExpiresAt;
|
|
582
|
+
socket.auth = { token: bundle.runtimeToken };
|
|
583
|
+
clearConnectErrors();
|
|
584
|
+
const ready = new Promise((resolveReady, rejectReady) => {
|
|
585
|
+
const timeout = setTimeout(() => {
|
|
586
|
+
socket.off("runtime.ready", onReady);
|
|
587
|
+
rejectReady(new Error("backend did not acknowledge the replacement credential generation"));
|
|
588
|
+
}, 15_000);
|
|
589
|
+
const onReady = (payload) => {
|
|
590
|
+
if (payload?.credentialGeneration !== bundle.credentialGeneration)
|
|
591
|
+
return;
|
|
592
|
+
clearTimeout(timeout);
|
|
593
|
+
socket.off("runtime.ready", onReady);
|
|
594
|
+
resolveReady();
|
|
595
|
+
};
|
|
596
|
+
socket.on("runtime.ready", onReady);
|
|
597
|
+
});
|
|
598
|
+
pushLog(`runtime-credential-setup-redial generation=${bundle.credentialGeneration}`);
|
|
599
|
+
if (socket.connected)
|
|
600
|
+
socket.disconnect();
|
|
601
|
+
socket.connect();
|
|
602
|
+
await ready;
|
|
603
|
+
pushLog(`runtime-credential-setup-ready generation=${bundle.credentialGeneration}`);
|
|
604
|
+
return {
|
|
605
|
+
runtimeId: bundle.runtimeId,
|
|
606
|
+
credentialGeneration: bundle.credentialGeneration,
|
|
607
|
+
};
|
|
608
|
+
});
|
|
609
|
+
runtimeSetupInFlight = attempt;
|
|
610
|
+
void attempt
|
|
611
|
+
.finally(() => {
|
|
612
|
+
if (runtimeSetupInFlight === attempt)
|
|
613
|
+
runtimeSetupInFlight = null;
|
|
614
|
+
})
|
|
615
|
+
.catch(() => { });
|
|
616
|
+
return attempt;
|
|
617
|
+
};
|
|
618
|
+
const relocationInFlight = new Set();
|
|
619
|
+
const persistWorkspaceRelocations = () => {
|
|
620
|
+
const current = readConfig();
|
|
621
|
+
writeConfig({
|
|
622
|
+
...current,
|
|
623
|
+
workspaceRelocations: workspaceRelocations.size > 0 ? [...workspaceRelocations.values()] : undefined,
|
|
624
|
+
});
|
|
625
|
+
};
|
|
626
|
+
const flushWorkspaceRelocations = () => {
|
|
627
|
+
if (!socket.connected)
|
|
628
|
+
return;
|
|
629
|
+
for (const [key, relocation] of workspaceRelocations) {
|
|
630
|
+
if (relocationInFlight.has(key))
|
|
631
|
+
continue;
|
|
632
|
+
relocationInFlight.add(key);
|
|
633
|
+
socket.emit("runtime.workspace.relocated", relocation, (result) => {
|
|
634
|
+
relocationInFlight.delete(key);
|
|
635
|
+
if (!result?.ok || !result.persisted)
|
|
636
|
+
return;
|
|
637
|
+
const current = workspaceRelocations.get(key);
|
|
638
|
+
if (current?.canonicalPath !== relocation.canonicalPath)
|
|
639
|
+
return;
|
|
640
|
+
workspaceRelocations.delete(key);
|
|
641
|
+
try {
|
|
642
|
+
persistWorkspaceRelocations();
|
|
643
|
+
pushLog(`workspace-relocation-persisted path=${relocation.canonicalPath}`);
|
|
644
|
+
}
|
|
645
|
+
catch (error) {
|
|
646
|
+
workspaceRelocations.set(key, relocation);
|
|
647
|
+
pushLog(`workspace-relocation-ack-save-failed ${error.message}`);
|
|
648
|
+
}
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
};
|
|
652
|
+
const emitRunnerPresence = () => {
|
|
653
|
+
if (!socket.connected)
|
|
654
|
+
return;
|
|
655
|
+
reconcileActiveRuns({ activeAgents, pushLog });
|
|
656
|
+
heartbeatSeq += 1;
|
|
657
|
+
const runnerSummary = summarizeRunnerStatus({
|
|
658
|
+
connected: socket.connected,
|
|
659
|
+
activeAgents,
|
|
660
|
+
disconnectedSinceMs,
|
|
661
|
+
daemonSessionId,
|
|
662
|
+
daemonEpoch,
|
|
663
|
+
heartbeatSeq,
|
|
664
|
+
interruptedRunIds: runJournal.recoveredRunIds(),
|
|
665
|
+
endpointProfile,
|
|
666
|
+
configPath,
|
|
667
|
+
runtimeId,
|
|
668
|
+
runtimeAuthValid: Boolean(runtimeToken),
|
|
669
|
+
endpointMismatchDetail: endpointMismatch?.detail ?? null,
|
|
670
|
+
workspacePath: recheckWorkspaceIssue(),
|
|
671
|
+
maintenanceReason: maintenanceReason(),
|
|
672
|
+
reservedStartCount: pendingRunStarts.size,
|
|
673
|
+
});
|
|
674
|
+
void currentMetadata().then((metadata) => {
|
|
675
|
+
socket.emit("runtime.heartbeat", {
|
|
676
|
+
capabilities: currentCapabilities(),
|
|
677
|
+
metadata: {
|
|
678
|
+
...metadata,
|
|
679
|
+
runnerStatus: runnerSummary.runnerStatus,
|
|
680
|
+
},
|
|
681
|
+
});
|
|
682
|
+
});
|
|
683
|
+
};
|
|
684
|
+
const heartbeat = setInterval(() => {
|
|
685
|
+
if (!socket.connected)
|
|
686
|
+
return;
|
|
687
|
+
emitRunnerPresence();
|
|
688
|
+
flushWorkspaceRelocations();
|
|
689
|
+
void flushNativeHookInbox();
|
|
690
|
+
}, 15_000);
|
|
691
|
+
// Report suspension after reconnect so the backend can grant grace instead of failing the run.
|
|
692
|
+
let pendingWakeGapMs = null;
|
|
693
|
+
const flushWakeGap = () => {
|
|
694
|
+
if (pendingWakeGapMs === null || !socket.connected)
|
|
695
|
+
return;
|
|
696
|
+
const gapMs = pendingWakeGapMs;
|
|
697
|
+
pendingWakeGapMs = null;
|
|
698
|
+
socket.emit("runtime.wake", { gapMs });
|
|
699
|
+
pushLog(`runtime-wake reported gapMs=${gapMs}`);
|
|
700
|
+
};
|
|
701
|
+
const suspensionDetector = new SuspensionDetector({
|
|
702
|
+
onSuspensionDetected: (gapMs) => {
|
|
703
|
+
// Keep the longest gap if several elapse before we reconnect.
|
|
704
|
+
pendingWakeGapMs = Math.max(pendingWakeGapMs ?? 0, gapMs);
|
|
705
|
+
pushLog(`host-suspension detected gapMs=${gapMs}`);
|
|
706
|
+
flushWakeGap();
|
|
707
|
+
},
|
|
708
|
+
});
|
|
709
|
+
suspensionDetector.start();
|
|
710
|
+
void refreshProviderVersions();
|
|
711
|
+
const versionRefresh = setInterval(() => {
|
|
712
|
+
void refreshProviderVersions();
|
|
713
|
+
}, VERSION_REFRESH_INTERVAL_MS);
|
|
714
|
+
// Transient lease-probe tolerance for git slowness; see notes/agent-daemon-app-nap.md.
|
|
715
|
+
const parsedMaxTransient = Number(process.env.ALAN_WORKSPACE_LEASE_MAX_TRANSIENT_FAILURES);
|
|
716
|
+
const WORKSPACE_LEASE_MAX_TRANSIENT_FAILURES = Number.isFinite(parsedMaxTransient) && parsedMaxTransient >= 1
|
|
717
|
+
? Math.floor(parsedMaxTransient)
|
|
718
|
+
: 3;
|
|
719
|
+
const WORKSPACE_LEASE_FAILURE_BACKOFF_BASE_MS = 1_000;
|
|
720
|
+
const WORKSPACE_LEASE_FAILURE_BACKOFF_MAX_MS = 15_000;
|
|
721
|
+
const stopRunForWorkspaceLease = (runId, entry, failure) => {
|
|
722
|
+
entry.stage = "failed";
|
|
723
|
+
if (!entry.presenter.failWorkspaceLease(failure))
|
|
724
|
+
return;
|
|
725
|
+
entry.agent.kill();
|
|
726
|
+
entry.cleanupState = "stopping";
|
|
727
|
+
void entry.agent.cleanup().then((cleaned) => {
|
|
728
|
+
if (cleaned && activeAgents.get(runId) === entry)
|
|
729
|
+
activeAgents.delete(runId);
|
|
730
|
+
else if (!cleaned)
|
|
731
|
+
entry.cleanupState = "failed";
|
|
732
|
+
});
|
|
733
|
+
pushLog(`workspace-lease-lost run=${runId} code=${failure.code}`);
|
|
734
|
+
};
|
|
735
|
+
const leaseCheckPolicy = {
|
|
736
|
+
maxTransientFailures: WORKSPACE_LEASE_MAX_TRANSIENT_FAILURES,
|
|
737
|
+
backoffBaseMs: WORKSPACE_LEASE_FAILURE_BACKOFF_BASE_MS,
|
|
738
|
+
backoffMaxMs: WORKSPACE_LEASE_FAILURE_BACKOFF_MAX_MS,
|
|
739
|
+
};
|
|
740
|
+
const verifyActiveWorkspaceLease = async (runId, entry, nowMs, force = false) => {
|
|
741
|
+
if (entry.cleanupState || entry.presenter.isTerminalFenced())
|
|
742
|
+
return false;
|
|
743
|
+
const lease = entry.workspaceLease;
|
|
744
|
+
if (!lease)
|
|
745
|
+
return true;
|
|
746
|
+
const state = entry.leaseCheck ?? { ...FRESH_LEASE_MONITOR_STATE };
|
|
747
|
+
const inFlight = entry.leaseCheckInFlight;
|
|
748
|
+
if (inFlight && (!force || inFlight.fresh))
|
|
749
|
+
return inFlight.promise;
|
|
750
|
+
if (!force && !isLeaseCheckDue(state, nowMs))
|
|
751
|
+
return true;
|
|
752
|
+
let verification;
|
|
753
|
+
verification = Promise.resolve(lease.verifyDetailed(force ? { fresh: true } : undefined))
|
|
754
|
+
.then((detailed) => {
|
|
755
|
+
if (activeAgents.get(runId) !== entry ||
|
|
756
|
+
entry.cleanupState ||
|
|
757
|
+
entry.presenter.isTerminalFenced()) {
|
|
758
|
+
return false;
|
|
759
|
+
}
|
|
760
|
+
const decision = applyLeaseCheckResult({
|
|
761
|
+
state,
|
|
762
|
+
detailed,
|
|
763
|
+
nowMs: Date.now(),
|
|
764
|
+
policy: leaseCheckPolicy,
|
|
765
|
+
});
|
|
766
|
+
entry.leaseCheck =
|
|
767
|
+
decision.action === "ok"
|
|
768
|
+
? { ...decision.state, nextCheckAtMs: Date.now() + WORKSPACE_LEASE_CHECK_INTERVAL_MS }
|
|
769
|
+
: decision.state;
|
|
770
|
+
if (decision.action === "fail" && decision.failure) {
|
|
771
|
+
stopRunForWorkspaceLease(runId, entry, decision.failure);
|
|
772
|
+
return false;
|
|
773
|
+
}
|
|
774
|
+
else if (decision.action === "transient" && decision.failure) {
|
|
775
|
+
pushLog(`workspace-lease-transient run=${runId} streak=${decision.state.transientStreak}/${WORKSPACE_LEASE_MAX_TRANSIENT_FAILURES} code=${decision.failure.code} backoff_ms=${decision.backoffMs ?? 0}`);
|
|
776
|
+
}
|
|
777
|
+
return true;
|
|
778
|
+
})
|
|
779
|
+
.catch((error) => {
|
|
780
|
+
pushLog(`workspace-lease-check-failed run=${runId} ${String(error)}`);
|
|
781
|
+
return false;
|
|
782
|
+
})
|
|
783
|
+
.finally(() => {
|
|
784
|
+
if (entry.leaseCheckInFlight?.promise === verification) {
|
|
785
|
+
entry.leaseCheckInFlight = undefined;
|
|
786
|
+
}
|
|
787
|
+
});
|
|
788
|
+
entry.leaseCheckInFlight = { promise: verification, fresh: force };
|
|
789
|
+
return verification;
|
|
790
|
+
};
|
|
791
|
+
const workspaceLeaseMonitor = setInterval(() => {
|
|
792
|
+
const nowMs = Date.now();
|
|
793
|
+
for (const [runId, entry] of activeAgents) {
|
|
794
|
+
void verifyActiveWorkspaceLease(runId, entry, nowMs);
|
|
795
|
+
}
|
|
796
|
+
}, WORKSPACE_LEASE_CHECK_INTERVAL_MS);
|
|
797
|
+
workspaceLeaseMonitor.unref?.();
|
|
798
|
+
socket.on("connect", () => {
|
|
799
|
+
disconnectedSinceMs = null;
|
|
800
|
+
clearConnectErrors();
|
|
801
|
+
setConnectedRuntimeSocket(socket);
|
|
802
|
+
pushLog(`connected runtime=${runtimeId}`);
|
|
803
|
+
console.info("[alan-agent] Daemon connected", { runtimeId, wsUrl, version: AGENT_VERSION });
|
|
804
|
+
flushWorkspaceRelocations();
|
|
805
|
+
flushPendingDeliveryLostEvents({
|
|
806
|
+
socket,
|
|
807
|
+
activeAgents,
|
|
808
|
+
pending: pendingDeliveryLost,
|
|
809
|
+
eventOutbox,
|
|
810
|
+
pushLog,
|
|
811
|
+
});
|
|
812
|
+
reconcileActiveRuns({ activeAgents, pushLog });
|
|
813
|
+
// The server responds with the exact repositories and onboarding-enabled
|
|
814
|
+
// provider kinds. Do not perform a broad local scan before that authority arrives.
|
|
815
|
+
initialSkillReadiness = runSkillSync("startup");
|
|
816
|
+
void currentMetadata().then((metadata) => {
|
|
817
|
+
const runnerSummary = summarizeRunnerStatus({
|
|
818
|
+
connected: true,
|
|
819
|
+
activeAgents,
|
|
820
|
+
disconnectedSinceMs,
|
|
821
|
+
daemonSessionId,
|
|
822
|
+
daemonEpoch,
|
|
823
|
+
heartbeatSeq,
|
|
824
|
+
interruptedRunIds: runJournal.recoveredRunIds(),
|
|
825
|
+
endpointProfile,
|
|
826
|
+
configPath,
|
|
827
|
+
runtimeId,
|
|
828
|
+
runtimeAuthValid: Boolean(runtimeToken),
|
|
829
|
+
endpointMismatchDetail: endpointMismatch?.detail ?? null,
|
|
830
|
+
workspacePath: recheckWorkspaceIssue(),
|
|
831
|
+
maintenanceReason: maintenanceReason(),
|
|
832
|
+
reservedStartCount: pendingRunStarts.size,
|
|
833
|
+
});
|
|
834
|
+
socket.emit("runtime.hello", {
|
|
835
|
+
capabilities: currentCapabilities(),
|
|
836
|
+
metadata: {
|
|
837
|
+
...metadata,
|
|
838
|
+
runnerStatus: runnerSummary.runnerStatus,
|
|
839
|
+
},
|
|
840
|
+
});
|
|
841
|
+
// Emitted after hello so the backend has already marked this runtime
|
|
842
|
+
// online and can attribute the wake to a known connection.
|
|
843
|
+
flushWakeGap();
|
|
844
|
+
scheduleRuntimeModelScan(() => Promise.resolve(metadata));
|
|
845
|
+
});
|
|
846
|
+
});
|
|
847
|
+
let lastConnectErrorLogAt = 0;
|
|
848
|
+
socket.on("connect_error", (error) => {
|
|
849
|
+
if (!socket.connected && disconnectedSinceMs === null)
|
|
850
|
+
disconnectedSinceMs = Date.now();
|
|
851
|
+
lastConnectError = error.message;
|
|
852
|
+
const errorCode = error?.data?.code;
|
|
853
|
+
// Middleware rejections carry a server code. A connect_error without one is
|
|
854
|
+
// a transport/service reachability failure, not evidence that the local
|
|
855
|
+
// daemon or Computer is broken.
|
|
856
|
+
rememberConnectErrorCode(typeof errorCode === "string" ? errorCode : classifyTransportFailure(error));
|
|
857
|
+
pushLog(`connect_error ${error.message}${lastConnectErrorCode ? ` code=${lastConnectErrorCode}` : ""}`);
|
|
858
|
+
if ((lastConnectErrorCode === "invalid_runtime_token" ||
|
|
859
|
+
terminalAuthFailureCode === "invalid_runtime_token") &&
|
|
860
|
+
runtimeRenewalToken) {
|
|
861
|
+
void renewRuntimeLease().catch((renewalError) => {
|
|
862
|
+
rememberConnectErrorCode("runtime_renewal_failed");
|
|
863
|
+
lastConnectError = `Runtime renewal failed unexpectedly: ${renewalError.message}`;
|
|
864
|
+
pushLog(`runtime-renewal-unexpected-failure ${renewalError.message}`);
|
|
865
|
+
});
|
|
866
|
+
return;
|
|
867
|
+
}
|
|
868
|
+
const now = Date.now();
|
|
869
|
+
if (now - lastConnectErrorLogAt < 5000)
|
|
870
|
+
return;
|
|
871
|
+
lastConnectErrorLogAt = now;
|
|
872
|
+
const wsHint = isLocalEndpoint(wsUrl)
|
|
873
|
+
? ` Nothing is listening at ${wsUrl}. For local dev run setup with --profile local and start the stack (WS on ws://localhost:8401).`
|
|
874
|
+
: ` Verify wsUrl in ${getConfigPath()} and that you can reach ${wsUrl}.`;
|
|
875
|
+
console.warn("[alan-agent] Daemon connection error:", `${error.message}.${wsHint}`);
|
|
876
|
+
});
|
|
877
|
+
socket.on("connection_directive", (value) => {
|
|
878
|
+
if (!value || typeof value !== "object")
|
|
879
|
+
return;
|
|
880
|
+
const directive = value;
|
|
881
|
+
if (directive.retryable !== false || typeof directive.code !== "string")
|
|
882
|
+
return;
|
|
883
|
+
rememberConnectErrorCode(directive.code);
|
|
884
|
+
if (typeof directive.reason === "string")
|
|
885
|
+
lastConnectError = directive.reason;
|
|
886
|
+
pushLog(`connection_directive ${directive.code}${lastConnectError ? ` reason=${lastConnectError}` : ""}`);
|
|
887
|
+
});
|
|
888
|
+
socket.on("disconnect", (reason) => {
|
|
889
|
+
setConnectedRuntimeSocket(null);
|
|
890
|
+
relocationInFlight.clear();
|
|
891
|
+
eventOutbox.disconnect();
|
|
892
|
+
nativeStreamOutbox?.disconnect();
|
|
893
|
+
nativeStreamEnrollment = false;
|
|
894
|
+
nativeDeliveryReady = false;
|
|
895
|
+
if (disconnectedSinceMs === null)
|
|
896
|
+
disconnectedSinceMs = Date.now();
|
|
897
|
+
pushLog(`disconnected ${reason}`);
|
|
898
|
+
console.info("[alan-agent] Daemon disconnected:", reason);
|
|
899
|
+
for (const [runId, entry] of activeAgents.entries()) {
|
|
900
|
+
if (entry.stage === "completed" ||
|
|
901
|
+
entry.stage === "failed" ||
|
|
902
|
+
entry.stage === "aborted" ||
|
|
903
|
+
entry.stage === "delivery_lost") {
|
|
904
|
+
continue;
|
|
905
|
+
}
|
|
906
|
+
entry.stage = "delivery_lost";
|
|
907
|
+
pendingDeliveryLost.push({ conversationId: entry.conversationId, runId });
|
|
908
|
+
}
|
|
909
|
+
});
|
|
910
|
+
socket.on("desktop:rpc", async (message) => {
|
|
911
|
+
if (!message?.id || !message.method)
|
|
912
|
+
return;
|
|
913
|
+
try {
|
|
914
|
+
const params = message.params ?? {};
|
|
915
|
+
const result = isBindingSwitchGitMethod(message.method)
|
|
916
|
+
? await executeBindingSwitchGitRpc(message.method, params)
|
|
917
|
+
: executeWorkspaceRpc(message.method, params);
|
|
918
|
+
socket.emit("desktop:rpc", { jsonrpc: "2.0", id: message.id, result });
|
|
919
|
+
}
|
|
920
|
+
catch (error) {
|
|
921
|
+
const stableCode = error instanceof BindingSwitchGitError ? error.code : "runtime_operation_failed";
|
|
922
|
+
const stableDetails = error instanceof BindingSwitchGitError ? error.details : undefined;
|
|
923
|
+
socket.emit("desktop:rpc", {
|
|
924
|
+
jsonrpc: "2.0",
|
|
925
|
+
id: message.id,
|
|
926
|
+
error: {
|
|
927
|
+
code: -32000,
|
|
928
|
+
message: error.message,
|
|
929
|
+
data: { ...stableDetails, code: stableCode },
|
|
930
|
+
},
|
|
931
|
+
});
|
|
932
|
+
}
|
|
933
|
+
});
|
|
934
|
+
socket.on("agent.execute", async (payload) => {
|
|
935
|
+
if (shutdownAbort.signal.aborted)
|
|
936
|
+
return;
|
|
937
|
+
if (payload.skillInvocation || payload.skillInvocations?.length) {
|
|
938
|
+
let readiness = await waitForSkillReadiness(initialSkillReadiness, 15_000);
|
|
939
|
+
if (readiness.status === "failed") {
|
|
940
|
+
initialSkillReadiness = runSkillSync("sync");
|
|
941
|
+
readiness = await waitForSkillReadiness(initialSkillReadiness, 15_000);
|
|
942
|
+
}
|
|
943
|
+
if (readiness.status !== "ready") {
|
|
944
|
+
socket.emit("agent.rejected", {
|
|
945
|
+
runId: payload.runId,
|
|
946
|
+
message: readiness.status === "timeout"
|
|
947
|
+
? "Skills are still synchronizing on this runtime. Try again in a moment."
|
|
948
|
+
: `Skills could not be synchronized on this runtime: ${readiness.message}`,
|
|
949
|
+
code: "skill_sync_unavailable",
|
|
950
|
+
});
|
|
951
|
+
return;
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
await handleAgentExecute({
|
|
955
|
+
activeAgents,
|
|
956
|
+
shutdownSignal: shutdownAbort.signal,
|
|
957
|
+
pendingRunStarts,
|
|
958
|
+
pushLog,
|
|
959
|
+
socket,
|
|
960
|
+
runJournal,
|
|
961
|
+
publishCapacityChange: emitRunnerPresence,
|
|
962
|
+
runtimeId,
|
|
963
|
+
runtimeSkillManager,
|
|
964
|
+
getRuntimeToken: () => runtimeToken,
|
|
965
|
+
ensureRuntimeTokenForLaunch,
|
|
966
|
+
eventOutbox,
|
|
967
|
+
daemonSessionId,
|
|
968
|
+
daemonEpoch,
|
|
969
|
+
getHeartbeatSeq: () => heartbeatSeq,
|
|
970
|
+
endpointProfile,
|
|
971
|
+
apiUrl,
|
|
972
|
+
configPath,
|
|
973
|
+
endpointMismatch,
|
|
974
|
+
workspaceAccessTracker,
|
|
975
|
+
getDisconnectedSinceMs: () => disconnectedSinceMs,
|
|
976
|
+
workspaceRelocations,
|
|
977
|
+
persistWorkspaceRelocations,
|
|
978
|
+
flushWorkspaceRelocations,
|
|
979
|
+
recheckWorkspaceIssue,
|
|
980
|
+
maintenanceReason,
|
|
981
|
+
verifyActiveWorkspaceLease,
|
|
982
|
+
}, payload);
|
|
983
|
+
});
|
|
984
|
+
socket.on("agent.abort", (payload) => {
|
|
985
|
+
if (!payload?.runId)
|
|
986
|
+
return;
|
|
987
|
+
const entry = activeAgents.get(payload.runId);
|
|
988
|
+
if (!entry)
|
|
989
|
+
return;
|
|
990
|
+
// Only server-side timeouts are accepted as a non-user reason; everything
|
|
991
|
+
// else (including legacy payloads without a reason) is a user stop.
|
|
992
|
+
const reason = payload.reason === "watchdog_timeout" ? "watchdog_timeout" : "user";
|
|
993
|
+
abortActiveAgent(entry, { userInitiated: true, reason });
|
|
994
|
+
pushLog(`aborted run=${payload.runId} reason=${reason}`);
|
|
995
|
+
console.info("[alan-agent] Agent run aborted", {
|
|
996
|
+
runId: payload.runId,
|
|
997
|
+
reason,
|
|
998
|
+
agentVersion: AGENT_VERSION,
|
|
999
|
+
});
|
|
1000
|
+
});
|
|
1001
|
+
socket.on("agent.tool_response", async (payload) => {
|
|
1002
|
+
if (!payload?.toolId || typeof payload.response !== "string")
|
|
1003
|
+
return;
|
|
1004
|
+
// Route tool answers by run/conversation; stdin writes do not verify tool ownership.
|
|
1005
|
+
const entries = payload.runId
|
|
1006
|
+
? [[payload.runId, activeAgents.get(payload.runId)]]
|
|
1007
|
+
: payload.conversationId
|
|
1008
|
+
? [...activeAgents.entries()].filter(([, entry]) => entry.conversationId === payload.conversationId)
|
|
1009
|
+
: [...activeAgents.entries()];
|
|
1010
|
+
let delivered = false;
|
|
1011
|
+
for (const [runId, entry] of entries) {
|
|
1012
|
+
if (!entry || !(await verifyActiveWorkspaceLease(runId, entry, Date.now(), true))) {
|
|
1013
|
+
continue;
|
|
1014
|
+
}
|
|
1015
|
+
if (entry.agent.sendToolResponse(payload.toolId, payload.response) === true) {
|
|
1016
|
+
delivered = true;
|
|
1017
|
+
break;
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
if (!delivered) {
|
|
1021
|
+
pushLog(`tool_response missed tool=${payload.toolId}`);
|
|
1022
|
+
// Surface the lost delivery to the conversation instead of hanging
|
|
1023
|
+
// silently until a timeout: the agent process is gone or its stdin is
|
|
1024
|
+
// closed, so the user's answer can only arrive as a new message.
|
|
1025
|
+
// Resolve the conversation even when the runId is stale (e.g. the run was
|
|
1026
|
+
// retried or already reaped) so the error still lands in the right chat.
|
|
1027
|
+
const fallbackRun = [...activeAgents.entries()][0];
|
|
1028
|
+
const resolvedRunId = payload.runId ?? entries[0]?.[0] ?? fallbackRun?.[0];
|
|
1029
|
+
const conversationId = payload.conversationId ??
|
|
1030
|
+
(payload.runId ? activeAgents.get(payload.runId)?.conversationId : undefined) ??
|
|
1031
|
+
fallbackRun?.[1].conversationId;
|
|
1032
|
+
if (conversationId && resolvedRunId) {
|
|
1033
|
+
eventOutbox.enqueue(conversationId, resolvedRunId, {
|
|
1034
|
+
type: "error",
|
|
1035
|
+
message: "Your answer could not be delivered to the running agent — its input channel is closed. Send your answer as a new message to continue.",
|
|
1036
|
+
});
|
|
1037
|
+
eventOutbox.flush(socket);
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
});
|
|
1041
|
+
socket.on("runtime.skills.invalidate", () => {
|
|
1042
|
+
void runSkillSync("refresh");
|
|
1043
|
+
});
|
|
1044
|
+
socket.on("runtime.models.scan", (payload) => {
|
|
1045
|
+
if (!payload?.requestId)
|
|
1046
|
+
return;
|
|
1047
|
+
const requestId = payload.requestId;
|
|
1048
|
+
void (async () => {
|
|
1049
|
+
try {
|
|
1050
|
+
await runRuntimeModelScan({
|
|
1051
|
+
refreshModels: refreshProviderModels,
|
|
1052
|
+
getCapabilities: () => discoverCapabilities(),
|
|
1053
|
+
getBaseMetadata: async () => (await currentMetadata()),
|
|
1054
|
+
publishCredentials: (() => {
|
|
1055
|
+
const stored = readConfig();
|
|
1056
|
+
if (!stored.runtimeId || !stored.runtimeToken || !stored.apiUrl)
|
|
1057
|
+
return undefined;
|
|
1058
|
+
return {
|
|
1059
|
+
apiUrl: stored.apiUrl,
|
|
1060
|
+
runtimeId: stored.runtimeId,
|
|
1061
|
+
runtimeToken: stored.runtimeToken,
|
|
1062
|
+
};
|
|
1063
|
+
})(),
|
|
1064
|
+
}, { force: payload.force === true || payload.refresh === true });
|
|
1065
|
+
const metadata = await currentMetadata();
|
|
1066
|
+
socket.emit("runtime.models.result", {
|
|
1067
|
+
requestId,
|
|
1068
|
+
capabilities: discoverCapabilities(),
|
|
1069
|
+
metadata: {
|
|
1070
|
+
...metadata,
|
|
1071
|
+
...buildModelsScanMetadata("complete"),
|
|
1072
|
+
},
|
|
1073
|
+
});
|
|
1074
|
+
}
|
|
1075
|
+
catch (error) {
|
|
1076
|
+
socket.emit("runtime.models.result", {
|
|
1077
|
+
requestId,
|
|
1078
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1079
|
+
});
|
|
1080
|
+
}
|
|
1081
|
+
})();
|
|
1082
|
+
});
|
|
1083
|
+
const adoptionChallenges = new Map();
|
|
1084
|
+
let queueFullShutdown = () => { };
|
|
1085
|
+
const localServer = http.createServer(createLocalDaemonHttpHandler({
|
|
1086
|
+
adoptionChallenges,
|
|
1087
|
+
localServiceId,
|
|
1088
|
+
localServiceSecret,
|
|
1089
|
+
installationId,
|
|
1090
|
+
runtimeId,
|
|
1091
|
+
getLocalApiPort: () => localApiPort,
|
|
1092
|
+
localServiceStartedAtMs,
|
|
1093
|
+
localApiToken,
|
|
1094
|
+
activeAgents,
|
|
1095
|
+
pendingRunStarts,
|
|
1096
|
+
pushLog,
|
|
1097
|
+
socket,
|
|
1098
|
+
getDisconnectedSinceMs: () => disconnectedSinceMs,
|
|
1099
|
+
daemonSessionId,
|
|
1100
|
+
daemonEpoch,
|
|
1101
|
+
getHeartbeatSeq: () => heartbeatSeq,
|
|
1102
|
+
runJournal,
|
|
1103
|
+
endpointProfile,
|
|
1104
|
+
configPath,
|
|
1105
|
+
getRuntimeToken: () => runtimeToken,
|
|
1106
|
+
endpointMismatchDetail: endpointMismatch?.detail ?? null,
|
|
1107
|
+
recheckWorkspaceIssue,
|
|
1108
|
+
maintenanceReason,
|
|
1109
|
+
getLastConnectError: () => lastConnectError,
|
|
1110
|
+
getLastConnectErrorCode: () => lastConnectErrorCode,
|
|
1111
|
+
setLastConnectError: (v) => {
|
|
1112
|
+
lastConnectError = v;
|
|
1113
|
+
},
|
|
1114
|
+
setLastConnectErrorCode: (v) => {
|
|
1115
|
+
if (v === null) {
|
|
1116
|
+
// Explicit reconnect clears transport noise, but sticky identity
|
|
1117
|
+
// rejections stay until a successful connect/renew clears them.
|
|
1118
|
+
lastConnectErrorCode = terminalAuthFailureCode;
|
|
1119
|
+
return;
|
|
1120
|
+
}
|
|
1121
|
+
rememberConnectErrorCode(v);
|
|
1122
|
+
},
|
|
1123
|
+
workspaceAccessTracker,
|
|
1124
|
+
recentLogs,
|
|
1125
|
+
emitRunnerPresence,
|
|
1126
|
+
queueFullShutdown: () => queueFullShutdown(),
|
|
1127
|
+
wsUrl,
|
|
1128
|
+
inspectLocalSkill: (input) => runtimeSkillManager.inspectLocalPackage(input),
|
|
1129
|
+
readLocalSkillFile: (input) => runtimeSkillManager.readLocalPackageFile(input),
|
|
1130
|
+
adoptRuntimeSetupGrant,
|
|
1131
|
+
}));
|
|
1132
|
+
const occupiedProbe = await probeLocalDaemonEndpoint(localApiPort, localApiToken);
|
|
1133
|
+
if (occupiedProbe.state === "running") {
|
|
1134
|
+
clearInterval(heartbeat);
|
|
1135
|
+
clearInterval(versionRefresh);
|
|
1136
|
+
clearInterval(workspaceLeaseMonitor);
|
|
1137
|
+
suspensionDetector.stop();
|
|
1138
|
+
socket.disconnect();
|
|
1139
|
+
releaseOwnerLease();
|
|
1140
|
+
releaseStartupLease();
|
|
1141
|
+
throw new Error(`${describeOccupiedLocalDaemon(localApiPort)} (runtime ${occupiedProbe.status.runtimeId})`);
|
|
1142
|
+
}
|
|
1143
|
+
if (occupiedProbe.state === "listening_unauthorized") {
|
|
1144
|
+
clearInterval(heartbeat);
|
|
1145
|
+
clearInterval(versionRefresh);
|
|
1146
|
+
clearInterval(workspaceLeaseMonitor);
|
|
1147
|
+
suspensionDetector.stop();
|
|
1148
|
+
socket.disconnect();
|
|
1149
|
+
releaseOwnerLease();
|
|
1150
|
+
releaseStartupLease();
|
|
1151
|
+
throw new Error(`${describeOccupiedLocalDaemon(localApiPort)} The token in ${getConfigPath()} no longer matches the running daemon; quit Alan desktop or run setup again.`);
|
|
1152
|
+
}
|
|
1153
|
+
const localApiListening = new Promise((resolve, reject) => {
|
|
1154
|
+
const onError = (error) => {
|
|
1155
|
+
clearInterval(heartbeat);
|
|
1156
|
+
clearInterval(versionRefresh);
|
|
1157
|
+
clearInterval(workspaceLeaseMonitor);
|
|
1158
|
+
suspensionDetector.stop();
|
|
1159
|
+
socket.disconnect();
|
|
1160
|
+
if (error.code === "EADDRINUSE") {
|
|
1161
|
+
void probeLocalDaemonEndpoint(localApiPort).then((probe) => {
|
|
1162
|
+
if (probe.state !== "absent") {
|
|
1163
|
+
reject(new Error(describeOccupiedLocalDaemon(localApiPort)));
|
|
1164
|
+
return;
|
|
1165
|
+
}
|
|
1166
|
+
reject(new Error(`local daemon API port ${localApiPort} is already in use by another process. ` +
|
|
1167
|
+
"Otherwise free the port or pass `--local-api-port` with a different value."));
|
|
1168
|
+
});
|
|
1169
|
+
return;
|
|
1170
|
+
}
|
|
1171
|
+
reject(error);
|
|
1172
|
+
};
|
|
1173
|
+
localServer.once("error", onError);
|
|
1174
|
+
localServer.listen(localApiPort, "127.0.0.1", () => {
|
|
1175
|
+
localServer.off("error", onError);
|
|
1176
|
+
const address = localServer.address();
|
|
1177
|
+
if (address && typeof address === "object")
|
|
1178
|
+
localApiPort = address.port;
|
|
1179
|
+
pushLog(`local_api listening port=${localApiPort}`);
|
|
1180
|
+
console.info("[alan-agent] Local daemon API listening", { port: localApiPort });
|
|
1181
|
+
resolve();
|
|
1182
|
+
});
|
|
1183
|
+
});
|
|
1184
|
+
try {
|
|
1185
|
+
await localApiListening;
|
|
1186
|
+
}
|
|
1187
|
+
catch (error) {
|
|
1188
|
+
releaseOwnerLease();
|
|
1189
|
+
releaseStartupLease();
|
|
1190
|
+
throw error;
|
|
1191
|
+
}
|
|
1192
|
+
try {
|
|
1193
|
+
ownerLease = updateLocalDaemonOwnerEndpoint(configPath, ownerLease, localApiPort);
|
|
1194
|
+
// The config-scoped owner is held and its authenticated endpoint is now
|
|
1195
|
+
// bound. Only now may prior journal entries be persisted as interrupted.
|
|
1196
|
+
runJournal.confirmRecoveryOwnership();
|
|
1197
|
+
pendingRunStarts.discardRecovered();
|
|
1198
|
+
}
|
|
1199
|
+
catch (error) {
|
|
1200
|
+
clearInterval(heartbeat);
|
|
1201
|
+
clearInterval(versionRefresh);
|
|
1202
|
+
clearInterval(workspaceLeaseMonitor);
|
|
1203
|
+
suspensionDetector.stop();
|
|
1204
|
+
socket.disconnect();
|
|
1205
|
+
await new Promise((resolve) => localServer.close(() => resolve()));
|
|
1206
|
+
releaseOwnerLease();
|
|
1207
|
+
releaseStartupLease();
|
|
1208
|
+
throw error;
|
|
1209
|
+
}
|
|
1210
|
+
releaseStartupLease();
|
|
1211
|
+
// Incremental scans reuse cached file hashes and only inspect the source roots
|
|
1212
|
+
// selected during onboarding; the daily audit deliberately ignores that cache.
|
|
1213
|
+
const skillSyncInterval = setInterval(() => void runSkillSync("interval"), 60_000);
|
|
1214
|
+
skillSyncInterval.unref?.();
|
|
1215
|
+
const skillAuditInterval = setInterval(() => void runSkillSync("refresh"), 24 * 60 * 60_000);
|
|
1216
|
+
skillAuditInterval.unref?.();
|
|
1217
|
+
const nativeHookDrainInterval = setInterval(() => void flushNativeHookInbox(), 1_000);
|
|
1218
|
+
nativeHookDrainInterval.unref?.();
|
|
1219
|
+
let stopInFlight = null;
|
|
1220
|
+
const stopOwned = async () => {
|
|
1221
|
+
let shutdownComplete = false;
|
|
1222
|
+
try {
|
|
1223
|
+
clearInterval(heartbeat);
|
|
1224
|
+
clearInterval(versionRefresh);
|
|
1225
|
+
clearInterval(workspaceLeaseMonitor);
|
|
1226
|
+
clearInterval(skillSyncInterval);
|
|
1227
|
+
clearInterval(skillAuditInterval);
|
|
1228
|
+
clearInterval(nativeHookDrainInterval);
|
|
1229
|
+
suspensionDetector.stop();
|
|
1230
|
+
shutdownAbort.abort(new Error("Daemon stopping"));
|
|
1231
|
+
socket.disconnect();
|
|
1232
|
+
// scheduleRuntimeModelScan() on socket "connect" is fire-and-forget: it
|
|
1233
|
+
// spawns each installed provider CLI (codex, claude, antigravity, ...)
|
|
1234
|
+
// to probe versions/models and was never joined by shutdown, so a probe
|
|
1235
|
+
// could still be writing to its own CLI-owned config/log directory
|
|
1236
|
+
// after stop() had already resolved. Reproduced on macOS: 10 back-to-
|
|
1237
|
+
// back daemon-child cycles raced a still-running `agy` (antigravity
|
|
1238
|
+
// CLI) version probe against that cycle's temp HOME directory cleanup.
|
|
1239
|
+
// Bounded wait: a slow or hung provider CLI must not block shutdown.
|
|
1240
|
+
const pendingModelScan = getModelScanInFlight();
|
|
1241
|
+
if (pendingModelScan)
|
|
1242
|
+
await Promise.race([pendingModelScan, sleep(3_000)]);
|
|
1243
|
+
const stoppingEntries = [...activeAgents.values()];
|
|
1244
|
+
for (const entry of stoppingEntries) {
|
|
1245
|
+
abortActiveAgent(entry, { reason: "runtime_shutdown" });
|
|
1246
|
+
}
|
|
1247
|
+
const cleanupResults = await Promise.all(stoppingEntries.map((entry) => entry.agent.cleanup()));
|
|
1248
|
+
const providersExited = cleanupResults.every(Boolean);
|
|
1249
|
+
if (!providersExited)
|
|
1250
|
+
throw new Error("Provider cleanup failed; daemon ownership retained");
|
|
1251
|
+
activeAgents.clear();
|
|
1252
|
+
await nativeHookDrainInFlight;
|
|
1253
|
+
await nativeStreamOutbox?.close();
|
|
1254
|
+
// A momentary I/O error must not fail shutdown and strand the owner
|
|
1255
|
+
// lease: the failed batch is requeued, so retry the flush a bounded
|
|
1256
|
+
// number of times and only refuse to release ownership when the journal
|
|
1257
|
+
// still cannot persist. Include the recorded cause: without it the next
|
|
1258
|
+
// failure of this kind is undebuggable (the outbox logs to the daemon
|
|
1259
|
+
// log file, not the test output).
|
|
1260
|
+
for (let attempt = 0;; attempt++) {
|
|
1261
|
+
await eventOutbox.flushDurable();
|
|
1262
|
+
const stopMetrics = eventOutbox.metrics();
|
|
1263
|
+
if (!stopMetrics.persistFailure)
|
|
1264
|
+
break;
|
|
1265
|
+
if (attempt >= 2)
|
|
1266
|
+
throw new Error(`Event journal flush failed (${stopMetrics.persistFailure}); daemon ownership retained`);
|
|
1267
|
+
await sleep(100);
|
|
1268
|
+
}
|
|
1269
|
+
await new Promise((resolve) => {
|
|
1270
|
+
localServer.close(() => resolve());
|
|
1271
|
+
});
|
|
1272
|
+
shutdownComplete = true;
|
|
1273
|
+
}
|
|
1274
|
+
finally {
|
|
1275
|
+
// Release the sleep assertion even if teardown above threw; a leaked
|
|
1276
|
+
// helper would keep the machine awake indefinitely.
|
|
1277
|
+
sleepInhibitor.dispose();
|
|
1278
|
+
// Never advertise the endpoint as replaceable while a provider process
|
|
1279
|
+
// from this daemon is still alive. The owner lease then expires or is
|
|
1280
|
+
// released when the containing process exits, preventing overlap.
|
|
1281
|
+
if (shutdownComplete)
|
|
1282
|
+
releaseOwnerLease();
|
|
1283
|
+
}
|
|
1284
|
+
};
|
|
1285
|
+
const stop = () => {
|
|
1286
|
+
if (!stopInFlight) {
|
|
1287
|
+
stopInFlight = stopOwned().catch((error) => {
|
|
1288
|
+
stopInFlight = null;
|
|
1289
|
+
throw error;
|
|
1290
|
+
});
|
|
1291
|
+
}
|
|
1292
|
+
return stopInFlight;
|
|
1293
|
+
};
|
|
1294
|
+
const controller = {
|
|
1295
|
+
runtimeId,
|
|
1296
|
+
wsUrl,
|
|
1297
|
+
localApiPort,
|
|
1298
|
+
localApiToken,
|
|
1299
|
+
stop,
|
|
1300
|
+
};
|
|
1301
|
+
queueFullShutdown = () => {
|
|
1302
|
+
void stop()
|
|
1303
|
+
.then(() => controller.whenExternallyStopped?.())
|
|
1304
|
+
.catch((error) => pushLog(`daemon-shutdown-failed ${String(error)}`));
|
|
1305
|
+
};
|
|
1306
|
+
queueIdentityRestart = () => {
|
|
1307
|
+
void stop()
|
|
1308
|
+
.then(() => controller.whenExternallyStopped?.("runtime_identity_changed"))
|
|
1309
|
+
.catch((error) => pushLog(`daemon-identity-restart-failed ${String(error)}`));
|
|
1310
|
+
};
|
|
1311
|
+
return controller;
|
|
1312
|
+
}
|
|
1313
|
+
//# sourceMappingURL=daemon-start.js.map
|