@telora/daemon 0.12.33
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/.env.example +64 -0
- package/README.md +229 -0
- package/build-info.json +4 -0
- package/dist/activity-tracker.d.ts +13 -0
- package/dist/activity-tracker.d.ts.map +1 -0
- package/dist/activity-tracker.js +19 -0
- package/dist/activity-tracker.js.map +1 -0
- package/dist/agent-state.d.ts +45 -0
- package/dist/agent-state.d.ts.map +1 -0
- package/dist/agent-state.js +61 -0
- package/dist/agent-state.js.map +1 -0
- package/dist/audit-hooks.d.ts +12 -0
- package/dist/audit-hooks.d.ts.map +1 -0
- package/dist/audit-hooks.js +45 -0
- package/dist/audit-hooks.js.map +1 -0
- package/dist/auto-update.d.ts +42 -0
- package/dist/auto-update.d.ts.map +1 -0
- package/dist/auto-update.js +96 -0
- package/dist/auto-update.js.map +1 -0
- package/dist/branch-status.d.ts +40 -0
- package/dist/branch-status.d.ts.map +1 -0
- package/dist/branch-status.js +107 -0
- package/dist/branch-status.js.map +1 -0
- package/dist/completion-detector.d.ts +87 -0
- package/dist/completion-detector.d.ts.map +1 -0
- package/dist/completion-detector.js +160 -0
- package/dist/completion-detector.js.map +1 -0
- package/dist/completion-handler.d.ts +48 -0
- package/dist/completion-handler.d.ts.map +1 -0
- package/dist/completion-handler.js +200 -0
- package/dist/completion-handler.js.map +1 -0
- package/dist/condition-evaluators.d.ts +31 -0
- package/dist/condition-evaluators.d.ts.map +1 -0
- package/dist/condition-evaluators.js +416 -0
- package/dist/condition-evaluators.js.map +1 -0
- package/dist/config.d.ts +55 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +311 -0
- package/dist/config.js.map +1 -0
- package/dist/control-state.d.ts +41 -0
- package/dist/control-state.d.ts.map +1 -0
- package/dist/control-state.js +204 -0
- package/dist/control-state.js.map +1 -0
- package/dist/crash-recovery-cleanup.d.ts +21 -0
- package/dist/crash-recovery-cleanup.d.ts.map +1 -0
- package/dist/crash-recovery-cleanup.js +198 -0
- package/dist/crash-recovery-cleanup.js.map +1 -0
- package/dist/crash-recovery-scan.d.ts +19 -0
- package/dist/crash-recovery-scan.d.ts.map +1 -0
- package/dist/crash-recovery-scan.js +145 -0
- package/dist/crash-recovery-scan.js.map +1 -0
- package/dist/crash-recovery-types.d.ts +54 -0
- package/dist/crash-recovery-types.d.ts.map +1 -0
- package/dist/crash-recovery-types.js +13 -0
- package/dist/crash-recovery-types.js.map +1 -0
- package/dist/crash-recovery.d.ts +88 -0
- package/dist/crash-recovery.d.ts.map +1 -0
- package/dist/crash-recovery.js +448 -0
- package/dist/crash-recovery.js.map +1 -0
- package/dist/daemon-logs.d.ts +19 -0
- package/dist/daemon-logs.d.ts.map +1 -0
- package/dist/daemon-logs.js +81 -0
- package/dist/daemon-logs.js.map +1 -0
- package/dist/daemon-process.d.ts +154 -0
- package/dist/daemon-process.d.ts.map +1 -0
- package/dist/daemon-process.js +427 -0
- package/dist/daemon-process.js.map +1 -0
- package/dist/dag-validator.d.ts +52 -0
- package/dist/dag-validator.d.ts.map +1 -0
- package/dist/dag-validator.js +199 -0
- package/dist/dag-validator.js.map +1 -0
- package/dist/delivery-guards.d.ts +41 -0
- package/dist/delivery-guards.d.ts.map +1 -0
- package/dist/delivery-guards.js +195 -0
- package/dist/delivery-guards.js.map +1 -0
- package/dist/delivery-lifecycle.d.ts +110 -0
- package/dist/delivery-lifecycle.d.ts.map +1 -0
- package/dist/delivery-lifecycle.js +353 -0
- package/dist/delivery-lifecycle.js.map +1 -0
- package/dist/delivery-merge.d.ts +17 -0
- package/dist/delivery-merge.d.ts.map +1 -0
- package/dist/delivery-merge.js +89 -0
- package/dist/delivery-merge.js.map +1 -0
- package/dist/dependency-resolver.d.ts +77 -0
- package/dist/dependency-resolver.d.ts.map +1 -0
- package/dist/dependency-resolver.js +337 -0
- package/dist/dependency-resolver.js.map +1 -0
- package/dist/evaluation-context.d.ts +49 -0
- package/dist/evaluation-context.d.ts.map +1 -0
- package/dist/evaluation-context.js +98 -0
- package/dist/evaluation-context.js.map +1 -0
- package/dist/git-activity.d.ts +24 -0
- package/dist/git-activity.d.ts.map +1 -0
- package/dist/git-activity.js +97 -0
- package/dist/git-activity.js.map +1 -0
- package/dist/git-branch.d.ts +33 -0
- package/dist/git-branch.d.ts.map +1 -0
- package/dist/git-branch.js +88 -0
- package/dist/git-branch.js.map +1 -0
- package/dist/git-integration.d.ts +27 -0
- package/dist/git-integration.d.ts.map +1 -0
- package/dist/git-integration.js +82 -0
- package/dist/git-integration.js.map +1 -0
- package/dist/git-merge-helpers.d.ts +48 -0
- package/dist/git-merge-helpers.d.ts.map +1 -0
- package/dist/git-merge-helpers.js +105 -0
- package/dist/git-merge-helpers.js.map +1 -0
- package/dist/git-merge-lock.d.ts +67 -0
- package/dist/git-merge-lock.d.ts.map +1 -0
- package/dist/git-merge-lock.js +157 -0
- package/dist/git-merge-lock.js.map +1 -0
- package/dist/git-merge-strategies.d.ts +39 -0
- package/dist/git-merge-strategies.d.ts.map +1 -0
- package/dist/git-merge-strategies.js +127 -0
- package/dist/git-merge-strategies.js.map +1 -0
- package/dist/git-merge.d.ts +80 -0
- package/dist/git-merge.d.ts.map +1 -0
- package/dist/git-merge.js +373 -0
- package/dist/git-merge.js.map +1 -0
- package/dist/git-state-detector.d.ts +24 -0
- package/dist/git-state-detector.d.ts.map +1 -0
- package/dist/git-state-detector.js +122 -0
- package/dist/git-state-detector.js.map +1 -0
- package/dist/git-types.d.ts +40 -0
- package/dist/git-types.d.ts.map +1 -0
- package/dist/git-types.js +23 -0
- package/dist/git-types.js.map +1 -0
- package/dist/git-utils.d.ts +28 -0
- package/dist/git-utils.d.ts.map +1 -0
- package/dist/git-utils.js +57 -0
- package/dist/git-utils.js.map +1 -0
- package/dist/git.d.ts +24 -0
- package/dist/git.d.ts.map +1 -0
- package/dist/git.js +64 -0
- package/dist/git.js.map +1 -0
- package/dist/guard-engine.d.ts +19 -0
- package/dist/guard-engine.d.ts.map +1 -0
- package/dist/guard-engine.js +21 -0
- package/dist/guard-engine.js.map +1 -0
- package/dist/guard-evaluator.d.ts +47 -0
- package/dist/guard-evaluator.d.ts.map +1 -0
- package/dist/guard-evaluator.js +193 -0
- package/dist/guard-evaluator.js.map +1 -0
- package/dist/heartbeat.d.ts +73 -0
- package/dist/heartbeat.d.ts.map +1 -0
- package/dist/heartbeat.js +306 -0
- package/dist/heartbeat.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +493 -0
- package/dist/index.js.map +1 -0
- package/dist/listener-auto-advance.d.ts +29 -0
- package/dist/listener-auto-advance.d.ts.map +1 -0
- package/dist/listener-auto-advance.js +172 -0
- package/dist/listener-auto-advance.js.map +1 -0
- package/dist/listener-review.d.ts +37 -0
- package/dist/listener-review.d.ts.map +1 -0
- package/dist/listener-review.js +217 -0
- package/dist/listener-review.js.map +1 -0
- package/dist/listener.d.ts +57 -0
- package/dist/listener.d.ts.map +1 -0
- package/dist/listener.js +361 -0
- package/dist/listener.js.map +1 -0
- package/dist/log-manager.d.ts +18 -0
- package/dist/log-manager.d.ts.map +1 -0
- package/dist/log-manager.js +18 -0
- package/dist/log-manager.js.map +1 -0
- package/dist/otlp-log-parser.d.ts +21 -0
- package/dist/otlp-log-parser.d.ts.map +1 -0
- package/dist/otlp-log-parser.js +143 -0
- package/dist/otlp-log-parser.js.map +1 -0
- package/dist/otlp-metric-parser.d.ts +20 -0
- package/dist/otlp-metric-parser.d.ts.map +1 -0
- package/dist/otlp-metric-parser.js +113 -0
- package/dist/otlp-metric-parser.js.map +1 -0
- package/dist/otlp-port-manager.d.ts +26 -0
- package/dist/otlp-port-manager.d.ts.map +1 -0
- package/dist/otlp-port-manager.js +130 -0
- package/dist/otlp-port-manager.js.map +1 -0
- package/dist/otlp-receiver.d.ts +51 -0
- package/dist/otlp-receiver.d.ts.map +1 -0
- package/dist/otlp-receiver.js +663 -0
- package/dist/otlp-receiver.js.map +1 -0
- package/dist/otlp-types.d.ts +92 -0
- package/dist/otlp-types.d.ts.map +1 -0
- package/dist/otlp-types.js +133 -0
- package/dist/otlp-types.js.map +1 -0
- package/dist/output-monitor.d.ts +33 -0
- package/dist/output-monitor.d.ts.map +1 -0
- package/dist/output-monitor.js +67 -0
- package/dist/output-monitor.js.map +1 -0
- package/dist/planning-prompt-builder.d.ts +67 -0
- package/dist/planning-prompt-builder.d.ts.map +1 -0
- package/dist/planning-prompt-builder.js +515 -0
- package/dist/planning-prompt-builder.js.map +1 -0
- package/dist/prompt-builder.d.ts +14 -0
- package/dist/prompt-builder.d.ts.map +1 -0
- package/dist/prompt-builder.js +174 -0
- package/dist/prompt-builder.js.map +1 -0
- package/dist/qa-crash-recovery.d.ts +77 -0
- package/dist/qa-crash-recovery.d.ts.map +1 -0
- package/dist/qa-crash-recovery.js +243 -0
- package/dist/qa-crash-recovery.js.map +1 -0
- package/dist/qa-dev-server.d.ts +73 -0
- package/dist/qa-dev-server.d.ts.map +1 -0
- package/dist/qa-dev-server.js +279 -0
- package/dist/qa-dev-server.js.map +1 -0
- package/dist/qa-orchestrator.d.ts +79 -0
- package/dist/qa-orchestrator.d.ts.map +1 -0
- package/dist/qa-orchestrator.js +349 -0
- package/dist/qa-orchestrator.js.map +1 -0
- package/dist/qa-port-allocator.d.ts +34 -0
- package/dist/qa-port-allocator.d.ts.map +1 -0
- package/dist/qa-port-allocator.js +75 -0
- package/dist/qa-port-allocator.js.map +1 -0
- package/dist/qa-provisioner.d.ts +33 -0
- package/dist/qa-provisioner.d.ts.map +1 -0
- package/dist/qa-provisioner.js +141 -0
- package/dist/qa-provisioner.js.map +1 -0
- package/dist/qa-state.d.ts +93 -0
- package/dist/qa-state.d.ts.map +1 -0
- package/dist/qa-state.js +74 -0
- package/dist/qa-state.js.map +1 -0
- package/dist/queries/control-state.d.ts +25 -0
- package/dist/queries/control-state.d.ts.map +1 -0
- package/dist/queries/control-state.js +34 -0
- package/dist/queries/control-state.js.map +1 -0
- package/dist/queries/daemon-connection.d.ts +25 -0
- package/dist/queries/daemon-connection.d.ts.map +1 -0
- package/dist/queries/daemon-connection.js +28 -0
- package/dist/queries/daemon-connection.js.map +1 -0
- package/dist/queries/deliveries.d.ts +100 -0
- package/dist/queries/deliveries.d.ts.map +1 -0
- package/dist/queries/deliveries.js +184 -0
- package/dist/queries/deliveries.js.map +1 -0
- package/dist/queries/git-activity.d.ts +20 -0
- package/dist/queries/git-activity.d.ts.map +1 -0
- package/dist/queries/git-activity.js +22 -0
- package/dist/queries/git-activity.js.map +1 -0
- package/dist/queries/guards.d.ts +47 -0
- package/dist/queries/guards.d.ts.map +1 -0
- package/dist/queries/guards.js +138 -0
- package/dist/queries/guards.js.map +1 -0
- package/dist/queries/index.d.ts +19 -0
- package/dist/queries/index.d.ts.map +1 -0
- package/dist/queries/index.js +17 -0
- package/dist/queries/index.js.map +1 -0
- package/dist/queries/issues.d.ts +41 -0
- package/dist/queries/issues.d.ts.map +1 -0
- package/dist/queries/issues.js +67 -0
- package/dist/queries/issues.js.map +1 -0
- package/dist/queries/qa.d.ts +79 -0
- package/dist/queries/qa.d.ts.map +1 -0
- package/dist/queries/qa.js +85 -0
- package/dist/queries/qa.js.map +1 -0
- package/dist/queries/roles.d.ts +13 -0
- package/dist/queries/roles.d.ts.map +1 -0
- package/dist/queries/roles.js +39 -0
- package/dist/queries/roles.js.map +1 -0
- package/dist/queries/schemas.d.ts +777 -0
- package/dist/queries/schemas.d.ts.map +1 -0
- package/dist/queries/schemas.js +391 -0
- package/dist/queries/schemas.js.map +1 -0
- package/dist/queries/sessions.d.ts +64 -0
- package/dist/queries/sessions.d.ts.map +1 -0
- package/dist/queries/sessions.js +100 -0
- package/dist/queries/sessions.js.map +1 -0
- package/dist/queries/shared.d.ts +61 -0
- package/dist/queries/shared.d.ts.map +1 -0
- package/dist/queries/shared.js +187 -0
- package/dist/queries/shared.js.map +1 -0
- package/dist/queries/strategies.d.ts +69 -0
- package/dist/queries/strategies.d.ts.map +1 -0
- package/dist/queries/strategies.js +80 -0
- package/dist/queries/strategies.js.map +1 -0
- package/dist/queries/workflows.d.ts +17 -0
- package/dist/queries/workflows.d.ts.map +1 -0
- package/dist/queries/workflows.js +49 -0
- package/dist/queries/workflows.js.map +1 -0
- package/dist/queries/worktrees.d.ts +38 -0
- package/dist/queries/worktrees.d.ts.map +1 -0
- package/dist/queries/worktrees.js +37 -0
- package/dist/queries/worktrees.js.map +1 -0
- package/dist/self-update.d.ts +94 -0
- package/dist/self-update.d.ts.map +1 -0
- package/dist/self-update.js +438 -0
- package/dist/self-update.js.map +1 -0
- package/dist/session-lifecycle.d.ts +77 -0
- package/dist/session-lifecycle.d.ts.map +1 -0
- package/dist/session-lifecycle.js +379 -0
- package/dist/session-lifecycle.js.map +1 -0
- package/dist/shutdown-state.d.ts +17 -0
- package/dist/shutdown-state.d.ts.map +1 -0
- package/dist/shutdown-state.js +22 -0
- package/dist/shutdown-state.js.map +1 -0
- package/dist/spawn-cooldown.d.ts +14 -0
- package/dist/spawn-cooldown.d.ts.map +1 -0
- package/dist/spawn-cooldown.js +34 -0
- package/dist/spawn-cooldown.js.map +1 -0
- package/dist/spawn-environment.d.ts +35 -0
- package/dist/spawn-environment.d.ts.map +1 -0
- package/dist/spawn-environment.js +48 -0
- package/dist/spawn-environment.js.map +1 -0
- package/dist/spawner-liveness.d.ts +23 -0
- package/dist/spawner-liveness.d.ts.map +1 -0
- package/dist/spawner-liveness.js +99 -0
- package/dist/spawner-liveness.js.map +1 -0
- package/dist/spawner-resolution.d.ts +27 -0
- package/dist/spawner-resolution.d.ts.map +1 -0
- package/dist/spawner-resolution.js +99 -0
- package/dist/spawner-resolution.js.map +1 -0
- package/dist/spawner-timeout.d.ts +32 -0
- package/dist/spawner-timeout.d.ts.map +1 -0
- package/dist/spawner-timeout.js +124 -0
- package/dist/spawner-timeout.js.map +1 -0
- package/dist/spawner.d.ts +77 -0
- package/dist/spawner.d.ts.map +1 -0
- package/dist/spawner.js +734 -0
- package/dist/spawner.js.map +1 -0
- package/dist/strategy-completion.d.ts +110 -0
- package/dist/strategy-completion.d.ts.map +1 -0
- package/dist/strategy-completion.js +434 -0
- package/dist/strategy-completion.js.map +1 -0
- package/dist/strategy-engine.d.ts +47 -0
- package/dist/strategy-engine.d.ts.map +1 -0
- package/dist/strategy-engine.js +419 -0
- package/dist/strategy-engine.js.map +1 -0
- package/dist/strategy-executor.d.ts +93 -0
- package/dist/strategy-executor.d.ts.map +1 -0
- package/dist/strategy-executor.js +775 -0
- package/dist/strategy-executor.js.map +1 -0
- package/dist/strategy-lifecycle.d.ts +61 -0
- package/dist/strategy-lifecycle.d.ts.map +1 -0
- package/dist/strategy-lifecycle.js +516 -0
- package/dist/strategy-lifecycle.js.map +1 -0
- package/dist/strategy-merge.d.ts +72 -0
- package/dist/strategy-merge.d.ts.map +1 -0
- package/dist/strategy-merge.js +371 -0
- package/dist/strategy-merge.js.map +1 -0
- package/dist/strategy-prompt-builder.d.ts +62 -0
- package/dist/strategy-prompt-builder.d.ts.map +1 -0
- package/dist/strategy-prompt-builder.js +538 -0
- package/dist/strategy-prompt-builder.js.map +1 -0
- package/dist/strategy-provisioning.d.ts +16 -0
- package/dist/strategy-provisioning.d.ts.map +1 -0
- package/dist/strategy-provisioning.js +119 -0
- package/dist/strategy-provisioning.js.map +1 -0
- package/dist/strategy-team-state.d.ts +24 -0
- package/dist/strategy-team-state.d.ts.map +1 -0
- package/dist/strategy-team-state.js +43 -0
- package/dist/strategy-team-state.js.map +1 -0
- package/dist/strategy-teardown.d.ts +24 -0
- package/dist/strategy-teardown.d.ts.map +1 -0
- package/dist/strategy-teardown.js +158 -0
- package/dist/strategy-teardown.js.map +1 -0
- package/dist/strategy-worktree-state.d.ts +47 -0
- package/dist/strategy-worktree-state.d.ts.map +1 -0
- package/dist/strategy-worktree-state.js +104 -0
- package/dist/strategy-worktree-state.js.map +1 -0
- package/dist/supabase.d.ts +36 -0
- package/dist/supabase.d.ts.map +1 -0
- package/dist/supabase.js +50 -0
- package/dist/supabase.js.map +1 -0
- package/dist/task-converter.d.ts +61 -0
- package/dist/task-converter.d.ts.map +1 -0
- package/dist/task-converter.js +286 -0
- package/dist/task-converter.js.map +1 -0
- package/dist/task-dag-builder.d.ts +14 -0
- package/dist/task-dag-builder.d.ts.map +1 -0
- package/dist/task-dag-builder.js +17 -0
- package/dist/task-dag-builder.js.map +1 -0
- package/dist/team-prompt-base.d.ts +114 -0
- package/dist/team-prompt-base.d.ts.map +1 -0
- package/dist/team-prompt-base.js +531 -0
- package/dist/team-prompt-base.js.map +1 -0
- package/dist/team-prompt-variants.d.ts +27 -0
- package/dist/team-prompt-variants.d.ts.map +1 -0
- package/dist/team-prompt-variants.js +134 -0
- package/dist/team-prompt-variants.js.map +1 -0
- package/dist/team-spawner.d.ts +50 -0
- package/dist/team-spawner.d.ts.map +1 -0
- package/dist/team-spawner.js +410 -0
- package/dist/team-spawner.js.map +1 -0
- package/dist/telemetry-writer.d.ts +66 -0
- package/dist/telemetry-writer.d.ts.map +1 -0
- package/dist/telemetry-writer.js +96 -0
- package/dist/telemetry-writer.js.map +1 -0
- package/dist/trigger-executor.d.ts +56 -0
- package/dist/trigger-executor.d.ts.map +1 -0
- package/dist/trigger-executor.js +313 -0
- package/dist/trigger-executor.js.map +1 -0
- package/dist/types/config.d.ts +60 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +5 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/dag.d.ts +53 -0
- package/dist/types/dag.d.ts.map +1 -0
- package/dist/types/dag.js +5 -0
- package/dist/types/dag.js.map +1 -0
- package/dist/types/delivery.d.ts +71 -0
- package/dist/types/delivery.d.ts.map +1 -0
- package/dist/types/delivery.js +5 -0
- package/dist/types/delivery.js.map +1 -0
- package/dist/types/index.d.ts +15 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +15 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/issue.d.ts +22 -0
- package/dist/types/issue.d.ts.map +1 -0
- package/dist/types/issue.js +5 -0
- package/dist/types/issue.js.map +1 -0
- package/dist/types/merge.d.ts +28 -0
- package/dist/types/merge.d.ts.map +1 -0
- package/dist/types/merge.js +5 -0
- package/dist/types/merge.js.map +1 -0
- package/dist/types/session.d.ts +98 -0
- package/dist/types/session.d.ts.map +1 -0
- package/dist/types/session.js +5 -0
- package/dist/types/session.js.map +1 -0
- package/dist/types/strategy.d.ts +175 -0
- package/dist/types/strategy.d.ts.map +1 -0
- package/dist/types/strategy.js +5 -0
- package/dist/types/strategy.js.map +1 -0
- package/dist/types/workflow.d.ts +34 -0
- package/dist/types/workflow.d.ts.map +1 -0
- package/dist/types/workflow.js +9 -0
- package/dist/types/workflow.js.map +1 -0
- package/dist/types.d.ts +9 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/dist/unified-init.d.ts +16 -0
- package/dist/unified-init.d.ts.map +1 -0
- package/dist/unified-init.js +183 -0
- package/dist/unified-init.js.map +1 -0
- package/dist/unified-shell-config.d.ts +34 -0
- package/dist/unified-shell-config.d.ts.map +1 -0
- package/dist/unified-shell-config.js +238 -0
- package/dist/unified-shell-config.js.map +1 -0
- package/dist/unified-shell-status.d.ts +15 -0
- package/dist/unified-shell-status.d.ts.map +1 -0
- package/dist/unified-shell-status.js +100 -0
- package/dist/unified-shell-status.js.map +1 -0
- package/dist/unified-shell.d.ts +50 -0
- package/dist/unified-shell.d.ts.map +1 -0
- package/dist/unified-shell.js +682 -0
- package/dist/unified-shell.js.map +1 -0
- package/dist/version-check.d.ts +19 -0
- package/dist/version-check.d.ts.map +1 -0
- package/dist/version-check.js +67 -0
- package/dist/version-check.js.map +1 -0
- package/dist/workflow-engine.d.ts +95 -0
- package/dist/workflow-engine.d.ts.map +1 -0
- package/dist/workflow-engine.js +165 -0
- package/dist/workflow-engine.js.map +1 -0
- package/dist/worktree-merge.d.ts +23 -0
- package/dist/worktree-merge.d.ts.map +1 -0
- package/dist/worktree-merge.js +57 -0
- package/dist/worktree-merge.js.map +1 -0
- package/dist/worktree-safety.d.ts +48 -0
- package/dist/worktree-safety.d.ts.map +1 -0
- package/dist/worktree-safety.js +113 -0
- package/dist/worktree-safety.js.map +1 -0
- package/dist/worktree-strategy.d.ts +69 -0
- package/dist/worktree-strategy.d.ts.map +1 -0
- package/dist/worktree-strategy.js +214 -0
- package/dist/worktree-strategy.js.map +1 -0
- package/dist/worktree.d.ts +159 -0
- package/dist/worktree.d.ts.map +1 -0
- package/dist/worktree.js +512 -0
- package/dist/worktree.js.map +1 -0
- package/package.json +76 -0
- package/scripts/telora-daemon-wrapper.sh +31 -0
|
@@ -0,0 +1,682 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified daemon process shell -- the single entry point for telora-daemon.
|
|
3
|
+
*
|
|
4
|
+
* Composes daemon-core's lifecycle primitives (PID locking, signal handling,
|
|
5
|
+
* config loading) with one or more ExecutionEngine adapters (StrategyEngine,
|
|
6
|
+
* FactoryEngine). The CLI in index.ts creates engine instances based on the
|
|
7
|
+
* unified config and passes them here.
|
|
8
|
+
*
|
|
9
|
+
* Lifecycle sequence:
|
|
10
|
+
* 1. Load unified config via loadUnifiedConfig()
|
|
11
|
+
* 2. Apply engine filter (--engine flag) if provided
|
|
12
|
+
* 3. Acquire PID lock at .telora/daemon.pid
|
|
13
|
+
* 4. Register uncaughtException / unhandledRejection handlers
|
|
14
|
+
* 5. Set up signal handlers (SIGTERM, SIGINT)
|
|
15
|
+
* 6. For each enabled engine: build config, create instance, init, recover, start
|
|
16
|
+
* 7. Register SIGUSR1 (status) and SIGUSR2 (circuit breaker reset) handlers
|
|
17
|
+
* 8. Keep the process running indefinitely
|
|
18
|
+
*
|
|
19
|
+
* On shutdown (SIGTERM/SIGINT):
|
|
20
|
+
* 1. Stop all engines (stop accepting new work)
|
|
21
|
+
* 2. Shut down all engines in parallel (graceful cleanup)
|
|
22
|
+
* 3. Release PID lock
|
|
23
|
+
* 4. Exit
|
|
24
|
+
*/
|
|
25
|
+
import { readFileSync, openSync, closeSync } from 'node:fs';
|
|
26
|
+
import { dirname, join, resolve } from 'node:path';
|
|
27
|
+
import { fileURLToPath } from 'node:url';
|
|
28
|
+
import { networkInterfaces } from 'node:os';
|
|
29
|
+
import { spawn } from 'node:child_process';
|
|
30
|
+
import { loadUnifiedConfig, buildEngineConfig, acquirePidLock, releasePidLock, setupSignalHandlers, PidLockError, forceCloseAllCircuitBreakers, ResourceGovernor, } from '@telora/daemon-core';
|
|
31
|
+
import { StrategyEngine } from './strategy-engine.js';
|
|
32
|
+
import { startAutoUpdateLoop } from './auto-update.js';
|
|
33
|
+
import { setDaemonShuttingDown } from './shutdown-state.js';
|
|
34
|
+
import { rotateDaemonLog, setupCanonicalLogTee, writeDaemonMeta, removeDaemonMeta, resolveDaemonLogPath, resolvePidFilePath, ensureGlobalStateDir } from './daemon-process.js';
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
// LAN host detection (mirrored from daemon's config.ts to avoid circular deps)
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
/**
|
|
39
|
+
* Auto-detect the first non-internal IPv4 address for LAN-accessible URLs.
|
|
40
|
+
* Falls back to 'localhost' if no suitable address is found.
|
|
41
|
+
*/
|
|
42
|
+
function detectLanHost() {
|
|
43
|
+
const interfaces = networkInterfaces();
|
|
44
|
+
for (const ifaceEntries of Object.values(interfaces)) {
|
|
45
|
+
if (!ifaceEntries)
|
|
46
|
+
continue;
|
|
47
|
+
for (const iface of ifaceEntries) {
|
|
48
|
+
if (iface.family === 'IPv4' && !iface.internal) {
|
|
49
|
+
return iface.address;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return 'localhost';
|
|
54
|
+
}
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
// Numeric env-var helpers (mirrored from daemon/config.ts)
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
function resolveInt(envVal, fileVal, defaultVal) {
|
|
59
|
+
if (envVal !== undefined && envVal !== '') {
|
|
60
|
+
return parseInt(envVal, 10);
|
|
61
|
+
}
|
|
62
|
+
if (typeof fileVal === 'number') {
|
|
63
|
+
return fileVal;
|
|
64
|
+
}
|
|
65
|
+
return defaultVal;
|
|
66
|
+
}
|
|
67
|
+
function resolveFloat(envVal, fileVal, defaultVal) {
|
|
68
|
+
if (envVal !== undefined && envVal !== '') {
|
|
69
|
+
return parseFloat(envVal);
|
|
70
|
+
}
|
|
71
|
+
if (typeof fileVal === 'number') {
|
|
72
|
+
return fileVal;
|
|
73
|
+
}
|
|
74
|
+
return defaultVal;
|
|
75
|
+
}
|
|
76
|
+
// ---------------------------------------------------------------------------
|
|
77
|
+
// Engine config builders
|
|
78
|
+
// ---------------------------------------------------------------------------
|
|
79
|
+
/**
|
|
80
|
+
* Build a complete strategy engine config (DaemonConfig) from the unified
|
|
81
|
+
* base config and the strategy engine section. Applies the same defaults
|
|
82
|
+
* and env-var overrides as daemon/src/config.ts loadConfig().
|
|
83
|
+
*/
|
|
84
|
+
function buildStrategyConfig(base, section, env = process.env) {
|
|
85
|
+
// Start with the flat merge from daemon-core
|
|
86
|
+
const flat = buildEngineConfig(base, section);
|
|
87
|
+
const fields = section.fields;
|
|
88
|
+
const repoPath = flat.repoPath || process.cwd();
|
|
89
|
+
const claudeCodePath = flat.claudeCodePath || 'claude';
|
|
90
|
+
const logDir = flat.logDir || resolve(repoPath, '.telora', 'logs');
|
|
91
|
+
const sessionTimeoutMs = flat.sessionTimeoutMs ?? 3600000;
|
|
92
|
+
const mcpConfigPath = flat.mcpConfigPath ?? null;
|
|
93
|
+
// Strategy-specific fields with env overrides
|
|
94
|
+
const worktreeDir = env.TELORA_WORKTREE_DIR
|
|
95
|
+
|| (typeof fields.worktreeDir === 'string' ? fields.worktreeDir : '')
|
|
96
|
+
|| resolve(repoPath, '.telora', 'worktrees');
|
|
97
|
+
const integrationBranch = env.TELORA_INTEGRATION_BRANCH
|
|
98
|
+
|| (typeof fields.integrationBranch === 'string' ? fields.integrationBranch : '')
|
|
99
|
+
|| 'integration';
|
|
100
|
+
const maxTotalSessions = resolveInt(env.MAX_TOTAL_SESSIONS, fields.maxTotalSessions, 5);
|
|
101
|
+
const tokenLimit = resolveInt(env.TOKEN_LIMIT, fields.tokenLimit, 200000);
|
|
102
|
+
const costLimit = resolveFloat(env.COST_LIMIT, fields.costLimit, 10.0);
|
|
103
|
+
const mergeLockTimeoutMs = resolveInt(env.MERGE_LOCK_TIMEOUT_MS, fields.mergeLockTimeoutMs, 300000);
|
|
104
|
+
const mergeLockContentionWarningMs = resolveInt(env.MERGE_LOCK_CONTENTION_WARNING_MS, fields.mergeLockContentionWarningMs, 30000);
|
|
105
|
+
// Guard failure mode
|
|
106
|
+
const rawPolicyFailureMode = env.TELORA_POLICY_FAILURE_MODE
|
|
107
|
+
|| (typeof fields.policyFailureMode === 'string' ? fields.policyFailureMode : '')
|
|
108
|
+
|| 'fail-open';
|
|
109
|
+
if (rawPolicyFailureMode !== 'fail-open' && rawPolicyFailureMode !== 'fail-closed') {
|
|
110
|
+
throw new Error(`Invalid TELORA_POLICY_FAILURE_MODE: "${rawPolicyFailureMode}". Must be "fail-open" or "fail-closed".`);
|
|
111
|
+
}
|
|
112
|
+
// Log retention
|
|
113
|
+
const logMaxAgeDays = resolveInt(env.LOG_MAX_AGE_DAYS, fields.logMaxAgeDays, 7);
|
|
114
|
+
const logMaxTotalBytes = resolveInt(env.LOG_MAX_TOTAL_BYTES, fields.logMaxTotalBytes, 1073741824);
|
|
115
|
+
const logMaxFiles = resolveInt(env.LOG_MAX_FILES, fields.logMaxFiles, 500);
|
|
116
|
+
// Telemetry
|
|
117
|
+
const telemetryFile = (typeof fields.telemetry === 'object' && fields.telemetry !== null)
|
|
118
|
+
? fields.telemetry
|
|
119
|
+
: {};
|
|
120
|
+
const telemetry = {
|
|
121
|
+
enabled: env.TELORA_TELEMETRY_ENABLED !== undefined
|
|
122
|
+
? env.TELORA_TELEMETRY_ENABLED !== '0' && env.TELORA_TELEMETRY_ENABLED !== 'false'
|
|
123
|
+
: (typeof telemetryFile.enabled === 'boolean' ? telemetryFile.enabled : true),
|
|
124
|
+
port: resolveInt(env.TELORA_TELEMETRY_PORT, telemetryFile.port, 4318),
|
|
125
|
+
flushIntervalMs: resolveInt(env.TELORA_TELEMETRY_FLUSH_INTERVAL_MS, telemetryFile.flushIntervalMs, 5000),
|
|
126
|
+
retentionDays: resolveInt(env.TELORA_TELEMETRY_RETENTION_DAYS, telemetryFile.retentionDays, 30),
|
|
127
|
+
};
|
|
128
|
+
// QA environment
|
|
129
|
+
const qaPortRangeStart = resolveInt(env.TELORA_QA_PORT_RANGE_START, fields.qaPortRangeStart, 9100);
|
|
130
|
+
const qaPortRangeEnd = resolveInt(env.TELORA_QA_PORT_RANGE_END, fields.qaPortRangeEnd, 9199);
|
|
131
|
+
const qaHost = env.TELORA_QA_HOST
|
|
132
|
+
|| (typeof fields.qaHost === 'string' ? fields.qaHost : '')
|
|
133
|
+
|| detectLanHost();
|
|
134
|
+
// Local Supabase
|
|
135
|
+
const localSupabaseUrl = env.TELORA_LOCAL_SUPABASE_URL
|
|
136
|
+
|| (typeof fields.localSupabaseUrl === 'string' ? fields.localSupabaseUrl : '')
|
|
137
|
+
|| 'http://127.0.0.1:54321';
|
|
138
|
+
const localSupabaseAnonKey = env.TELORA_LOCAL_SUPABASE_ANON_KEY
|
|
139
|
+
|| (typeof fields.localSupabaseAnonKey === 'string' ? fields.localSupabaseAnonKey : '')
|
|
140
|
+
|| null;
|
|
141
|
+
// Products array from base config (already resolved by loadUnifiedConfig)
|
|
142
|
+
const products = flat.products ?? [];
|
|
143
|
+
return {
|
|
144
|
+
// Base fields
|
|
145
|
+
teloraUrl: flat.teloraUrl,
|
|
146
|
+
trackerId: flat.trackerId,
|
|
147
|
+
organizationId: flat.organizationId,
|
|
148
|
+
productId: flat.productId,
|
|
149
|
+
repoPath,
|
|
150
|
+
products,
|
|
151
|
+
claudeCodePath,
|
|
152
|
+
logDir,
|
|
153
|
+
sessionTimeoutMs,
|
|
154
|
+
mcpConfigPath,
|
|
155
|
+
// Strategy-specific fields
|
|
156
|
+
worktreeDir,
|
|
157
|
+
integrationBranch,
|
|
158
|
+
maxTotalSessions,
|
|
159
|
+
tokenLimit,
|
|
160
|
+
costLimit,
|
|
161
|
+
mergeLockTimeoutMs,
|
|
162
|
+
mergeLockContentionWarningMs,
|
|
163
|
+
policyFailureMode: rawPolicyFailureMode,
|
|
164
|
+
logMaxAgeDays,
|
|
165
|
+
logMaxTotalBytes,
|
|
166
|
+
logMaxFiles,
|
|
167
|
+
telemetry,
|
|
168
|
+
qaPortRangeStart,
|
|
169
|
+
qaPortRangeEnd,
|
|
170
|
+
qaHost,
|
|
171
|
+
localSupabaseUrl,
|
|
172
|
+
localSupabaseAnonKey,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Build a complete factory engine config (FactoryConfig) from the unified
|
|
177
|
+
* base config and the factory engine section. Applies the same defaults
|
|
178
|
+
* and env-var overrides as factory/src/config.ts loadConfig().
|
|
179
|
+
*/
|
|
180
|
+
function buildFactoryConfig(base, section, env = process.env) {
|
|
181
|
+
const flat = buildEngineConfig(base, section);
|
|
182
|
+
const fields = section.fields;
|
|
183
|
+
const repoPath = flat.repoPath || process.cwd();
|
|
184
|
+
const claudeCodePath = flat.claudeCodePath || 'claude';
|
|
185
|
+
// Factory uses a separate log directory by default
|
|
186
|
+
const logDir = flat.logDir || resolve(repoPath, '.telora', 'factory-logs');
|
|
187
|
+
const sessionTimeoutMs = flat.sessionTimeoutMs ?? 3600000;
|
|
188
|
+
const mcpConfigPath = flat.mcpConfigPath ?? null;
|
|
189
|
+
// Factory-specific fields with env overrides
|
|
190
|
+
const factoryWorktreeDir = env.TELORA_FACTORY_WORKTREE_DIR
|
|
191
|
+
|| (typeof fields.factoryWorktreeDir === 'string' ? fields.factoryWorktreeDir : '')
|
|
192
|
+
|| resolve(repoPath, '.telora', 'factory-worktrees');
|
|
193
|
+
const maxConcurrentInstances = env.FACTORY_MAX_CONCURRENT_INSTANCES
|
|
194
|
+
? parseInt(env.FACTORY_MAX_CONCURRENT_INSTANCES, 10)
|
|
195
|
+
: (typeof fields.maxConcurrentInstances === 'number' ? fields.maxConcurrentInstances : 3);
|
|
196
|
+
// Telemetry: inherit from flat config (resolved by buildEngineConfig) or default
|
|
197
|
+
const telemetry = (flat.telemetry && typeof flat.telemetry === 'object')
|
|
198
|
+
? flat.telemetry
|
|
199
|
+
: { enabled: true, port: 4318 };
|
|
200
|
+
// Products array from base config (already resolved by loadUnifiedConfig)
|
|
201
|
+
const products = flat.products ?? [];
|
|
202
|
+
return {
|
|
203
|
+
// Base fields
|
|
204
|
+
teloraUrl: flat.teloraUrl,
|
|
205
|
+
trackerId: flat.trackerId,
|
|
206
|
+
organizationId: flat.organizationId,
|
|
207
|
+
productId: flat.productId,
|
|
208
|
+
repoPath,
|
|
209
|
+
products,
|
|
210
|
+
claudeCodePath,
|
|
211
|
+
logDir,
|
|
212
|
+
sessionTimeoutMs,
|
|
213
|
+
mcpConfigPath,
|
|
214
|
+
// Factory-specific fields
|
|
215
|
+
factoryWorktreeDir,
|
|
216
|
+
maxConcurrentInstances,
|
|
217
|
+
telemetry,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
// ---------------------------------------------------------------------------
|
|
221
|
+
// Status reporting
|
|
222
|
+
// ---------------------------------------------------------------------------
|
|
223
|
+
/**
|
|
224
|
+
* Print aggregated status from all active engines.
|
|
225
|
+
*/
|
|
226
|
+
function printAggregatedStatus(engines, governor) {
|
|
227
|
+
console.log('\n=== Unified Daemon Status ===');
|
|
228
|
+
console.log(`Engines: ${engines.map(e => e.name).join(', ')}`);
|
|
229
|
+
console.log(`PID: ${process.pid}`);
|
|
230
|
+
console.log(`Uptime: ${Math.round(process.uptime())}s`);
|
|
231
|
+
// Resource governor utilization
|
|
232
|
+
if (governor) {
|
|
233
|
+
const util = governor.getUtilization();
|
|
234
|
+
console.log(`\n--- Resource Governor ---`);
|
|
235
|
+
console.log(` Global slots: ${util.total}/${util.max} in use`);
|
|
236
|
+
for (const [engine, count] of Object.entries(util.perEngine)) {
|
|
237
|
+
console.log(` ${engine}: ${count} slots`);
|
|
238
|
+
}
|
|
239
|
+
if (util.queueDepth > 0) {
|
|
240
|
+
console.log(` Queue depth: ${util.queueDepth}`);
|
|
241
|
+
for (const [engine, depth] of Object.entries(util.queueDepthPerEngine)) {
|
|
242
|
+
console.log(` ${engine}: ${depth} waiting`);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
for (const engine of engines) {
|
|
247
|
+
const health = engine.healthCheck();
|
|
248
|
+
const resources = engine.getResourceUsage();
|
|
249
|
+
console.log(`\n--- ${engine.name} engine ---`);
|
|
250
|
+
console.log(` Status: ${health.status}`);
|
|
251
|
+
console.log(` Active work items: ${health.activeWorkItems}`);
|
|
252
|
+
console.log(` Claude processes: ${resources.activeClaudeProcesses}`);
|
|
253
|
+
console.log(` Active worktrees: ${resources.activeWorktrees}`);
|
|
254
|
+
// Print engine-specific details
|
|
255
|
+
const details = health.details;
|
|
256
|
+
for (const [key, value] of Object.entries(details)) {
|
|
257
|
+
if (Array.isArray(value)) {
|
|
258
|
+
if (value.length > 0) {
|
|
259
|
+
console.log(` ${key}:`);
|
|
260
|
+
for (const item of value) {
|
|
261
|
+
if (typeof item === 'object' && item !== null) {
|
|
262
|
+
const parts = Object.entries(item)
|
|
263
|
+
.map(([k, v]) => `${k}: ${String(v)}`)
|
|
264
|
+
.join(' | ');
|
|
265
|
+
console.log(` - ${parts}`);
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
console.log(` - ${String(item)}`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
else if (typeof value === 'object' && value !== null) {
|
|
274
|
+
const entries = Object.entries(value);
|
|
275
|
+
if (entries.length > 0) {
|
|
276
|
+
console.log(` ${key}:`);
|
|
277
|
+
for (const [k, v] of entries) {
|
|
278
|
+
console.log(` ${k}: ${String(v)}`);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
283
|
+
console.log(` ${key}: ${String(value)}`);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
console.log('');
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Print a dry-run summary of the unified config.
|
|
291
|
+
*/
|
|
292
|
+
function printUnifiedConfigSummary(config) {
|
|
293
|
+
console.log('Unified Daemon Configuration:');
|
|
294
|
+
console.log(` Telora URL: ${config.base.teloraUrl ?? '[not set]'}`);
|
|
295
|
+
console.log(` Tracker ID: ${config.base.trackerId ? '[configured]' : '[not set]'}`);
|
|
296
|
+
console.log(` Organization ID: ${config.base.organizationId ?? '[not set]'}`);
|
|
297
|
+
const products = config.base.products ?? [];
|
|
298
|
+
if (products.length > 0) {
|
|
299
|
+
console.log(` Products: ${products.length}`);
|
|
300
|
+
for (const p of products) {
|
|
301
|
+
console.log(` - ${p.id} -> ${p.repoPath}`);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
else {
|
|
305
|
+
console.log(` Product ID: ${config.base.productId ?? '[not set]'}`);
|
|
306
|
+
console.log(` Repository Path: ${config.base.repoPath ?? '[cwd]'}`);
|
|
307
|
+
}
|
|
308
|
+
console.log(` Claude Code Path: ${config.base.claudeCodePath ?? 'claude'}`);
|
|
309
|
+
console.log(` Log Directory: ${config.base.logDir ?? '[default]'}`);
|
|
310
|
+
console.log(` Session Timeout: ${config.base.sessionTimeoutMs ?? 3600000}ms`);
|
|
311
|
+
console.log(` MCP Config Path: ${config.base.mcpConfigPath ?? '[not set]'}`);
|
|
312
|
+
console.log(`\n Strategy engine: ${config.strategy.enabled ? 'enabled' : 'disabled'}`);
|
|
313
|
+
if (config.strategy.enabled) {
|
|
314
|
+
const sf = config.strategy.fields;
|
|
315
|
+
console.log(` worktreeDir: ${String(sf.worktreeDir ?? '[default]')}`);
|
|
316
|
+
console.log(` integrationBranch: ${String(sf.integrationBranch ?? 'integration')}`);
|
|
317
|
+
console.log(` maxTotalSessions: ${String(sf.maxTotalSessions ?? 5)}`);
|
|
318
|
+
}
|
|
319
|
+
console.log(`\n Factory engine: ${config.factory.enabled ? 'enabled' : 'disabled'}`);
|
|
320
|
+
if (config.factory.enabled) {
|
|
321
|
+
const ff = config.factory.fields;
|
|
322
|
+
console.log(` factoryWorktreeDir: ${String(ff.factoryWorktreeDir ?? '[default]')}`);
|
|
323
|
+
console.log(` maxConcurrentInstances: ${String(ff.maxConcurrentInstances ?? 3)}`);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
// ---------------------------------------------------------------------------
|
|
327
|
+
// Auto-update config resolution
|
|
328
|
+
// ---------------------------------------------------------------------------
|
|
329
|
+
const DEFAULT_AUTO_UPDATE_INTERVAL_MS = 3_600_000; // 1 hour
|
|
330
|
+
/**
|
|
331
|
+
* Resolve whether auto-update is enabled from env vars and config file.
|
|
332
|
+
* Env var TELORA_AUTO_UPDATE takes precedence over config file autoUpdate.enabled.
|
|
333
|
+
* Default: true.
|
|
334
|
+
*/
|
|
335
|
+
function resolveAutoUpdateEnabled(config) {
|
|
336
|
+
const envVal = process.env.TELORA_AUTO_UPDATE;
|
|
337
|
+
if (envVal !== undefined && envVal !== '') {
|
|
338
|
+
return envVal !== '0' && envVal !== 'false';
|
|
339
|
+
}
|
|
340
|
+
const fileConfig = config.rawFileConfig;
|
|
341
|
+
if (typeof fileConfig.autoUpdate === 'object' && fileConfig.autoUpdate !== null) {
|
|
342
|
+
const au = fileConfig.autoUpdate;
|
|
343
|
+
if (typeof au.enabled === 'boolean')
|
|
344
|
+
return au.enabled;
|
|
345
|
+
}
|
|
346
|
+
return true;
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Resolve auto-update check interval from env vars and config file.
|
|
350
|
+
* Env var TELORA_AUTO_UPDATE_INTERVAL_MS takes precedence.
|
|
351
|
+
* Default: 3600000 (1 hour).
|
|
352
|
+
*/
|
|
353
|
+
function resolveAutoUpdateIntervalMs(config) {
|
|
354
|
+
const envVal = process.env.TELORA_AUTO_UPDATE_INTERVAL_MS;
|
|
355
|
+
if (envVal !== undefined && envVal !== '') {
|
|
356
|
+
const parsed = parseInt(envVal, 10);
|
|
357
|
+
if (!isNaN(parsed) && parsed > 0)
|
|
358
|
+
return parsed;
|
|
359
|
+
}
|
|
360
|
+
const fileConfig = config.rawFileConfig;
|
|
361
|
+
if (typeof fileConfig.autoUpdate === 'object' && fileConfig.autoUpdate !== null) {
|
|
362
|
+
const au = fileConfig.autoUpdate;
|
|
363
|
+
if (typeof au.intervalMs === 'number' && au.intervalMs > 0)
|
|
364
|
+
return au.intervalMs;
|
|
365
|
+
}
|
|
366
|
+
return DEFAULT_AUTO_UPDATE_INTERVAL_MS;
|
|
367
|
+
}
|
|
368
|
+
// ---------------------------------------------------------------------------
|
|
369
|
+
// Main entry point
|
|
370
|
+
// ---------------------------------------------------------------------------
|
|
371
|
+
/**
|
|
372
|
+
* Run the unified daemon process shell.
|
|
373
|
+
*
|
|
374
|
+
* This function loads configuration, acquires a PID lock, creates and
|
|
375
|
+
* initializes execution engines, runs crash recovery, starts the engines,
|
|
376
|
+
* and keeps the process running until a shutdown signal is received.
|
|
377
|
+
*
|
|
378
|
+
* @param opts CLI options (verbose, config path, dry-run, skip-recovery, engine filter).
|
|
379
|
+
*/
|
|
380
|
+
export async function runUnifiedDaemon(opts) {
|
|
381
|
+
// ── 1. Load unified config ───────────────────────────────────────────────
|
|
382
|
+
const config = loadUnifiedConfig(opts.config);
|
|
383
|
+
// ── 2. Apply engine filter ───────────────────────────────────────────────
|
|
384
|
+
if (opts.engineFilter) {
|
|
385
|
+
if (opts.engineFilter === 'strategy') {
|
|
386
|
+
config.factory = { enabled: false, fields: {} };
|
|
387
|
+
}
|
|
388
|
+
else if (opts.engineFilter === 'factory') {
|
|
389
|
+
config.strategy = { enabled: false, fields: {} };
|
|
390
|
+
}
|
|
391
|
+
console.log(`Engine filter active: only '${opts.engineFilter}' engine enabled`);
|
|
392
|
+
}
|
|
393
|
+
// ── 3. Dry run / verbose ─────────────────────────────────────────────────
|
|
394
|
+
if (opts.verbose || opts.dryRun) {
|
|
395
|
+
printUnifiedConfigSummary(config);
|
|
396
|
+
}
|
|
397
|
+
if (opts.dryRun) {
|
|
398
|
+
console.log('\nDry run mode -- exiting');
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
// ── 4. Resolve repo path ─────────────────────────────────────────────────
|
|
402
|
+
const repoPath = config.base.repoPath || process.cwd();
|
|
403
|
+
// ── 5. Acquire PID lock ──────────────────────────────────────────────────
|
|
404
|
+
ensureGlobalStateDir();
|
|
405
|
+
const pidFilePath = resolvePidFilePath();
|
|
406
|
+
try {
|
|
407
|
+
acquirePidLock(pidFilePath);
|
|
408
|
+
}
|
|
409
|
+
catch (err) {
|
|
410
|
+
if (err instanceof PidLockError) {
|
|
411
|
+
console.error(err.message);
|
|
412
|
+
process.exit(1);
|
|
413
|
+
}
|
|
414
|
+
throw err;
|
|
415
|
+
}
|
|
416
|
+
// ── 5a. Canonical log setup ─────────────────────────────────────────────
|
|
417
|
+
// In foreground mode (TTY), rotate the log and tee stdout/stderr to it.
|
|
418
|
+
// In background mode, startDaemonBackground already rotated and redirected
|
|
419
|
+
// stdio to the log file, so we skip rotation to avoid renaming the file
|
|
420
|
+
// out from under our own fd.
|
|
421
|
+
const logPath = resolveDaemonLogPath();
|
|
422
|
+
let cleanupLogTee = null;
|
|
423
|
+
if (process.stdout.isTTY) {
|
|
424
|
+
rotateDaemonLog();
|
|
425
|
+
cleanupLogTee = setupCanonicalLogTee();
|
|
426
|
+
}
|
|
427
|
+
writeDaemonMeta(logPath);
|
|
428
|
+
// ── 5b. Register top-level error handlers (early, before engine init) ──
|
|
429
|
+
let isShuttingDown = false;
|
|
430
|
+
// shutdown is assigned later in this scope once engines and governor exist.
|
|
431
|
+
// It will be set before the event loop yields, so async handlers will see it.
|
|
432
|
+
let shutdown = null;
|
|
433
|
+
process.on('uncaughtException', (err) => {
|
|
434
|
+
console.error('[FATAL] Uncaught exception:', err);
|
|
435
|
+
if (isShuttingDown)
|
|
436
|
+
return;
|
|
437
|
+
if (shutdown) {
|
|
438
|
+
void shutdown('uncaughtException').finally(() => process.exit(1));
|
|
439
|
+
}
|
|
440
|
+
else {
|
|
441
|
+
// Exception during init before shutdown is wired up
|
|
442
|
+
releasePidLock(pidFilePath);
|
|
443
|
+
process.exit(1);
|
|
444
|
+
}
|
|
445
|
+
});
|
|
446
|
+
process.on('unhandledRejection', (reason) => {
|
|
447
|
+
console.error('[FATAL] Unhandled rejection:', reason);
|
|
448
|
+
if (isShuttingDown)
|
|
449
|
+
return;
|
|
450
|
+
if (shutdown) {
|
|
451
|
+
void shutdown('unhandledRejection');
|
|
452
|
+
}
|
|
453
|
+
});
|
|
454
|
+
// ── 6. Create engine instances ───────────────────────────────────────────
|
|
455
|
+
const engines = [];
|
|
456
|
+
if (config.strategy.enabled) {
|
|
457
|
+
engines.push(new StrategyEngine());
|
|
458
|
+
}
|
|
459
|
+
if (config.factory.enabled) {
|
|
460
|
+
try {
|
|
461
|
+
// Dynamic import: @telora/factory may not be installed in all deployments.
|
|
462
|
+
// The specifier is constructed via a variable so TypeScript does not attempt
|
|
463
|
+
// static module resolution (the factory dist/ may not exist at typecheck time).
|
|
464
|
+
const factoryModulePath = '@telora/factory/factory-engine';
|
|
465
|
+
const factoryModule = await import(/* webpackIgnore: true */ factoryModulePath);
|
|
466
|
+
engines.push(new factoryModule.FactoryEngine());
|
|
467
|
+
}
|
|
468
|
+
catch (err) {
|
|
469
|
+
console.warn(`Factory engine is enabled but @telora/factory could not be loaded: ` +
|
|
470
|
+
`${err instanceof Error ? err.message : String(err)}`);
|
|
471
|
+
console.warn('The factory engine will be skipped. Install @telora/factory to enable it.');
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
if (engines.length === 0) {
|
|
475
|
+
console.error('No engines are enabled. Enable at least one engine in the config or remove --engine filter.');
|
|
476
|
+
releasePidLock(pidFilePath);
|
|
477
|
+
process.exit(1);
|
|
478
|
+
}
|
|
479
|
+
// ── 6b. Create and inject resource governor ─────────────────────────────
|
|
480
|
+
// Compute per-engine limits from config (using resolved defaults)
|
|
481
|
+
const engineLimits = {};
|
|
482
|
+
if (config.strategy.enabled && engines.some(e => e.name === 'strategy')) {
|
|
483
|
+
const strategyMax = typeof config.strategy.fields.maxTotalSessions === 'number'
|
|
484
|
+
? config.strategy.fields.maxTotalSessions : 5;
|
|
485
|
+
engineLimits.strategy = strategyMax;
|
|
486
|
+
}
|
|
487
|
+
if (config.factory.enabled && engines.some(e => e.name === 'factory')) {
|
|
488
|
+
const factoryMax = typeof config.factory.fields.maxConcurrentInstances === 'number'
|
|
489
|
+
? config.factory.fields.maxConcurrentInstances : 3;
|
|
490
|
+
engineLimits.factory = factoryMax;
|
|
491
|
+
}
|
|
492
|
+
// Global max: sum of per-engine limits (or env override)
|
|
493
|
+
const env = process.env;
|
|
494
|
+
const defaultGlobalMax = Object.values(engineLimits).reduce((sum, v) => sum + v, 0) || 8;
|
|
495
|
+
const maxGlobalSessions = env.TELORA_MAX_GLOBAL_SESSIONS
|
|
496
|
+
? parseInt(env.TELORA_MAX_GLOBAL_SESSIONS, 10)
|
|
497
|
+
: (typeof config.rawFileConfig.maxGlobalSessions === 'number'
|
|
498
|
+
? config.rawFileConfig.maxGlobalSessions : defaultGlobalMax);
|
|
499
|
+
const governor = new ResourceGovernor({
|
|
500
|
+
maxGlobalSessions,
|
|
501
|
+
engineLimits,
|
|
502
|
+
});
|
|
503
|
+
// Inject governor into engines via the ExecutionEngine interface
|
|
504
|
+
for (const engine of engines) {
|
|
505
|
+
engine.setGovernor?.(governor);
|
|
506
|
+
}
|
|
507
|
+
console.log(`Resource governor: max ${maxGlobalSessions} global sessions ` +
|
|
508
|
+
`(${Object.entries(engineLimits).map(([k, v]) => `${k}: ${v}`).join(', ')})`);
|
|
509
|
+
// ── 7. Set up shutdown handler ───────────────────────────────────────────
|
|
510
|
+
shutdown = async (signal) => {
|
|
511
|
+
if (isShuttingDown)
|
|
512
|
+
return;
|
|
513
|
+
isShuttingDown = true;
|
|
514
|
+
setDaemonShuttingDown();
|
|
515
|
+
console.log(`\nReceived ${signal}, shutting down gracefully...`);
|
|
516
|
+
// Shut down the resource governor (reject pending acquires)
|
|
517
|
+
governor.shutdown();
|
|
518
|
+
// Phase 1: Stop all engines (stop accepting new work)
|
|
519
|
+
for (const engine of engines) {
|
|
520
|
+
try {
|
|
521
|
+
engine.stop();
|
|
522
|
+
console.log(`[${engine.name}] Stopped accepting new work`);
|
|
523
|
+
}
|
|
524
|
+
catch (err) {
|
|
525
|
+
console.warn(`[${engine.name}] Error during stop: ${err instanceof Error ? err.message : String(err)}`);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
// Phase 2: Shut down all engines in parallel (graceful cleanup)
|
|
529
|
+
const shutdownResults = await Promise.allSettled(engines.map(async (engine) => {
|
|
530
|
+
try {
|
|
531
|
+
await engine.shutdown();
|
|
532
|
+
console.log(`[${engine.name}] Shutdown complete`);
|
|
533
|
+
}
|
|
534
|
+
catch (err) {
|
|
535
|
+
console.warn(`[${engine.name}] Error during shutdown: ${err instanceof Error ? err.message : String(err)}`);
|
|
536
|
+
}
|
|
537
|
+
}));
|
|
538
|
+
// Log any rejected promises
|
|
539
|
+
for (let i = 0; i < shutdownResults.length; i++) {
|
|
540
|
+
const result = shutdownResults[i];
|
|
541
|
+
if (result && result.status === 'rejected') {
|
|
542
|
+
console.warn(`[${engines[i]?.name ?? 'unknown'}] Shutdown promise rejected: ${String(result.reason)}`);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
// Phase 3: Release PID lock and clean up
|
|
546
|
+
cleanupLogTee?.();
|
|
547
|
+
removeDaemonMeta();
|
|
548
|
+
releasePidLock(pidFilePath);
|
|
549
|
+
console.log('Shutdown complete');
|
|
550
|
+
process.exit(0);
|
|
551
|
+
};
|
|
552
|
+
async function shutdownForUpdate() {
|
|
553
|
+
if (isShuttingDown)
|
|
554
|
+
return;
|
|
555
|
+
isShuttingDown = true;
|
|
556
|
+
setDaemonShuttingDown();
|
|
557
|
+
console.log('\n[auto-update] Shutting down for restart...');
|
|
558
|
+
governor.shutdown();
|
|
559
|
+
for (const engine of engines) {
|
|
560
|
+
try {
|
|
561
|
+
engine.stop();
|
|
562
|
+
}
|
|
563
|
+
catch { /* best effort */ }
|
|
564
|
+
}
|
|
565
|
+
await Promise.allSettled(engines.map(async (engine) => {
|
|
566
|
+
try {
|
|
567
|
+
await engine.shutdown();
|
|
568
|
+
}
|
|
569
|
+
catch { /* best effort */ }
|
|
570
|
+
}));
|
|
571
|
+
cleanupLogTee?.();
|
|
572
|
+
removeDaemonMeta();
|
|
573
|
+
releasePidLock(pidFilePath);
|
|
574
|
+
// Re-exec: spawn a new daemon process with the updated code.
|
|
575
|
+
// The old approach (exit code 75) required an external wrapper script
|
|
576
|
+
// or systemd config that was never present in normal `telora-daemon start`
|
|
577
|
+
// usage, so the daemon just died after a successful update.
|
|
578
|
+
console.log('[auto-update] Spawning new daemon process with updated code...');
|
|
579
|
+
const logPath = resolveDaemonLogPath();
|
|
580
|
+
const logFd = openSync(logPath, 'a');
|
|
581
|
+
const thisDir = dirname(fileURLToPath(import.meta.url));
|
|
582
|
+
const entryPoint = resolve(thisDir, 'index.js');
|
|
583
|
+
const child = spawn(process.execPath, [entryPoint, 'run'], {
|
|
584
|
+
detached: true,
|
|
585
|
+
stdio: ['ignore', logFd, logFd],
|
|
586
|
+
cwd: repoPath,
|
|
587
|
+
env: { ...process.env },
|
|
588
|
+
});
|
|
589
|
+
child.unref();
|
|
590
|
+
closeSync(logFd);
|
|
591
|
+
console.log(`[auto-update] New daemon spawned (PID ${child.pid}), exiting old process`);
|
|
592
|
+
process.exit(0);
|
|
593
|
+
}
|
|
594
|
+
// Register signal handlers (debounced by setupSignalHandlers)
|
|
595
|
+
// shutdown is guaranteed non-null here (assigned above)
|
|
596
|
+
setupSignalHandlers(shutdown);
|
|
597
|
+
// ── 8. Initialize and start engines ──────────────────────────────────────
|
|
598
|
+
try {
|
|
599
|
+
for (const engine of engines) {
|
|
600
|
+
console.log(`\n--- Initializing ${engine.name} engine ---`);
|
|
601
|
+
// Build the full config for this engine with all defaults applied
|
|
602
|
+
let engineConfig;
|
|
603
|
+
if (engine.name === 'strategy') {
|
|
604
|
+
engineConfig = buildStrategyConfig(config.base, config.strategy);
|
|
605
|
+
}
|
|
606
|
+
else if (engine.name === 'factory') {
|
|
607
|
+
engineConfig = buildFactoryConfig(config.base, config.factory);
|
|
608
|
+
}
|
|
609
|
+
else {
|
|
610
|
+
// Unknown engine type -- pass a basic merged config
|
|
611
|
+
const section = engine.name === 'strategy' ? config.strategy : config.factory;
|
|
612
|
+
engineConfig = buildEngineConfig(config.base, section);
|
|
613
|
+
}
|
|
614
|
+
// Init
|
|
615
|
+
await engine.init(engineConfig);
|
|
616
|
+
console.log(`[${engine.name}] Initialized`);
|
|
617
|
+
// Crash recovery
|
|
618
|
+
if (opts.skipRecovery) {
|
|
619
|
+
console.log(`[${engine.name}] Skipping crash recovery (--skip-recovery flag)`);
|
|
620
|
+
}
|
|
621
|
+
else {
|
|
622
|
+
await engine.recoverFromCrash();
|
|
623
|
+
console.log(`[${engine.name}] Crash recovery complete`);
|
|
624
|
+
}
|
|
625
|
+
// Start
|
|
626
|
+
await engine.start();
|
|
627
|
+
console.log(`[${engine.name}] Started`);
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
catch (err) {
|
|
631
|
+
console.error('Failed to initialize engines:', err instanceof Error ? err.message : String(err));
|
|
632
|
+
// Clean up: stop any engines that already started
|
|
633
|
+
for (const engine of engines) {
|
|
634
|
+
try {
|
|
635
|
+
engine.stop();
|
|
636
|
+
}
|
|
637
|
+
catch {
|
|
638
|
+
// Best effort
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
releasePidLock(pidFilePath);
|
|
642
|
+
process.exit(1);
|
|
643
|
+
}
|
|
644
|
+
// ── 9. Register SIGUSR1 (status) and SIGUSR2 (circuit breaker reset) ──
|
|
645
|
+
process.on('SIGUSR1', () => {
|
|
646
|
+
printAggregatedStatus(engines, governor);
|
|
647
|
+
});
|
|
648
|
+
process.on('SIGUSR2', () => {
|
|
649
|
+
forceCloseAllCircuitBreakers();
|
|
650
|
+
});
|
|
651
|
+
// ── 10. Print startup message ────────────────────────────────────────────
|
|
652
|
+
const engineNames = engines.map(e => e.name).join(', ');
|
|
653
|
+
let currentVersion = '0.0.0';
|
|
654
|
+
try {
|
|
655
|
+
const thisDir = dirname(fileURLToPath(import.meta.url));
|
|
656
|
+
const pkg = JSON.parse(readFileSync(join(thisDir, '..', 'package.json'), 'utf-8'));
|
|
657
|
+
if (pkg.version)
|
|
658
|
+
currentVersion = pkg.version;
|
|
659
|
+
}
|
|
660
|
+
catch { /* dev mode -- no package.json next to dist */ }
|
|
661
|
+
console.log(`\n[unified-daemon] v${currentVersion} Started at ${new Date().toISOString()} (PID ${process.pid})`);
|
|
662
|
+
console.log(`Engines: ${engineNames}`);
|
|
663
|
+
console.log('Press Ctrl+C to stop\n');
|
|
664
|
+
// ── 11. Start auto-update loop ──────────────────────────────────────────
|
|
665
|
+
const autoUpdateEnabled = !opts.noAutoUpdate && resolveAutoUpdateEnabled(config);
|
|
666
|
+
if (autoUpdateEnabled) {
|
|
667
|
+
const autoUpdateIntervalMs = resolveAutoUpdateIntervalMs(config);
|
|
668
|
+
startAutoUpdateLoop({
|
|
669
|
+
currentVersion,
|
|
670
|
+
intervalMs: autoUpdateIntervalMs,
|
|
671
|
+
isIdle: () => engines.every((e) => e.getResourceUsage().activeClaudeProcesses === 0),
|
|
672
|
+
onUpdateReady: () => {
|
|
673
|
+
console.log('[auto-update] Update installed, initiating restart...');
|
|
674
|
+
// Trigger graceful shutdown, then exit with code 75
|
|
675
|
+
void shutdownForUpdate();
|
|
676
|
+
},
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
// ── 12. Keep process alive ─────────────────────────────────────────────
|
|
680
|
+
await new Promise(() => { });
|
|
681
|
+
}
|
|
682
|
+
//# sourceMappingURL=unified-shell.js.map
|