@ouro.bot/cli 0.1.0-alpha.51 → 0.1.0-alpha.511
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +133 -19
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/agent.json +3 -2
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/SOUL.md +2 -2
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-serpent.md +1 -1
- package/changelog.json +3253 -0
- package/dist/arc/attention-types.js +8 -0
- package/dist/arc/cares.js +140 -0
- package/dist/arc/episodes.js +117 -0
- package/dist/arc/intentions.js +133 -0
- package/dist/arc/json-store.js +117 -0
- package/dist/arc/obligations.js +237 -0
- package/dist/arc/packets.js +193 -0
- package/dist/arc/presence.js +185 -0
- package/dist/arc/task-lifecycle.js +65 -0
- package/dist/heart/active-work.js +867 -35
- package/dist/heart/agent-entry.js +58 -3
- package/dist/heart/attachments/image-normalize.js +194 -0
- package/dist/heart/attachments/materialize.js +97 -0
- package/dist/heart/attachments/originals.js +88 -0
- package/dist/heart/attachments/render.js +29 -0
- package/dist/heart/attachments/sources/adapter.js +2 -0
- package/dist/heart/attachments/sources/bluebubbles.js +156 -0
- package/dist/heart/attachments/sources/cli-local-file.js +78 -0
- package/dist/heart/attachments/sources/index.js +16 -0
- package/dist/heart/attachments/store.js +103 -0
- package/dist/heart/attachments/types.js +93 -0
- package/dist/heart/auth/auth-flow.js +426 -0
- package/dist/heart/background-operations.js +281 -0
- package/dist/heart/bundle-state.js +168 -0
- package/dist/heart/commitments.js +111 -0
- package/dist/heart/config-registry.js +304 -0
- package/dist/heart/config.js +119 -129
- package/dist/heart/core.js +878 -244
- package/dist/heart/cross-chat-delivery.js +131 -0
- package/dist/heart/daemon/agent-config-check.js +490 -0
- package/dist/heart/daemon/agent-discovery.js +79 -3
- package/dist/heart/daemon/agent-service.js +360 -0
- package/dist/heart/daemon/agentic-repair.js +216 -0
- package/dist/heart/daemon/bluebubbles-health-diagnostics.js +122 -0
- package/dist/heart/daemon/cadence.js +70 -0
- package/dist/heart/daemon/cli-defaults.js +640 -0
- package/dist/heart/daemon/cli-exec.js +7241 -0
- package/dist/heart/daemon/cli-help.js +493 -0
- package/dist/heart/daemon/cli-parse.js +1536 -0
- package/dist/heart/daemon/cli-render-doctor.js +57 -0
- package/dist/heart/daemon/cli-render.js +561 -0
- package/dist/heart/daemon/cli-types.js +8 -0
- package/dist/heart/daemon/connect-bay.js +323 -0
- package/dist/heart/daemon/daemon-cli.js +29 -1631
- package/dist/heart/daemon/daemon-entry.js +345 -3
- package/dist/heart/daemon/daemon-health.js +141 -0
- package/dist/heart/daemon/daemon-runtime-sync.js +190 -12
- package/dist/heart/daemon/daemon-tombstone.js +236 -0
- package/dist/heart/daemon/daemon.js +677 -58
- package/dist/heart/daemon/dns-workflow.js +394 -0
- package/dist/heart/daemon/doctor-types.js +8 -0
- package/dist/heart/daemon/doctor.js +750 -0
- package/dist/heart/daemon/health-monitor.js +92 -1
- package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
- package/dist/heart/daemon/hooks/bundle-meta.js +115 -1
- package/dist/heart/daemon/http-health-probe.js +80 -0
- package/dist/heart/daemon/human-command-screens.js +234 -0
- package/dist/heart/daemon/human-readiness.js +114 -0
- package/dist/heart/daemon/inner-status.js +89 -0
- package/dist/heart/daemon/interactive-repair.js +394 -0
- package/dist/heart/daemon/launchd.js +25 -5
- package/dist/heart/daemon/log-tailer.js +82 -12
- package/dist/heart/daemon/logs-prune.js +110 -0
- package/dist/heart/daemon/message-router.js +2 -2
- package/dist/heart/daemon/os-cron-deps.js +134 -0
- package/dist/heart/daemon/ouro-bot-entry.js +4 -2
- package/dist/heart/daemon/ouro-entry.js +3 -1
- package/dist/heart/daemon/process-manager.js +214 -0
- package/dist/heart/daemon/provider-discovery.js +137 -0
- package/dist/heart/daemon/provider-ping-progress.js +83 -0
- package/dist/heart/daemon/pulse.js +475 -0
- package/dist/heart/daemon/readiness-repair.js +365 -0
- package/dist/heart/daemon/run-hooks.js +2 -0
- package/dist/heart/daemon/runtime-logging.js +67 -16
- package/dist/heart/daemon/runtime-metadata.js +73 -0
- package/dist/heart/daemon/runtime-mode.js +67 -0
- package/dist/heart/daemon/safe-mode.js +161 -0
- package/dist/heart/daemon/sense-manager.js +178 -37
- package/dist/heart/daemon/session-id-resolver.js +131 -0
- package/dist/heart/daemon/skill-management-installer.js +94 -0
- package/dist/heart/daemon/socket-client.js +109 -4
- package/dist/heart/daemon/stale-bundle-prune.js +96 -0
- package/dist/heart/daemon/startup-tui.js +264 -0
- package/dist/heart/daemon/task-scheduler.js +3 -25
- package/dist/heart/daemon/terminal-ui.js +499 -0
- package/dist/heart/daemon/thoughts.js +162 -17
- package/dist/heart/daemon/up-progress.js +366 -0
- package/dist/heart/daemon/vault-items.js +56 -0
- package/dist/heart/delegation.js +1 -1
- package/dist/heart/habits/habit-migration.js +189 -0
- package/dist/heart/habits/habit-parser.js +140 -0
- package/dist/heart/habits/habit-runtime-state.js +100 -0
- package/dist/heart/habits/habit-scheduler.js +372 -0
- package/dist/heart/{daemon → hatch}/hatch-flow.js +52 -117
- package/dist/heart/{daemon → hatch}/hatch-specialist.js +3 -3
- package/dist/heart/{daemon → hatch}/specialist-prompt.js +12 -9
- package/dist/heart/{daemon → hatch}/specialist-tools.js +35 -12
- package/dist/heart/identity.js +205 -66
- package/dist/heart/kept-notes.js +357 -0
- package/dist/heart/kicks.js +1 -1
- package/dist/heart/machine-identity.js +161 -0
- package/dist/heart/mail-import-discovery.js +353 -0
- package/dist/heart/mcp/mcp-server.js +653 -0
- package/dist/heart/migrate-config.js +100 -0
- package/dist/heart/model-capabilities.js +19 -0
- package/dist/heart/outlook/outlook-http-hooks.js +66 -0
- package/dist/heart/outlook/outlook-http-response.js +7 -0
- package/dist/heart/outlook/outlook-http-routes.js +244 -0
- package/dist/heart/outlook/outlook-http-static.js +103 -0
- package/dist/heart/outlook/outlook-http-transport.js +116 -0
- package/dist/heart/outlook/outlook-http.js +99 -0
- package/dist/heart/outlook/outlook-read.js +31 -0
- package/dist/heart/outlook/outlook-types.js +27 -0
- package/dist/heart/outlook/outlook-view.js +195 -0
- package/dist/heart/outlook/readers/agent-machine.js +382 -0
- package/dist/heart/outlook/readers/continuity-readers.js +336 -0
- package/dist/heart/outlook/readers/mail.js +362 -0
- package/dist/heart/outlook/readers/runtime-readers.js +644 -0
- package/dist/heart/outlook/readers/sessions.js +232 -0
- package/dist/heart/outlook/readers/shared.js +111 -0
- package/dist/heart/platform.js +81 -0
- package/dist/heart/provider-attempt.js +134 -0
- package/dist/heart/provider-binding-resolver.js +255 -0
- package/dist/heart/provider-credentials.js +424 -0
- package/dist/heart/provider-failover.js +301 -0
- package/dist/heart/provider-models.js +81 -0
- package/dist/heart/provider-ping.js +262 -0
- package/dist/heart/provider-state.js +216 -0
- package/dist/heart/provider-visibility.js +188 -0
- package/dist/heart/providers/anthropic-token.js +131 -0
- package/dist/heart/providers/anthropic.js +139 -52
- package/dist/heart/providers/azure.js +97 -13
- package/dist/heart/providers/error-classification.js +127 -0
- package/dist/heart/providers/github-copilot.js +145 -0
- package/dist/heart/providers/minimax-vlm.js +189 -0
- package/dist/heart/providers/minimax.js +26 -8
- package/dist/heart/providers/openai-codex.js +55 -40
- package/dist/heart/runtime-capability-check.js +170 -0
- package/dist/heart/runtime-credentials.js +260 -0
- package/dist/heart/sense-truth.js +11 -4
- package/dist/heart/session-activity.js +43 -22
- package/dist/heart/session-events.js +1150 -0
- package/dist/heart/session-playback-cli-main.js +5 -0
- package/dist/heart/session-playback-cli.js +36 -0
- package/dist/heart/session-playback.js +231 -0
- package/dist/heart/session-stats-cli-main.js +5 -0
- package/dist/heart/session-stats.js +182 -0
- package/dist/heart/session-transcript.js +167 -0
- package/dist/heart/start-of-turn-packet.js +345 -0
- package/dist/heart/streaming.js +44 -27
- package/dist/heart/sync.js +332 -0
- package/dist/heart/target-resolution.js +127 -0
- package/dist/heart/tempo.js +93 -0
- package/dist/heart/temporal-view.js +41 -0
- package/dist/heart/tool-activity-callbacks.js +36 -0
- package/dist/heart/tool-description.js +135 -0
- package/dist/heart/tool-friction.js +55 -0
- package/dist/heart/tool-loop.js +200 -0
- package/dist/heart/turn-context.js +372 -0
- package/dist/heart/{daemon → versioning}/ouro-bot-global-installer.js +1 -1
- package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +1 -1
- package/dist/heart/versioning/ouro-path-installer.js +425 -0
- package/dist/heart/versioning/ouro-version-manager.js +295 -0
- package/dist/heart/{daemon → versioning}/staged-restart.js +40 -8
- package/dist/heart/{daemon → versioning}/update-checker.js +5 -1
- package/dist/heart/{daemon → versioning}/update-hooks.js +63 -59
- package/dist/mailroom/attention.js +167 -0
- package/dist/mailroom/autonomy.js +209 -0
- package/dist/mailroom/blob-store.js +606 -0
- package/dist/mailroom/body-cache.js +61 -0
- package/dist/mailroom/core.js +672 -0
- package/dist/mailroom/entry.js +160 -0
- package/dist/mailroom/file-store.js +426 -0
- package/dist/mailroom/mbox-import.js +382 -0
- package/dist/mailroom/outbound.js +380 -0
- package/dist/mailroom/policy.js +263 -0
- package/dist/mailroom/reader.js +219 -0
- package/dist/mailroom/search-cache.js +182 -0
- package/dist/mailroom/search-relevance.js +319 -0
- package/dist/mailroom/smtp-ingress.js +176 -0
- package/dist/mailroom/source-state.js +176 -0
- package/dist/mailroom/thread.js +109 -0
- package/dist/mailroom/travel-extract.js +89 -0
- package/dist/mind/bundle-manifest.js +7 -1
- package/dist/mind/context.js +165 -101
- package/dist/mind/diary-integrity.js +60 -0
- package/dist/mind/{memory.js → diary.js} +74 -93
- package/dist/mind/embedding-provider.js +60 -0
- package/dist/mind/file-state.js +179 -0
- package/dist/mind/friends/channel.js +30 -0
- package/dist/mind/friends/group-context.js +144 -0
- package/dist/mind/friends/resolver.js +54 -2
- package/dist/mind/friends/store-file.js +39 -3
- package/dist/mind/friends/trust-explanation.js +74 -0
- package/dist/mind/friends/types.js +2 -2
- package/dist/mind/journal-index.js +161 -0
- package/dist/mind/note-search.js +268 -0
- package/dist/mind/obligation-steering.js +221 -0
- package/dist/mind/pending.js +4 -0
- package/dist/mind/prompt-refresh.js +3 -2
- package/dist/mind/prompt.js +940 -111
- package/dist/mind/provenance-trust.js +26 -0
- package/dist/mind/scrutiny.js +173 -0
- package/dist/nerves/cli-logging.js +7 -1
- package/dist/nerves/coverage/audit-rules.js +15 -6
- package/dist/nerves/coverage/audit.js +28 -2
- package/dist/nerves/coverage/cli.js +1 -1
- package/dist/nerves/coverage/contract.js +5 -5
- package/dist/nerves/coverage/file-completeness.js +114 -5
- package/dist/nerves/coverage/run-artifacts.js +1 -1
- package/dist/nerves/event-buffer.js +111 -0
- package/dist/nerves/index.js +224 -4
- package/dist/nerves/observation.js +20 -0
- package/dist/nerves/redact.js +79 -0
- package/dist/nerves/review/cli-main.js +5 -0
- package/dist/nerves/review/cli.js +156 -0
- package/dist/nerves/review/core.js +152 -0
- package/dist/nerves/runtime.js +5 -1
- package/dist/outlook-ui/assets/index-BPr5vNuM.css +1 -0
- package/dist/outlook-ui/assets/index-Cm51CY9W.js +61 -0
- package/dist/outlook-ui/index.html +15 -0
- package/dist/repertoire/ado-client.js +15 -56
- package/dist/repertoire/ado-semantic.js +11 -10
- package/dist/repertoire/api-client.js +97 -0
- package/dist/repertoire/bitwarden-store.js +774 -0
- package/dist/repertoire/bundle-templates.js +72 -0
- package/dist/repertoire/bw-installer.js +180 -0
- package/dist/repertoire/coding/codex-jsonl.js +64 -0
- package/dist/repertoire/coding/context-pack.js +330 -0
- package/dist/repertoire/coding/feedback.js +197 -30
- package/dist/repertoire/coding/manager.js +158 -9
- package/dist/repertoire/coding/spawner.js +55 -9
- package/dist/repertoire/coding/tools.js +170 -7
- package/dist/repertoire/commerce-errors.js +109 -0
- package/dist/repertoire/commerce-self-test.js +156 -0
- package/dist/repertoire/credential-access.js +111 -0
- package/dist/repertoire/duffel-client.js +185 -0
- package/dist/repertoire/github-client.js +14 -55
- package/dist/repertoire/graph-client.js +11 -52
- package/dist/repertoire/guardrails.js +396 -0
- package/dist/repertoire/mcp-client.js +255 -0
- package/dist/repertoire/mcp-manager.js +305 -0
- package/dist/repertoire/mcp-tools.js +63 -0
- package/dist/repertoire/shell-sessions.js +133 -0
- package/dist/repertoire/skills.js +15 -24
- package/dist/repertoire/stripe-client.js +131 -0
- package/dist/repertoire/tasks/board.js +31 -5
- package/dist/repertoire/tasks/fix.js +182 -0
- package/dist/repertoire/tasks/index.js +16 -4
- package/dist/repertoire/tasks/lifecycle.js +2 -2
- package/dist/repertoire/tasks/parser.js +3 -2
- package/dist/repertoire/tasks/scanner.js +194 -37
- package/dist/repertoire/tasks/transitions.js +16 -78
- package/dist/repertoire/tool-results.js +29 -0
- package/dist/repertoire/tools-attachments.js +317 -0
- package/dist/repertoire/tools-base.js +46 -955
- package/dist/repertoire/tools-bluebubbles.js +1 -0
- package/dist/repertoire/tools-bridge.js +141 -0
- package/dist/repertoire/tools-bundle.js +984 -0
- package/dist/repertoire/tools-config.js +185 -0
- package/dist/repertoire/tools-continuity.js +248 -0
- package/dist/repertoire/tools-credential.js +381 -0
- package/dist/repertoire/tools-files.js +342 -0
- package/dist/repertoire/tools-flight.js +224 -0
- package/dist/repertoire/tools-flow.js +105 -0
- package/dist/repertoire/tools-github.js +1 -7
- package/dist/repertoire/tools-mail.js +1477 -0
- package/dist/repertoire/tools-notes.js +376 -0
- package/dist/repertoire/tools-session.js +749 -0
- package/dist/repertoire/tools-shell.js +120 -0
- package/dist/repertoire/tools-stripe.js +180 -0
- package/dist/repertoire/tools-surface.js +243 -0
- package/dist/repertoire/tools-teams.js +9 -39
- package/dist/repertoire/tools-travel.js +125 -0
- package/dist/repertoire/tools-trip.js +422 -0
- package/dist/repertoire/tools-user-profile.js +144 -0
- package/dist/repertoire/tools-vault.js +40 -0
- package/dist/repertoire/tools.js +107 -100
- package/dist/repertoire/travel-api-client.js +360 -0
- package/dist/repertoire/user-profile.js +131 -0
- package/dist/repertoire/vault-setup.js +246 -0
- package/dist/repertoire/vault-unlock.js +561 -0
- package/dist/scripts/claude-code-hook.js +41 -0
- package/dist/scripts/claude-code-stop-hook.js +47 -0
- package/dist/senses/attention-queue.js +116 -0
- package/dist/senses/bluebubbles/attachment-cache.js +53 -0
- package/dist/senses/bluebubbles/attachment-download.js +137 -0
- package/dist/senses/{bluebubbles-client.js → bluebubbles/client.js} +219 -18
- package/dist/senses/bluebubbles/entry.js +73 -0
- package/dist/senses/{bluebubbles-inbound-log.js → bluebubbles/inbound-log.js} +20 -3
- package/dist/senses/bluebubbles/index.js +1881 -0
- package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +121 -70
- package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +33 -12
- package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +3 -3
- package/dist/senses/bluebubbles/processed-log.js +111 -0
- package/dist/senses/bluebubbles/replay.js +129 -0
- package/dist/senses/{bluebubbles-runtime-state.js → bluebubbles/runtime-state.js} +2 -2
- package/dist/senses/{bluebubbles-session-cleanup.js → bluebubbles/session-cleanup.js} +1 -1
- package/dist/senses/cli/bracketed-paste.js +82 -0
- package/dist/senses/cli/image-paste.js +287 -0
- package/dist/senses/cli/image-ref-navigation.js +75 -0
- package/dist/senses/cli/ink-app.js +156 -0
- package/dist/senses/cli/inline-diff.js +64 -0
- package/dist/senses/cli/input-keys.js +174 -0
- package/dist/senses/cli/kill-ring.js +86 -0
- package/dist/senses/cli/message-list.js +51 -0
- package/dist/senses/cli/ouro-tui.js +605 -0
- package/dist/senses/cli/spinner-imperative.js +135 -0
- package/dist/senses/cli/spinner.js +101 -0
- package/dist/senses/cli/status-line.js +60 -0
- package/dist/senses/cli/streaming-markdown.js +526 -0
- package/dist/senses/cli/tool-display.js +83 -0
- package/dist/senses/cli/tool-render.js +85 -0
- package/dist/senses/cli/tui-store.js +240 -0
- package/dist/senses/cli/virtual-list.js +35 -0
- package/dist/senses/cli-entry.js +60 -8
- package/dist/senses/cli-layout.js +187 -0
- package/dist/senses/cli.js +511 -209
- package/dist/senses/commands.js +66 -3
- package/dist/senses/habit-turn-message.js +108 -0
- package/dist/senses/inner-dialog-worker.js +175 -21
- package/dist/senses/inner-dialog.js +330 -27
- package/dist/senses/mail-entry.js +66 -0
- package/dist/senses/mail.js +379 -0
- package/dist/senses/pipeline.js +573 -164
- package/dist/senses/proactive-content-guard.js +51 -0
- package/dist/senses/shared-turn.js +248 -0
- package/dist/senses/surface-tool.js +68 -0
- package/dist/senses/teams-entry.js +60 -8
- package/dist/senses/teams.js +405 -170
- package/dist/senses/trust-gate.js +100 -5
- package/dist/trips/core.js +138 -0
- package/dist/trips/store.js +146 -0
- package/package.json +37 -7
- package/skills/agent-commerce.md +106 -0
- package/skills/browser-navigation.md +117 -0
- package/skills/commerce-setup-guide.md +116 -0
- package/skills/commerce-setup.md +84 -0
- package/skills/configure-dev-tools.md +101 -0
- package/skills/travel-planning.md +138 -0
- package/dist/heart/daemon/ouro-path-installer.js +0 -178
- package/dist/heart/daemon/subagent-installer.js +0 -166
- package/dist/heart/session-recall.js +0 -116
- package/dist/mind/associative-recall.js +0 -209
- package/dist/senses/bluebubbles-entry.js +0 -13
- package/dist/senses/bluebubbles.js +0 -1142
- package/dist/senses/debug-activity.js +0 -148
- package/subagents/README.md +0 -86
- package/subagents/work-doer.md +0 -237
- package/subagents/work-merger.md +0 -618
- package/subagents/work-planner.md +0 -390
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
- /package/dist/heart/{daemon → hatch}/hatch-animation.js +0 -0
- /package/dist/heart/{daemon → hatch}/specialist-orchestrator.js +0 -0
- /package/dist/heart/{daemon → versioning}/ouro-uti.js +0 -0
- /package/dist/heart/{daemon → versioning}/wrapper-publish-guard.js +0 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.ensureSkillManagement = ensureSkillManagement;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
40
|
+
const identity_1 = require("../identity");
|
|
41
|
+
const SKILL_MANAGEMENT_URL = "https://raw.githubusercontent.com/ouroborosbot/ouroboros-skills/main/skills/skill-management/SKILL.md";
|
|
42
|
+
async function ensureSkillManagement() {
|
|
43
|
+
const bundlesRoot = (0, identity_1.getAgentBundlesRoot)();
|
|
44
|
+
if (!fs.existsSync(bundlesRoot))
|
|
45
|
+
return;
|
|
46
|
+
// Find all agent bundles
|
|
47
|
+
const entries = fs.readdirSync(bundlesRoot).filter(e => e.endsWith(".ouro"));
|
|
48
|
+
if (entries.length === 0)
|
|
49
|
+
return;
|
|
50
|
+
// Check if ANY bundle is missing the skill
|
|
51
|
+
const missing = entries.filter(e => {
|
|
52
|
+
const targetPath = path.join(bundlesRoot, e, "skills", "skill-management.md");
|
|
53
|
+
return !fs.existsSync(targetPath);
|
|
54
|
+
});
|
|
55
|
+
if (missing.length === 0)
|
|
56
|
+
return;
|
|
57
|
+
// eslint-disable-next-line no-console -- terminal UX: visible install status
|
|
58
|
+
console.log("installing skill-management from ouroboros-skills...");
|
|
59
|
+
try {
|
|
60
|
+
const response = await fetch(SKILL_MANAGEMENT_URL);
|
|
61
|
+
if (!response.ok) {
|
|
62
|
+
// eslint-disable-next-line no-console -- terminal UX: visible install status
|
|
63
|
+
console.error(`✗ failed to fetch skill-management (HTTP ${response.status})`);
|
|
64
|
+
(0, runtime_1.emitNervesEvent)({
|
|
65
|
+
level: "warn",
|
|
66
|
+
component: "daemon",
|
|
67
|
+
event: "daemon.skill_management_install_error",
|
|
68
|
+
message: "failed to fetch skill-management from GitHub",
|
|
69
|
+
meta: { status: response.status, url: SKILL_MANAGEMENT_URL },
|
|
70
|
+
});
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const content = await response.text();
|
|
74
|
+
for (const bundle of missing) {
|
|
75
|
+
const skillsDir = path.join(bundlesRoot, bundle, "skills");
|
|
76
|
+
const targetPath = path.join(skillsDir, "skill-management.md");
|
|
77
|
+
fs.mkdirSync(skillsDir, { recursive: true });
|
|
78
|
+
fs.writeFileSync(targetPath, content, "utf-8");
|
|
79
|
+
}
|
|
80
|
+
// eslint-disable-next-line no-console -- terminal UX: visible install status
|
|
81
|
+
console.log(`✓ installed skill-management (${missing.length} agent${missing.length > 1 ? "s" : ""})`);
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
// eslint-disable-next-line no-console -- terminal UX: visible install status
|
|
85
|
+
console.error(`✗ failed to install skill-management: ${error instanceof Error ? error.message : String(error)}`);
|
|
86
|
+
(0, runtime_1.emitNervesEvent)({
|
|
87
|
+
level: "warn",
|
|
88
|
+
component: "daemon",
|
|
89
|
+
event: "daemon.skill_management_install_error",
|
|
90
|
+
message: "failed to install skill-management skill",
|
|
91
|
+
meta: { error: error instanceof Error ? error.message : String(error) },
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.DEFAULT_DAEMON_SOCKET_PATH = void 0;
|
|
37
|
+
exports.__bypassVitestGuardForTests = __bypassVitestGuardForTests;
|
|
37
38
|
exports.sendDaemonCommand = sendDaemonCommand;
|
|
38
39
|
exports.checkDaemonSocketAlive = checkDaemonSocketAlive;
|
|
39
40
|
exports.requestInnerWake = requestInnerWake;
|
|
@@ -41,7 +42,81 @@ const fs = __importStar(require("fs"));
|
|
|
41
42
|
const net = __importStar(require("net"));
|
|
42
43
|
const runtime_1 = require("../../nerves/runtime");
|
|
43
44
|
exports.DEFAULT_DAEMON_SOCKET_PATH = "/tmp/ouroboros-daemon.sock";
|
|
45
|
+
/**
|
|
46
|
+
* Defense-in-depth: detect if we're running under vitest. Tests that forget
|
|
47
|
+
* to `vi.mock("../../heart/daemon/socket-client")` would otherwise leak real
|
|
48
|
+
* `inner.wake` commands (with whatever literal agent name the test uses,
|
|
49
|
+
* commonly "testagent") into whichever real daemon happens to be running on
|
|
50
|
+
* the developer's machine. That has caused real outages when test loops
|
|
51
|
+
* flooded the production-on-localhost daemon.
|
|
52
|
+
*
|
|
53
|
+
* We detect vitest via `process.argv` (not env vars — the project bans those)
|
|
54
|
+
* and convert all socket operations into safe no-ops. Individual tests are
|
|
55
|
+
* still expected to vi.mock this module so they can assert on call counts,
|
|
56
|
+
* but this guard guarantees that "I forgot to mock" can never again take
|
|
57
|
+
* down the daemon.
|
|
58
|
+
*
|
|
59
|
+
* The socket-client's OWN tests (which exercise the real functions against a
|
|
60
|
+
* test socket) call `__bypassVitestGuardForTests(true)` to opt out of the
|
|
61
|
+
* guard. We persist that flag on globalThis so it survives `vi.resetModules`
|
|
62
|
+
* (which clears the module cache between tests).
|
|
63
|
+
*
|
|
64
|
+
* HARDENING (cross-file leak protection): the bypass flag lives on globalThis
|
|
65
|
+
* and is process-wide. With vitest's default `fileParallelism: true`,
|
|
66
|
+
* concurrently-running test files in the same worker can have the bypass on
|
|
67
|
+
* even though they didn't ask for it — and any test that uses
|
|
68
|
+
* `name: "testagent"` and exercises a code path through this module will
|
|
69
|
+
* leak `inner.wake testagent` to the production daemon socket. We saw this
|
|
70
|
+
* cause a real outage on 2026-04-08 (daemon SIGTERMed mid-validation).
|
|
71
|
+
*
|
|
72
|
+
* Defense: even when the bypass flag is set, we ALWAYS hard-block calls that
|
|
73
|
+
* target the production daemon socket path. Test files that legitimately
|
|
74
|
+
* exercise the real socket-client code paths use a synthetic socket path like
|
|
75
|
+
* `/tmp/daemon.sock` or `/tmp/some-real-daemon.sock` — those keep working.
|
|
76
|
+
* Calls to `/tmp/ouroboros-daemon.sock` (DEFAULT_DAEMON_SOCKET_PATH) under
|
|
77
|
+
* vitest are unconditionally blocked, regardless of bypass state. This makes
|
|
78
|
+
* the cross-file leak vector impossible without restricting legitimate
|
|
79
|
+
* socket-client unit tests.
|
|
80
|
+
*/
|
|
81
|
+
const BYPASS_KEY = "__ouro_socket_client_bypass_vitest_guard__";
|
|
82
|
+
/** ONLY for socket-client.test.ts. Do not call from any other test or production code. */
|
|
83
|
+
function __bypassVitestGuardForTests(bypass) {
|
|
84
|
+
;
|
|
85
|
+
globalThis[BYPASS_KEY] = bypass;
|
|
86
|
+
}
|
|
87
|
+
function isVitestProcess() {
|
|
88
|
+
/* v8 ignore next -- defensive: process and process.argv always exist in node @preserve */
|
|
89
|
+
if (typeof process === "undefined" || !Array.isArray(process.argv))
|
|
90
|
+
return false;
|
|
91
|
+
return process.argv.some((arg) => typeof arg === "string" && arg.includes("vitest"));
|
|
92
|
+
}
|
|
93
|
+
function isProductionDaemonSocket(socketPath) {
|
|
94
|
+
return socketPath === exports.DEFAULT_DAEMON_SOCKET_PATH;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Returns true when this socket call should be converted into a safe no-op
|
|
98
|
+
* because we're under vitest and the call would otherwise leak into a real
|
|
99
|
+
* daemon. The bypass flag short-circuits the guard for non-production socket
|
|
100
|
+
* paths only — production socket paths are ALWAYS blocked under vitest.
|
|
101
|
+
*/
|
|
102
|
+
function shouldSuppressSocketCall(socketPath) {
|
|
103
|
+
if (!isVitestProcess())
|
|
104
|
+
return false;
|
|
105
|
+
if (isProductionDaemonSocket(socketPath))
|
|
106
|
+
return true;
|
|
107
|
+
return globalThis[BYPASS_KEY] !== true;
|
|
108
|
+
}
|
|
44
109
|
function sendDaemonCommand(socketPath, command) {
|
|
110
|
+
if (shouldSuppressSocketCall(socketPath)) {
|
|
111
|
+
(0, runtime_1.emitNervesEvent)({
|
|
112
|
+
level: "warn",
|
|
113
|
+
component: "daemon",
|
|
114
|
+
event: "daemon.socket_command_test_blocked",
|
|
115
|
+
message: "blocked socket command from leaking into real daemon under vitest",
|
|
116
|
+
meta: { socketPath, kind: command.kind, isProductionSocket: isProductionDaemonSocket(socketPath) },
|
|
117
|
+
});
|
|
118
|
+
return Promise.resolve({ ok: true, message: "test mode: socket call suppressed" });
|
|
119
|
+
}
|
|
45
120
|
(0, runtime_1.emitNervesEvent)({
|
|
46
121
|
component: "daemon",
|
|
47
122
|
event: "daemon.socket_command_start",
|
|
@@ -52,8 +127,23 @@ function sendDaemonCommand(socketPath, command) {
|
|
|
52
127
|
const client = net.createConnection(socketPath);
|
|
53
128
|
let raw = "";
|
|
54
129
|
client.on("connect", () => {
|
|
55
|
-
client.
|
|
56
|
-
|
|
130
|
+
// Write the command + newline delimiter. DO NOT call client.end()
|
|
131
|
+
// afterwards — the server closes the connection once it has written
|
|
132
|
+
// its response, which triggers our on("end") handler with the full
|
|
133
|
+
// response in `raw`.
|
|
134
|
+
//
|
|
135
|
+
// Calling client.end() here half-closes the TCP connection. The
|
|
136
|
+
// daemon server uses net.createServer() with the default
|
|
137
|
+
// allowHalfOpen: false, so when the server sees the client's FIN it
|
|
138
|
+
// auto-closes its own writable side — and any response the server
|
|
139
|
+
// tries to write after processing a long-running command (like
|
|
140
|
+
// agent.senseTurn, which runs a full LLM turn) gets dropped on the
|
|
141
|
+
// floor. Verified via repro: with client.end(), agent.senseTurn
|
|
142
|
+
// returns empty in ~149ms; without it, the same call returns the real
|
|
143
|
+
// response in ~5.8s. This is a regression of the fix in #303 that
|
|
144
|
+
// was silently reverted by 253e4b1f (commit titled "socket half-close
|
|
145
|
+
// fix" but actually *added* the half-close back).
|
|
146
|
+
client.write(JSON.stringify(command) + "\n");
|
|
57
147
|
});
|
|
58
148
|
client.on("data", (chunk) => {
|
|
59
149
|
raw += chunk.toString("utf-8");
|
|
@@ -132,6 +222,9 @@ function sendDaemonCommand(socketPath, command) {
|
|
|
132
222
|
});
|
|
133
223
|
}
|
|
134
224
|
function checkDaemonSocketAlive(socketPath) {
|
|
225
|
+
if (shouldSuppressSocketCall(socketPath)) {
|
|
226
|
+
return Promise.resolve(false);
|
|
227
|
+
}
|
|
135
228
|
(0, runtime_1.emitNervesEvent)({
|
|
136
229
|
component: "daemon",
|
|
137
230
|
event: "daemon.socket_alive_check_start",
|
|
@@ -161,8 +254,10 @@ function checkDaemonSocketAlive(socketPath) {
|
|
|
161
254
|
});
|
|
162
255
|
}
|
|
163
256
|
client.on("connect", () => {
|
|
164
|
-
|
|
165
|
-
client.end()
|
|
257
|
+
// Same half-close rationale as sendDaemonCommand: do NOT call
|
|
258
|
+
// client.end() here. The server closes after responding and that
|
|
259
|
+
// triggers the on("end") handler below.
|
|
260
|
+
client.write(JSON.stringify({ kind: "daemon.status" }) + "\n");
|
|
166
261
|
});
|
|
167
262
|
client.on("data", (chunk) => {
|
|
168
263
|
raw += chunk.toString("utf-8");
|
|
@@ -184,6 +279,16 @@ function checkDaemonSocketAlive(socketPath) {
|
|
|
184
279
|
});
|
|
185
280
|
}
|
|
186
281
|
async function requestInnerWake(agent, socketPath = exports.DEFAULT_DAEMON_SOCKET_PATH) {
|
|
282
|
+
if (shouldSuppressSocketCall(socketPath)) {
|
|
283
|
+
(0, runtime_1.emitNervesEvent)({
|
|
284
|
+
level: "warn",
|
|
285
|
+
component: "daemon",
|
|
286
|
+
event: "daemon.inner_wake_test_blocked",
|
|
287
|
+
message: "blocked inner wake from leaking into real daemon under vitest",
|
|
288
|
+
meta: { agent, socketPath, isProductionSocket: isProductionDaemonSocket(socketPath) },
|
|
289
|
+
});
|
|
290
|
+
return null;
|
|
291
|
+
}
|
|
187
292
|
const socketAvailable = fs.existsSync(socketPath);
|
|
188
293
|
(0, runtime_1.emitNervesEvent)({
|
|
189
294
|
component: "daemon",
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.pruneStaleEphemeralBundles = pruneStaleEphemeralBundles;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const identity_1 = require("../identity");
|
|
40
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
41
|
+
/**
|
|
42
|
+
* Scan the bundles root for `.ouro` directories that have no `agent.json`
|
|
43
|
+
* file (definitively dead ephemeral bundles) and delete them.
|
|
44
|
+
*
|
|
45
|
+
* Returns a list of pruned bundle directory names (e.g. `["stale.ouro"]`)
|
|
46
|
+
* for display purposes. Bundles that have `agent.json` -- even if disabled
|
|
47
|
+
* -- are never deleted. Errors on individual bundles are swallowed so that
|
|
48
|
+
* one permission-denied doesn't block pruning the rest.
|
|
49
|
+
*/
|
|
50
|
+
function pruneStaleEphemeralBundles(deps = {}) {
|
|
51
|
+
const bundlesRoot = deps.bundlesRoot ?? (0, identity_1.getAgentBundlesRoot)();
|
|
52
|
+
const readdirSync = deps.readdirSync ?? fs.readdirSync;
|
|
53
|
+
const existsSync = deps.existsSync ?? fs.existsSync;
|
|
54
|
+
const rmSync = deps.rmSync ?? fs.rmSync;
|
|
55
|
+
let entries;
|
|
56
|
+
try {
|
|
57
|
+
entries = readdirSync(bundlesRoot, { withFileTypes: true });
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
62
|
+
const pruned = [];
|
|
63
|
+
for (const entry of entries) {
|
|
64
|
+
if (!entry.isDirectory() || !entry.name.endsWith(".ouro"))
|
|
65
|
+
continue;
|
|
66
|
+
const bundlePath = path.join(bundlesRoot, entry.name);
|
|
67
|
+
const agentJsonPath = path.join(bundlePath, "agent.json");
|
|
68
|
+
if (existsSync(agentJsonPath))
|
|
69
|
+
continue;
|
|
70
|
+
try {
|
|
71
|
+
rmSync(bundlePath, { recursive: true, force: true });
|
|
72
|
+
pruned.push(entry.name);
|
|
73
|
+
(0, runtime_1.emitNervesEvent)({
|
|
74
|
+
level: "info",
|
|
75
|
+
component: "daemon",
|
|
76
|
+
event: "daemon.stale_bundle_pruned",
|
|
77
|
+
message: `pruned stale ephemeral bundle: ${entry.name}`,
|
|
78
|
+
meta: { bundle: entry.name, bundlePath },
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
(0, runtime_1.emitNervesEvent)({
|
|
83
|
+
level: "warn",
|
|
84
|
+
component: "daemon",
|
|
85
|
+
event: "daemon.stale_bundle_prune_error",
|
|
86
|
+
message: `failed to prune stale bundle: ${entry.name}`,
|
|
87
|
+
meta: {
|
|
88
|
+
bundle: entry.name,
|
|
89
|
+
bundlePath,
|
|
90
|
+
error: error instanceof Error ? error.message : String(error),
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return pruned;
|
|
96
|
+
}
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Startup TUI — real-time progress display for `ouro up`.
|
|
4
|
+
*
|
|
5
|
+
* Replaces the old `verifyDaemonAlive()` socket poll with a richer system
|
|
6
|
+
* that shows per-agent status, waits for stability, and reports degraded
|
|
7
|
+
* agents with actionable error information.
|
|
8
|
+
*
|
|
9
|
+
* Pure functions (`renderStartupProgress`, `assessStability`) are fully
|
|
10
|
+
* testable. The polling loop (`pollDaemonStartup`) uses dependency injection
|
|
11
|
+
* for all I/O.
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.assessStability = assessStability;
|
|
15
|
+
exports.renderStartupProgress = renderStartupProgress;
|
|
16
|
+
exports.renderWaitingForDaemon = renderWaitingForDaemon;
|
|
17
|
+
exports.pollDaemonStartup = pollDaemonStartup;
|
|
18
|
+
const cli_render_1 = require("./cli-render");
|
|
19
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
20
|
+
const terminal_ui_1 = require("./terminal-ui");
|
|
21
|
+
// ── Constants ──
|
|
22
|
+
const SPINNER_FRAMES = "⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏";
|
|
23
|
+
const STABILITY_THRESHOLD_MS = 5_000;
|
|
24
|
+
const POLL_INTERVAL_MS = 500;
|
|
25
|
+
// ── ANSI helpers ──
|
|
26
|
+
const RESET = "\x1b[0m";
|
|
27
|
+
const BOLD = "\x1b[1m";
|
|
28
|
+
const DIM = "\x1b[2m";
|
|
29
|
+
const GREEN = "\x1b[38;2;46;204;64m";
|
|
30
|
+
const RED = "\x1b[38;2;231;76;60m";
|
|
31
|
+
const YELLOW = "\x1b[38;2;230;190;50m";
|
|
32
|
+
// ── Pure functions ──
|
|
33
|
+
/**
|
|
34
|
+
* Assess whether all workers have reached a terminal state (stable or crashed).
|
|
35
|
+
* A worker is "stable" when status is "running" and it has been running for
|
|
36
|
+
* at least STABILITY_THRESHOLD_MS. A worker is "definitively failed" when
|
|
37
|
+
* status is "crashed". All other states are unresolved.
|
|
38
|
+
*/
|
|
39
|
+
function assessStability(payload, now) {
|
|
40
|
+
const stable = [];
|
|
41
|
+
const degraded = [];
|
|
42
|
+
let allResolved = true;
|
|
43
|
+
for (const worker of payload.workers) {
|
|
44
|
+
if (worker.status === "crashed") {
|
|
45
|
+
degraded.push({
|
|
46
|
+
agent: worker.agent,
|
|
47
|
+
errorReason: worker.errorReason ?? "unknown error",
|
|
48
|
+
fixHint: worker.fixHint ?? "check daemon logs",
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
else if (worker.status === "running" && worker.startedAt !== null) {
|
|
52
|
+
const startedMs = new Date(worker.startedAt).getTime();
|
|
53
|
+
const runningMs = now - startedMs;
|
|
54
|
+
if (runningMs >= STABILITY_THRESHOLD_MS) {
|
|
55
|
+
stable.push(worker.agent);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
allResolved = false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
// starting, stopped, or running with null startedAt — not yet resolved
|
|
63
|
+
allResolved = false;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return { resolved: allResolved, stable, degraded };
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Build an ANSI string for in-place terminal display during polling.
|
|
70
|
+
* Uses cursor-up and line-clear escapes to overwrite previous output.
|
|
71
|
+
*/
|
|
72
|
+
function renderStartupProgress(payload, elapsed, prevLineCount = 0, options = {}) {
|
|
73
|
+
const isTTY = options.isTTY ?? true;
|
|
74
|
+
const frameIndex = Math.floor(elapsed / 100) % SPINNER_FRAMES.length;
|
|
75
|
+
const spinner = SPINNER_FRAMES[frameIndex];
|
|
76
|
+
const lines = [];
|
|
77
|
+
const elapsedSec = (elapsed / 1000).toFixed(1);
|
|
78
|
+
lines.push(isTTY
|
|
79
|
+
? `${spinner} ${BOLD}waiting for agents${RESET} ${DIM}(${elapsedSec}s)${RESET}`
|
|
80
|
+
: `${spinner} waiting for agents (${elapsedSec}s)`);
|
|
81
|
+
for (const worker of payload.workers) {
|
|
82
|
+
const statusText = isTTY ? colorStatus(worker.status) : worker.status;
|
|
83
|
+
lines.push(` ${worker.agent}/${worker.worker}: ${statusText}`);
|
|
84
|
+
}
|
|
85
|
+
return renderStartupLines(lines, prevLineCount, isTTY);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Render a pre-socket status line showing what the daemon is doing.
|
|
89
|
+
*/
|
|
90
|
+
function renderWaitingForDaemon(elapsed, latestEvent, prevLineCount = 0, options = {}) {
|
|
91
|
+
const isTTY = options.isTTY ?? true;
|
|
92
|
+
const elapsedSec = (elapsed / 1000).toFixed(1);
|
|
93
|
+
const frameIndex = Math.floor(elapsed / 100) % SPINNER_FRAMES.length;
|
|
94
|
+
const spinner = SPINNER_FRAMES[frameIndex];
|
|
95
|
+
const lines = [];
|
|
96
|
+
lines.push(isTTY
|
|
97
|
+
? `${spinner} ${BOLD}starting background service${RESET} ${DIM}(${elapsedSec}s)${RESET}`
|
|
98
|
+
: `${spinner} starting background service (${elapsedSec}s)`);
|
|
99
|
+
if (latestEvent) {
|
|
100
|
+
const detail = `latest daemon event: ${latestEvent}`;
|
|
101
|
+
lines.push(isTTY ? ` ${DIM}${detail}${RESET}` : ` ${detail}`);
|
|
102
|
+
}
|
|
103
|
+
return renderStartupLines(lines, prevLineCount, isTTY);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Render the final summary after all agents have resolved.
|
|
107
|
+
*/
|
|
108
|
+
function renderFinalSummary(result, isTTY) {
|
|
109
|
+
const lines = [];
|
|
110
|
+
for (const agent of result.stable) {
|
|
111
|
+
lines.push(isTTY ? ` ${GREEN}\u2713${RESET} ${agent}: ${GREEN}stable${RESET}` : ` \u2713 ${agent}: stable`);
|
|
112
|
+
}
|
|
113
|
+
for (const d of result.degraded) {
|
|
114
|
+
lines.push(isTTY ? ` ${RED}\u2717${RESET} ${d.agent}: ${RED}degraded${RESET}` : ` \u2717 ${d.agent}: degraded`);
|
|
115
|
+
if (d.errorReason !== "unknown error") {
|
|
116
|
+
lines.push(isTTY ? ` ${DIM}error: ${d.errorReason}${RESET}` : ` error: ${d.errorReason}`);
|
|
117
|
+
}
|
|
118
|
+
if (d.fixHint !== "check daemon logs") {
|
|
119
|
+
lines.push(isTTY ? ` ${DIM}fix: ${d.fixHint}${RESET}` : ` fix: ${d.fixHint}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (!isTTY)
|
|
123
|
+
return lines.join("\n") + "\n";
|
|
124
|
+
return lines.map((line) => `\x1b[2K${line}`).join("\n") + "\n";
|
|
125
|
+
}
|
|
126
|
+
// ── Polling loop ──
|
|
127
|
+
/**
|
|
128
|
+
* Poll the daemon's status socket until all agents are stable or definitively
|
|
129
|
+
* failed, rendering real-time progress to the terminal.
|
|
130
|
+
*
|
|
131
|
+
* Detects daemon process death: if the spawned PID is no longer alive and the
|
|
132
|
+
* socket never came up, reports the failure immediately instead of spinning.
|
|
133
|
+
*/
|
|
134
|
+
async function pollDaemonStartup(deps) {
|
|
135
|
+
const startTime = deps.now();
|
|
136
|
+
let prevLineCount = 0;
|
|
137
|
+
const isTTY = deps.isTTY ?? true;
|
|
138
|
+
const isAlive = deps.isProcessAlive ?? defaultIsProcessAlive;
|
|
139
|
+
const shouldRender = deps.render ?? true;
|
|
140
|
+
let lastProgress = null;
|
|
141
|
+
const reportProgress = (message) => {
|
|
142
|
+
if (!deps.onProgress || message === lastProgress)
|
|
143
|
+
return;
|
|
144
|
+
lastProgress = message;
|
|
145
|
+
deps.onProgress(message);
|
|
146
|
+
};
|
|
147
|
+
(0, runtime_1.emitNervesEvent)({
|
|
148
|
+
component: "daemon",
|
|
149
|
+
event: "daemon.startup_poll_start",
|
|
150
|
+
message: "beginning startup stability polling",
|
|
151
|
+
meta: { socketPath: deps.socketPath, daemonPid: deps.daemonPid },
|
|
152
|
+
});
|
|
153
|
+
while (true) {
|
|
154
|
+
const now = deps.now();
|
|
155
|
+
const elapsed = now - startTime;
|
|
156
|
+
let payload = null;
|
|
157
|
+
try {
|
|
158
|
+
const response = await deps.sendCommand(deps.socketPath, { kind: "daemon.status" });
|
|
159
|
+
payload = (0, cli_render_1.parseStatusPayload)(response.data);
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
// Socket not yet available — check if the daemon process is still alive
|
|
163
|
+
if (deps.daemonPid !== null && !isAlive(deps.daemonPid)) {
|
|
164
|
+
const latestEvent = deps.readLatestDaemonEvent?.() ?? null;
|
|
165
|
+
const errorMsg = latestEvent ?? "daemon process died during startup";
|
|
166
|
+
(0, runtime_1.emitNervesEvent)({
|
|
167
|
+
level: "error",
|
|
168
|
+
component: "daemon",
|
|
169
|
+
event: "daemon.startup_process_died",
|
|
170
|
+
message: "daemon process died before socket came up",
|
|
171
|
+
meta: { pid: deps.daemonPid, lastEvent: latestEvent },
|
|
172
|
+
});
|
|
173
|
+
// Clear the waiting line
|
|
174
|
+
if (isTTY && prevLineCount > 0) {
|
|
175
|
+
let clear = `\x1b[${prevLineCount}A`;
|
|
176
|
+
for (let i = 0; i < prevLineCount; i++)
|
|
177
|
+
clear += `\x1b[2K\n`;
|
|
178
|
+
deps.writeRaw(clear);
|
|
179
|
+
}
|
|
180
|
+
return {
|
|
181
|
+
stable: [],
|
|
182
|
+
degraded: [{ agent: "daemon", errorReason: errorMsg, fixHint: "check daemon logs or run `ouro doctor`" }],
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
// Show what the daemon is doing from its log
|
|
186
|
+
const latestEvent = deps.readLatestDaemonEvent?.() ?? null;
|
|
187
|
+
reportProgress([
|
|
188
|
+
"waiting for Ouro to answer",
|
|
189
|
+
latestEvent ? `- latest daemon event: ${latestEvent}` : "- background service is still starting",
|
|
190
|
+
].join("\n"));
|
|
191
|
+
if (shouldRender) {
|
|
192
|
+
const output = renderWaitingForDaemon(elapsed, latestEvent, prevLineCount, { isTTY });
|
|
193
|
+
deps.writeRaw(output);
|
|
194
|
+
prevLineCount = latestEvent ? 2 : 1;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (payload) {
|
|
198
|
+
reportProgress(formatStartupProgressDetail(payload));
|
|
199
|
+
if (shouldRender) {
|
|
200
|
+
const output = renderStartupProgress(payload, elapsed, prevLineCount, { isTTY });
|
|
201
|
+
deps.writeRaw(output);
|
|
202
|
+
prevLineCount = payload.workers.length + 1;
|
|
203
|
+
}
|
|
204
|
+
const assessment = assessStability(payload, now);
|
|
205
|
+
if (assessment.resolved) {
|
|
206
|
+
const result = {
|
|
207
|
+
stable: assessment.stable,
|
|
208
|
+
degraded: assessment.degraded,
|
|
209
|
+
};
|
|
210
|
+
if (shouldRender) {
|
|
211
|
+
const summary = renderFinalSummary(result, isTTY);
|
|
212
|
+
deps.writeRaw(summary);
|
|
213
|
+
}
|
|
214
|
+
(0, runtime_1.emitNervesEvent)({
|
|
215
|
+
component: "daemon",
|
|
216
|
+
event: "daemon.startup_poll_end",
|
|
217
|
+
message: "startup polling complete",
|
|
218
|
+
meta: {
|
|
219
|
+
stableCount: result.stable.length,
|
|
220
|
+
degradedCount: result.degraded.length,
|
|
221
|
+
elapsedMs: elapsed,
|
|
222
|
+
},
|
|
223
|
+
});
|
|
224
|
+
return result;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
await deps.sleep(POLL_INTERVAL_MS);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
function formatStartupWorkerLine(payload) {
|
|
231
|
+
const base = `- ${payload.agent}/${payload.worker}: ${payload.status}`;
|
|
232
|
+
if (payload.status === "crashed" && payload.errorReason) {
|
|
233
|
+
return `${base} (${payload.errorReason})`;
|
|
234
|
+
}
|
|
235
|
+
return base;
|
|
236
|
+
}
|
|
237
|
+
function formatStartupProgressDetail(payload) {
|
|
238
|
+
if (payload.workers.length === 0)
|
|
239
|
+
return "Ouro answered";
|
|
240
|
+
return [
|
|
241
|
+
"Ouro answered",
|
|
242
|
+
...payload.workers.map((worker) => formatStartupWorkerLine(worker)),
|
|
243
|
+
].join("\n");
|
|
244
|
+
}
|
|
245
|
+
function colorStatus(status) {
|
|
246
|
+
const statusColor = status === "running" ? GREEN
|
|
247
|
+
: status === "crashed" ? RED
|
|
248
|
+
: YELLOW;
|
|
249
|
+
return `${statusColor}${status}${RESET}`;
|
|
250
|
+
}
|
|
251
|
+
function renderStartupLines(lines, prevLineCount, isTTY) {
|
|
252
|
+
return (0, terminal_ui_1.renderOverwriteFrame)(lines, prevLineCount, isTTY);
|
|
253
|
+
}
|
|
254
|
+
/* v8 ignore start -- process liveness check: uses real process.kill(0), tested via deployment @preserve */
|
|
255
|
+
function defaultIsProcessAlive(pid) {
|
|
256
|
+
try {
|
|
257
|
+
process.kill(pid, 0);
|
|
258
|
+
return true;
|
|
259
|
+
}
|
|
260
|
+
catch {
|
|
261
|
+
return false;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
/* v8 ignore stop */
|
|
@@ -39,6 +39,7 @@ const path = __importStar(require("path"));
|
|
|
39
39
|
const identity_1 = require("../identity");
|
|
40
40
|
const runtime_1 = require("../../nerves/runtime");
|
|
41
41
|
const parser_1 = require("../../repertoire/tasks/parser");
|
|
42
|
+
const cadence_1 = require("./cadence");
|
|
42
43
|
function walkMarkdownFiles(root, readdirSync, existsSync, files) {
|
|
43
44
|
if (!existsSync(root))
|
|
44
45
|
return;
|
|
@@ -53,29 +54,6 @@ function walkMarkdownFiles(root, readdirSync, existsSync, files) {
|
|
|
53
54
|
}
|
|
54
55
|
}
|
|
55
56
|
}
|
|
56
|
-
function parseCadence(raw) {
|
|
57
|
-
if (typeof raw !== "string")
|
|
58
|
-
return null;
|
|
59
|
-
const value = raw.trim();
|
|
60
|
-
if (!value)
|
|
61
|
-
return null;
|
|
62
|
-
// Cron format (minute hour day month weekday)
|
|
63
|
-
if (/^\S+\s+\S+\s+\S+\s+\S+\s+\S+$/.test(value)) {
|
|
64
|
-
return value;
|
|
65
|
-
}
|
|
66
|
-
const cadenceMatch = /^(\d+)(m|h|d)$/.exec(value);
|
|
67
|
-
if (!cadenceMatch)
|
|
68
|
-
return null;
|
|
69
|
-
const interval = Number.parseInt(cadenceMatch[1], 10);
|
|
70
|
-
if (!Number.isFinite(interval) || interval <= 0)
|
|
71
|
-
return null;
|
|
72
|
-
const unit = cadenceMatch[2];
|
|
73
|
-
if (unit === "m")
|
|
74
|
-
return `*/${interval} * * * *`;
|
|
75
|
-
if (unit === "h")
|
|
76
|
-
return `0 */${interval} * * *`;
|
|
77
|
-
return `0 0 */${interval} * *`;
|
|
78
|
-
}
|
|
79
57
|
function parseScheduledAt(raw) {
|
|
80
58
|
if (typeof raw !== "string")
|
|
81
59
|
return null;
|
|
@@ -140,7 +118,7 @@ class TaskDrivenScheduler {
|
|
|
140
118
|
const nextTaskPaths = new Map();
|
|
141
119
|
for (const agent of this.agents) {
|
|
142
120
|
const taskRoot = path.join(this.bundlesRoot, `${agent}.ouro`, "tasks");
|
|
143
|
-
const collections = ["one-shots", "ongoing"
|
|
121
|
+
const collections = ["one-shots", "ongoing"];
|
|
144
122
|
const files = [];
|
|
145
123
|
for (const collection of collections) {
|
|
146
124
|
walkMarkdownFiles(path.join(taskRoot, collection), this.readdirSync, this.existsSync, files);
|
|
@@ -157,7 +135,7 @@ class TaskDrivenScheduler {
|
|
|
157
135
|
nextTaskPaths.set(`${agent}:${taskId}`, filePath);
|
|
158
136
|
if (task.status === "done")
|
|
159
137
|
continue;
|
|
160
|
-
const cadence =
|
|
138
|
+
const cadence = (0, cadence_1.parseCadenceToCron)(task.frontmatter.cadence);
|
|
161
139
|
if (cadence) {
|
|
162
140
|
const id = `${agent}:${taskId}:cadence`;
|
|
163
141
|
nextJobs.set(id, {
|