@ouro.bot/cli 0.1.0-alpha.56 → 0.1.0-alpha.560
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 +127 -23
- package/RepairGuide.ouro/agent.json +5 -0
- package/RepairGuide.ouro/psyche/IDENTITY.md +19 -0
- package/RepairGuide.ouro/psyche/SOUL.md +55 -0
- package/RepairGuide.ouro/skills/diagnose-broken-remote.md +63 -0
- package/RepairGuide.ouro/skills/diagnose-stacked-typed-issues.md +35 -0
- package/RepairGuide.ouro/skills/diagnose-sync-blocked.md +54 -0
- package/RepairGuide.ouro/skills/diagnose-vault-expired.md +60 -0
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/agent.json +4 -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 +3596 -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 +837 -26
- 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 +479 -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 +322 -0
- package/dist/heart/config.js +114 -118
- package/dist/heart/core.js +913 -246
- package/dist/heart/cross-chat-delivery.js +3 -18
- package/dist/heart/daemon/agent-config-check.js +419 -0
- package/dist/heart/daemon/agent-discovery.js +102 -3
- package/dist/heart/daemon/agent-service.js +522 -0
- package/dist/heart/daemon/agentic-repair.js +547 -0
- package/dist/heart/daemon/bluebubbles-health-diagnostics.js +122 -0
- package/dist/heart/daemon/boot-sync-probe.js +197 -0
- package/dist/heart/daemon/cadence.js +70 -0
- package/dist/heart/daemon/cli-defaults.js +776 -0
- package/dist/heart/daemon/cli-exec.js +7457 -0
- package/dist/heart/daemon/cli-help.js +498 -0
- package/dist/heart/daemon/cli-parse.js +1592 -0
- package/dist/heart/daemon/cli-render-doctor.js +57 -0
- package/dist/heart/daemon/cli-render.js +763 -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 -1698
- package/dist/heart/daemon/daemon-entry.js +387 -2
- package/dist/heart/daemon/daemon-health.js +176 -0
- package/dist/heart/daemon/daemon-rollup.js +57 -0
- package/dist/heart/daemon/daemon-runtime-sync.js +88 -13
- package/dist/heart/daemon/daemon-tombstone.js +236 -0
- package/dist/heart/daemon/daemon.js +796 -71
- package/dist/heart/daemon/dns-workflow.js +394 -0
- package/dist/heart/daemon/doctor-types.js +8 -0
- package/dist/heart/daemon/doctor.js +826 -0
- package/dist/heart/daemon/health-monitor.js +122 -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 +37 -8
- package/dist/heart/daemon/log-tailer.js +82 -12
- package/dist/heart/daemon/logs-prune.js +110 -0
- package/dist/heart/daemon/mcp-canary.js +297 -0
- package/dist/heart/daemon/message-router.js +2 -2
- package/dist/heart/daemon/os-cron-deps.js +135 -0
- package/dist/heart/daemon/os-cron.js +14 -12
- 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 +375 -33
- 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 +3 -31
- package/dist/heart/daemon/safe-mode.js +161 -0
- package/dist/heart/daemon/sense-manager.js +389 -38
- 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 +158 -11
- package/dist/heart/daemon/stale-bundle-prune.js +96 -0
- package/dist/heart/daemon/startup-tui.js +330 -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 +32 -56
- package/dist/heart/{daemon → hatch}/hatch-specialist.js +6 -8
- 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 +203 -57
- 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/mailbox/mailbox-http-hooks.js +66 -0
- package/dist/heart/mailbox/mailbox-http-response.js +7 -0
- package/dist/heart/mailbox/mailbox-http-routes.js +246 -0
- package/dist/heart/mailbox/mailbox-http-static.js +103 -0
- package/dist/heart/mailbox/mailbox-http-transport.js +116 -0
- package/dist/heart/mailbox/mailbox-http.js +99 -0
- package/dist/heart/mailbox/mailbox-read.js +31 -0
- package/dist/heart/mailbox/mailbox-types.js +27 -0
- package/dist/heart/mailbox/mailbox-view.js +195 -0
- package/dist/heart/mailbox/readers/agent-machine.js +382 -0
- package/dist/heart/mailbox/readers/continuity-readers.js +338 -0
- package/dist/heart/mailbox/readers/mail.js +362 -0
- package/dist/heart/mailbox/readers/runtime-readers.js +651 -0
- package/dist/heart/mailbox/readers/sessions.js +232 -0
- package/dist/heart/mailbox/readers/shared.js +111 -0
- package/dist/heart/mcp/mcp-server.js +683 -0
- package/dist/heart/migrate-config.js +100 -0
- package/dist/heart/model-capabilities.js +19 -0
- package/dist/heart/platform.js +81 -0
- package/dist/heart/provider-attempt.js +134 -0
- package/dist/heart/provider-binding-resolver.js +267 -0
- package/dist/heart/provider-credentials.js +425 -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-readiness-cache.js +40 -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 +367 -0
- package/dist/heart/runtime-cwd.js +87 -0
- package/dist/heart/sense-truth.js +13 -4
- package/dist/heart/session-activity.js +43 -22
- package/dist/heart/session-events.js +1149 -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 +243 -0
- package/dist/heart/start-of-turn-packet.js +345 -0
- package/dist/heart/streaming.js +44 -27
- package/dist/heart/sync-classification.js +176 -0
- package/dist/heart/sync.js +449 -0
- package/dist/heart/target-resolution.js +9 -5
- package/dist/heart/tempo.js +93 -0
- package/dist/heart/temporal-view.js +41 -0
- package/dist/heart/timeouts.js +101 -0
- package/dist/heart/tool-activity-callbacks.js +59 -0
- package/dist/heart/tool-description.js +139 -0
- package/dist/heart/tool-friction.js +55 -0
- package/dist/heart/tool-loop.js +200 -0
- package/dist/heart/turn-context.js +389 -0
- package/dist/heart/{daemon → versioning}/ouro-bot-global-installer.js +6 -5
- package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +1 -1
- package/dist/heart/versioning/ouro-path-installer.js +426 -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 +6 -1
- package/dist/heart/{daemon → versioning}/update-hooks.js +63 -59
- package/dist/mailbox-ui/assets/index-B-461hes.js +61 -0
- package/dist/mailbox-ui/assets/index-BPr5vNuM.css +1 -0
- package/dist/mailbox-ui/index.html +15 -0
- package/dist/mailroom/attention.js +167 -0
- package/dist/mailroom/autonomy.js +209 -0
- package/dist/mailroom/blob-store.js +674 -0
- package/dist/mailroom/body-cache.js +61 -0
- package/dist/mailroom/core.js +720 -0
- package/dist/mailroom/entry.js +160 -0
- package/dist/mailroom/file-store.js +430 -0
- package/dist/mailroom/mbox-import.js +383 -0
- package/dist/mailroom/outbound.js +380 -0
- package/dist/mailroom/policy.js +263 -0
- package/dist/mailroom/reader.js +233 -0
- package/dist/mailroom/search-cache.js +256 -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} +62 -75
- package/dist/mind/embedding-provider.js +60 -0
- package/dist/mind/file-state.js +179 -0
- package/dist/mind/friends/channel.js +39 -0
- package/dist/mind/friends/resolver.js +54 -2
- package/dist/mind/friends/store-file.js +39 -3
- 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 +1011 -123
- 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 +129 -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/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 +963 -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 +178 -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 +295 -0
- package/dist/repertoire/mcp-manager.js +362 -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 +47 -1075
- package/dist/repertoire/tools-bluebubbles.js +1 -0
- package/dist/repertoire/tools-bridge.js +142 -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 +119 -0
- package/dist/repertoire/tools-github.js +1 -7
- package/dist/repertoire/tools-mail.js +1857 -0
- package/dist/repertoire/tools-notes.js +421 -0
- package/dist/repertoire/tools-session.js +750 -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 +604 -0
- package/dist/repertoire/tools-user-profile.js +144 -0
- package/dist/repertoire/tools-vault.js +40 -0
- package/dist/repertoire/tools.js +108 -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 +594 -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/active-turns.js +216 -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 +77 -0
- package/dist/senses/{bluebubbles-inbound-log.js → bluebubbles/inbound-log.js} +20 -3
- package/dist/senses/bluebubbles/index.js +2305 -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 +133 -0
- package/dist/senses/bluebubbles/replay.js +137 -0
- package/dist/senses/{bluebubbles-runtime-state.js → bluebubbles/runtime-state.js} +30 -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 +607 -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 +85 -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 +520 -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 +549 -181
- 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 +387 -98
- package/dist/senses/trust-gate.js +100 -5
- package/dist/senses/voice/elevenlabs.js +125 -0
- package/dist/senses/voice/index.js +22 -0
- package/dist/senses/voice/transcript.js +70 -0
- package/dist/senses/voice/turn.js +85 -0
- package/dist/senses/voice/types.js +2 -0
- package/dist/senses/voice/whisper.js +133 -0
- package/dist/senses/voice-entry.js +80 -0
- package/dist/trips/core.js +138 -0
- package/dist/trips/store.js +146 -0
- package/package.json +38 -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/auth-flow.js +0 -351
- 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 -1177
- 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
|
@@ -37,39 +37,103 @@ exports.discoverLogFiles = discoverLogFiles;
|
|
|
37
37
|
exports.readLastLines = readLastLines;
|
|
38
38
|
exports.formatLogLine = formatLogLine;
|
|
39
39
|
exports.tailLogs = tailLogs;
|
|
40
|
-
const os = __importStar(require("os"));
|
|
41
40
|
const path = __importStar(require("path"));
|
|
41
|
+
const zlib = __importStar(require("zlib"));
|
|
42
42
|
const nerves_1 = require("../../nerves");
|
|
43
43
|
const runtime_1 = require("../../nerves/runtime");
|
|
44
|
+
const identity_1 = require("../identity");
|
|
44
45
|
const LEVEL_COLORS = {
|
|
45
46
|
debug: "\x1b[2m",
|
|
46
47
|
info: "\x1b[36m",
|
|
47
48
|
warn: "\x1b[33m",
|
|
48
49
|
error: "\x1b[31m",
|
|
49
50
|
};
|
|
51
|
+
/**
|
|
52
|
+
* Parse a log filename into a (streamBase, generationRank) tuple.
|
|
53
|
+
*
|
|
54
|
+
* - `daemon.ndjson` → { streamBase: "daemon", rank: 0 } (active, newest)
|
|
55
|
+
* - `daemon.1.ndjson.gz` → { streamBase: "daemon", rank: 1 }
|
|
56
|
+
* - `daemon.5.ndjson.gz` → { streamBase: "daemon", rank: 5 } (oldest)
|
|
57
|
+
* - Anything else → null (not a log file).
|
|
58
|
+
*
|
|
59
|
+
* Higher rank = older. Used to sort so the oldest generation is read first
|
|
60
|
+
* and the active stream is read last, producing chronological output.
|
|
61
|
+
*/
|
|
62
|
+
function parseLogFilename(name) {
|
|
63
|
+
if (name.endsWith(".ndjson.gz")) {
|
|
64
|
+
// e.g. daemon.1.ndjson.gz → base "daemon.1", strip ".gz" then ".ndjson" then ".<n>"
|
|
65
|
+
const withoutGz = name.slice(0, -".gz".length); // daemon.1.ndjson
|
|
66
|
+
const withoutNdjson = withoutGz.slice(0, -".ndjson".length); // daemon.1
|
|
67
|
+
const genMatch = withoutNdjson.match(/^(.+)\.(\d+)$/);
|
|
68
|
+
if (!genMatch)
|
|
69
|
+
return null;
|
|
70
|
+
return { streamBase: genMatch[1], rank: parseInt(genMatch[2], 10) };
|
|
71
|
+
}
|
|
72
|
+
if (name.endsWith(".ndjson")) {
|
|
73
|
+
const withoutNdjson = name.slice(0, -".ndjson".length);
|
|
74
|
+
// Active file: daemon.ndjson → base "daemon", rank 0
|
|
75
|
+
// Legacy numeric gen: daemon.1.ndjson → base "daemon", rank 1 (treat same as gzipped)
|
|
76
|
+
const legacyMatch = withoutNdjson.match(/^(.+)\.(\d+)$/);
|
|
77
|
+
if (legacyMatch) {
|
|
78
|
+
return { streamBase: legacyMatch[1], rank: parseInt(legacyMatch[2], 10) };
|
|
79
|
+
}
|
|
80
|
+
return { streamBase: withoutNdjson, rank: 0 };
|
|
81
|
+
}
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
50
84
|
function discoverLogFiles(options) {
|
|
51
85
|
/* v8 ignore start -- integration: default DI stubs for real OS @preserve */
|
|
52
|
-
const homeDir = options.homeDir ?? os.homedir();
|
|
53
86
|
const existsSync = options.existsSync ?? (() => false);
|
|
54
87
|
const readdirSync = options.readdirSync ?? (() => []);
|
|
55
88
|
/* v8 ignore stop */
|
|
56
|
-
const logDir =
|
|
57
|
-
|
|
89
|
+
const logDir = options.homeDir
|
|
90
|
+
? path.join(options.homeDir, "AgentBundles", `${options.agentName ?? "slugger"}.ouro`, "state", "daemon", "logs")
|
|
91
|
+
: (0, identity_1.getAgentDaemonLogsDir)(options.agentName);
|
|
92
|
+
const entries = [];
|
|
58
93
|
if (existsSync(logDir)) {
|
|
59
94
|
for (const name of readdirSync(logDir)) {
|
|
60
|
-
|
|
95
|
+
const parsed = parseLogFilename(name);
|
|
96
|
+
if (!parsed)
|
|
61
97
|
continue;
|
|
62
98
|
if (options.agentFilter && !name.includes(options.agentFilter))
|
|
63
99
|
continue;
|
|
64
|
-
|
|
100
|
+
entries.push({ name, parsed });
|
|
65
101
|
}
|
|
66
102
|
}
|
|
67
|
-
|
|
103
|
+
// Sort chronologically: for each stream, oldest generation first, active last.
|
|
104
|
+
// Across streams, sort alphabetically by streamBase so output is stable.
|
|
105
|
+
entries.sort((a, b) => {
|
|
106
|
+
if (a.parsed.streamBase !== b.parsed.streamBase) {
|
|
107
|
+
return a.parsed.streamBase < b.parsed.streamBase ? -1 : 1;
|
|
108
|
+
}
|
|
109
|
+
// Same stream: higher rank = older = read first.
|
|
110
|
+
return b.parsed.rank - a.parsed.rank;
|
|
111
|
+
});
|
|
112
|
+
return entries.map((e) => path.join(logDir, e.name));
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Read a log file as a string, transparently handling gzipped rotations.
|
|
116
|
+
*
|
|
117
|
+
* For `.ndjson.gz` files we read the raw bytes via `fs.readFileSync` (ignoring
|
|
118
|
+
* any caller-supplied DI `readFileSync`, since that stub returns a `string`
|
|
119
|
+
* and would corrupt gzip bytes) and then `zlib.gunzipSync` to recover the
|
|
120
|
+
* original text. For plain `.ndjson` files we defer to the DI stub so tests
|
|
121
|
+
* can keep mocking fs.
|
|
122
|
+
*/
|
|
123
|
+
function readNdjsonFileContents(filePath, readFileSync) {
|
|
124
|
+
if (filePath.endsWith(".gz")) {
|
|
125
|
+
// Binary path: tests can mock readFileSync to return a Buffer (typed as
|
|
126
|
+
// string) via `as unknown as string` — we accept both Buffer and Uint8Array.
|
|
127
|
+
const raw = readFileSync(filePath);
|
|
128
|
+
const buf = typeof raw === "string" ? Buffer.from(raw, "binary") : Buffer.from(raw);
|
|
129
|
+
return zlib.gunzipSync(buf).toString("utf-8");
|
|
130
|
+
}
|
|
131
|
+
return readFileSync(filePath, "utf-8");
|
|
68
132
|
}
|
|
69
133
|
function readLastLines(filePath, count, readFileSync) {
|
|
70
134
|
let content;
|
|
71
135
|
try {
|
|
72
|
-
content =
|
|
136
|
+
content = readNdjsonFileContents(filePath, readFileSync);
|
|
73
137
|
}
|
|
74
138
|
catch {
|
|
75
139
|
return [];
|
|
@@ -99,12 +163,17 @@ function tailLogs(options = {}) {
|
|
|
99
163
|
const files = discoverLogFiles(options);
|
|
100
164
|
(0, runtime_1.emitNervesEvent)({ component: "daemon", event: "daemon.log_tailer_started", message: "log tailer started", meta: { fileCount: files.length, follow: !!options.follow } });
|
|
101
165
|
const fileSizes = new Map();
|
|
102
|
-
// Read initial lines
|
|
166
|
+
// Read initial lines from each discovered file (oldest gz → newest gz → active).
|
|
167
|
+
// Gzipped files are historical and never tailed in follow mode.
|
|
103
168
|
for (const file of files) {
|
|
104
169
|
const lines = readLastLines(file, lineCount, readFileSync);
|
|
105
170
|
for (const line of lines) {
|
|
106
171
|
writer(`${formatLogLine(line)}\n`);
|
|
107
172
|
}
|
|
173
|
+
if (file.endsWith(".gz")) {
|
|
174
|
+
// Historical rotation — skip size tracking, never followed.
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
108
177
|
try {
|
|
109
178
|
const content = readFileSync(file, "utf-8");
|
|
110
179
|
fileSizes.set(file, content.length);
|
|
@@ -113,9 +182,10 @@ function tailLogs(options = {}) {
|
|
|
113
182
|
fileSizes.set(file, 0);
|
|
114
183
|
}
|
|
115
184
|
}
|
|
116
|
-
// Follow mode
|
|
185
|
+
// Follow mode: only the active (non-gzipped) stream is watched.
|
|
117
186
|
if (options.follow && watchFile && unwatchFile) {
|
|
118
|
-
|
|
187
|
+
const activeFiles = files.filter((f) => !f.endsWith(".gz"));
|
|
188
|
+
for (const file of activeFiles) {
|
|
119
189
|
watchFile(file, () => {
|
|
120
190
|
let content;
|
|
121
191
|
try {
|
|
@@ -137,7 +207,7 @@ function tailLogs(options = {}) {
|
|
|
137
207
|
});
|
|
138
208
|
}
|
|
139
209
|
return () => {
|
|
140
|
-
for (const file of
|
|
210
|
+
for (const file of activeFiles) {
|
|
141
211
|
unwatchFile(file);
|
|
142
212
|
}
|
|
143
213
|
};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.pruneDaemonLogs = pruneDaemonLogs;
|
|
4
|
+
const fs_1 = require("fs");
|
|
5
|
+
const path_1 = require("path");
|
|
6
|
+
const crypto_1 = require("crypto");
|
|
7
|
+
const nerves_1 = require("../../nerves");
|
|
8
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
9
|
+
const identity_1 = require("../identity");
|
|
10
|
+
function isActiveLogStream(name) {
|
|
11
|
+
if (name.endsWith(".ndjson")) {
|
|
12
|
+
return !/\.\d+\.ndjson$/.test(name);
|
|
13
|
+
}
|
|
14
|
+
if (name.endsWith(".log")) {
|
|
15
|
+
return !/\.\d+\.log$/.test(name);
|
|
16
|
+
}
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
function pruneDaemonLogs(options = {}) {
|
|
20
|
+
/* v8 ignore next -- defensive: tests always pass logsDir to avoid prod paths @preserve */
|
|
21
|
+
const logsDir = options.logsDir ?? (0, identity_1.getAgentDaemonLogsDir)(options.agentName);
|
|
22
|
+
const maxSizeBytes = options.maxSizeBytes ?? nerves_1.DEFAULT_MAX_LOG_SIZE_BYTES;
|
|
23
|
+
const maxGenerations = options.maxGenerations ?? nerves_1.DEFAULT_MAX_GENERATIONS;
|
|
24
|
+
const traceId = (0, crypto_1.randomUUID)();
|
|
25
|
+
(0, runtime_1.emitNervesEvent)({
|
|
26
|
+
component: "nerves",
|
|
27
|
+
event: "nerves.logs_prune_start",
|
|
28
|
+
trace_id: traceId,
|
|
29
|
+
message: "pruning daemon logs",
|
|
30
|
+
meta: { logsDir, maxSizeBytes, maxGenerations },
|
|
31
|
+
});
|
|
32
|
+
let completed = false;
|
|
33
|
+
try {
|
|
34
|
+
if (!(0, fs_1.existsSync)(logsDir)) {
|
|
35
|
+
completed = true;
|
|
36
|
+
(0, runtime_1.emitNervesEvent)({
|
|
37
|
+
component: "nerves",
|
|
38
|
+
event: "nerves.logs_prune_end",
|
|
39
|
+
trace_id: traceId,
|
|
40
|
+
message: "daemon logs dir does not exist",
|
|
41
|
+
meta: { logsDir, filesCompacted: 0, bytesFreed: 0 },
|
|
42
|
+
});
|
|
43
|
+
return { filesCompacted: 0, bytesFreed: 0 };
|
|
44
|
+
}
|
|
45
|
+
let filesCompacted = 0;
|
|
46
|
+
let bytesFreed = 0;
|
|
47
|
+
// Enumerate and rotate each active structured stream plus legacy launchd
|
|
48
|
+
// .log streams. We explicitly skip .gz and other files — only the active
|
|
49
|
+
// stream can be a rotation candidate. Legacy generation files are handled
|
|
50
|
+
// inside rotateIfNeeded's generation-shift step, so we skip them here to
|
|
51
|
+
// avoid double-rotating.
|
|
52
|
+
for (const name of (0, fs_1.readdirSync)(logsDir)) {
|
|
53
|
+
if (!isActiveLogStream(name))
|
|
54
|
+
continue;
|
|
55
|
+
const filePath = (0, path_1.join)(logsDir, name);
|
|
56
|
+
let sizeBefore;
|
|
57
|
+
try {
|
|
58
|
+
sizeBefore = (0, fs_1.statSync)(filePath).size;
|
|
59
|
+
/* v8 ignore start -- defensive: file disappears between readdir and stat @preserve */
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
/* v8 ignore stop */
|
|
65
|
+
if (sizeBefore < maxSizeBytes)
|
|
66
|
+
continue;
|
|
67
|
+
// rotateIfNeeded returns true because we pre-checked sizeBefore >=
|
|
68
|
+
// maxSizeBytes above. The false branch of `if (rotated)` is defensive
|
|
69
|
+
// and unreachable under normal flow; we skip it from coverage so a
|
|
70
|
+
// future refactor that weakens the pre-check still reports correct
|
|
71
|
+
// counts without needing a contrived test.
|
|
72
|
+
const rotated = (0, nerves_1.rotateIfNeeded)(filePath, {
|
|
73
|
+
maxSizeBytes,
|
|
74
|
+
maxGenerations,
|
|
75
|
+
compress: true,
|
|
76
|
+
});
|
|
77
|
+
/* v8 ignore next 3 -- defensive: pre-check guarantees rotated=true @preserve */
|
|
78
|
+
if (!rotated) {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
filesCompacted += 1;
|
|
82
|
+
bytesFreed += sizeBefore;
|
|
83
|
+
}
|
|
84
|
+
completed = true;
|
|
85
|
+
(0, runtime_1.emitNervesEvent)({
|
|
86
|
+
component: "nerves",
|
|
87
|
+
event: "nerves.logs_prune_end",
|
|
88
|
+
trace_id: traceId,
|
|
89
|
+
message: "daemon logs pruned",
|
|
90
|
+
meta: { logsDir, filesCompacted, bytesFreed },
|
|
91
|
+
});
|
|
92
|
+
return { filesCompacted, bytesFreed };
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
/* v8 ignore next -- defensive: completed=true only reached after try returns @preserve */
|
|
96
|
+
if (!completed) {
|
|
97
|
+
/* v8 ignore next -- defensive: rotation always throws real Errors @preserve */
|
|
98
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
99
|
+
(0, runtime_1.emitNervesEvent)({
|
|
100
|
+
component: "nerves",
|
|
101
|
+
event: "nerves.logs_prune_error",
|
|
102
|
+
trace_id: traceId,
|
|
103
|
+
level: "error",
|
|
104
|
+
message: "daemon logs prune failed",
|
|
105
|
+
meta: { logsDir, error: reason },
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
throw err;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -0,0 +1,297 @@
|
|
|
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.parseMcpStatusText = parseMcpStatusText;
|
|
37
|
+
exports.runMcpStatusCanary = runMcpStatusCanary;
|
|
38
|
+
exports.formatMcpStatusCanaryResult = formatMcpStatusCanaryResult;
|
|
39
|
+
exports.createMcpStatusCanaryProbe = createMcpStatusCanaryProbe;
|
|
40
|
+
const child_process_1 = require("child_process");
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
43
|
+
const DEFAULT_CANARY_TIMEOUT_MS = 10_000;
|
|
44
|
+
const MCP_PROTOCOL_VERSION = "2024-11-05";
|
|
45
|
+
function defaultCommandArgs(agent, socketPath) {
|
|
46
|
+
const entryPath = path.join(__dirname, "ouro-bot-entry.js");
|
|
47
|
+
return [
|
|
48
|
+
entryPath,
|
|
49
|
+
"mcp-serve",
|
|
50
|
+
"--agent",
|
|
51
|
+
agent,
|
|
52
|
+
...(socketPath ? ["--socket", socketPath] : []),
|
|
53
|
+
];
|
|
54
|
+
}
|
|
55
|
+
function responseText(response) {
|
|
56
|
+
const result = response.result;
|
|
57
|
+
if (!result || typeof result !== "object" || Array.isArray(result))
|
|
58
|
+
return JSON.stringify(response);
|
|
59
|
+
const content = result.content;
|
|
60
|
+
if (!Array.isArray(content))
|
|
61
|
+
return JSON.stringify(response);
|
|
62
|
+
const first = content[0];
|
|
63
|
+
if (!first || typeof first !== "object" || Array.isArray(first))
|
|
64
|
+
return JSON.stringify(response);
|
|
65
|
+
const text = first.text;
|
|
66
|
+
return typeof text === "string" ? text : JSON.stringify(response);
|
|
67
|
+
}
|
|
68
|
+
function parseFields(line) {
|
|
69
|
+
const parsed = {};
|
|
70
|
+
for (const segment of line.split("\t")) {
|
|
71
|
+
const idx = segment.indexOf("=");
|
|
72
|
+
if (idx <= 0)
|
|
73
|
+
continue;
|
|
74
|
+
parsed[segment.slice(0, idx)] = segment.slice(idx + 1);
|
|
75
|
+
}
|
|
76
|
+
return parsed;
|
|
77
|
+
}
|
|
78
|
+
function parseMcpStatusText(text) {
|
|
79
|
+
const daemon = {};
|
|
80
|
+
const senses = {};
|
|
81
|
+
for (const line of text.split(/\r?\n/)) {
|
|
82
|
+
const trimmed = line.trim();
|
|
83
|
+
if (!trimmed)
|
|
84
|
+
continue;
|
|
85
|
+
if (trimmed.startsWith("daemon=")) {
|
|
86
|
+
Object.assign(daemon, parseFields(trimmed));
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (!trimmed.startsWith("sense="))
|
|
90
|
+
continue;
|
|
91
|
+
const fields = parseFields(trimmed);
|
|
92
|
+
const sense = fields.sense;
|
|
93
|
+
if (!sense)
|
|
94
|
+
continue;
|
|
95
|
+
const [name, status = "unknown"] = sense.split(":");
|
|
96
|
+
senses[name] = { ...fields, name, status };
|
|
97
|
+
}
|
|
98
|
+
return { daemon, senses, raw: text };
|
|
99
|
+
}
|
|
100
|
+
function validateMcpStatus(parsed, requiredSenses, options = {}) {
|
|
101
|
+
const failures = [];
|
|
102
|
+
if (parsed.daemon.daemon !== "running") {
|
|
103
|
+
failures.push(`daemon=${parsed.daemon.daemon ?? "missing"}`);
|
|
104
|
+
}
|
|
105
|
+
if (!options.ignoreOverviewHealth && parsed.daemon.health !== "ok") {
|
|
106
|
+
failures.push(`health=${parsed.daemon.health ?? "missing"}`);
|
|
107
|
+
}
|
|
108
|
+
if (parsed.daemon.daemonVersion &&
|
|
109
|
+
parsed.daemon.mcpVersion &&
|
|
110
|
+
parsed.daemon.daemonVersion !== parsed.daemon.mcpVersion) {
|
|
111
|
+
failures.push(`version mismatch daemon=${parsed.daemon.daemonVersion} mcp=${parsed.daemon.mcpVersion}`);
|
|
112
|
+
}
|
|
113
|
+
for (const [sense, row] of Object.entries(parsed.senses)) {
|
|
114
|
+
if (row.status === "disabled")
|
|
115
|
+
continue;
|
|
116
|
+
if (row.status === "running" || row.status === "interactive")
|
|
117
|
+
continue;
|
|
118
|
+
failures.push(`sense=${sense}:${row.status}`);
|
|
119
|
+
}
|
|
120
|
+
for (const sense of requiredSenses) {
|
|
121
|
+
const row = parsed.senses[sense];
|
|
122
|
+
if (!row) {
|
|
123
|
+
failures.push(`required sense missing: ${sense}`);
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
if (row.status !== "running" && row.status !== "interactive") {
|
|
127
|
+
failures.push(`required sense unhealthy: ${sense}:${row.status}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const senseSummary = Object.values(parsed.senses)
|
|
131
|
+
.map((row) => `${row.name}:${row.status}`)
|
|
132
|
+
.join(",");
|
|
133
|
+
const summary = failures.length === 0
|
|
134
|
+
? `mcp canary ok: daemon=${parsed.daemon.daemon} health=${parsed.daemon.health}${options.ignoreOverviewHealth ? " (overview ignored)" : ""} senses=${senseSummary}`
|
|
135
|
+
: `mcp canary failed: ${failures.join("; ")}`;
|
|
136
|
+
return {
|
|
137
|
+
ok: failures.length === 0,
|
|
138
|
+
summary,
|
|
139
|
+
details: failures.length === 0 ? [parsed.raw] : [...failures, parsed.raw],
|
|
140
|
+
parsed,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
async function runMcpStatusCanary(options) {
|
|
144
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_CANARY_TIMEOUT_MS;
|
|
145
|
+
/* v8 ignore next -- default spawn is exercised by live canaries, while unit tests inject a fake child @preserve */
|
|
146
|
+
const spawnImpl = options.spawnImpl ?? child_process_1.spawn;
|
|
147
|
+
const command = options.command ?? process.execPath;
|
|
148
|
+
const commandArgs = options.commandArgs ?? defaultCommandArgs(options.agent, options.socketPath);
|
|
149
|
+
const requiredSenses = options.requiredSenses ?? [];
|
|
150
|
+
(0, runtime_1.emitNervesEvent)({
|
|
151
|
+
component: "daemon",
|
|
152
|
+
event: "daemon.mcp_canary_start",
|
|
153
|
+
message: "starting MCP status canary",
|
|
154
|
+
meta: {
|
|
155
|
+
agent: options.agent,
|
|
156
|
+
command,
|
|
157
|
+
commandArgs,
|
|
158
|
+
timeoutMs,
|
|
159
|
+
requiredSenses,
|
|
160
|
+
ignoreOverviewHealth: options.ignoreOverviewHealth === true,
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
const child = spawnImpl(command, commandArgs, { stdio: ["pipe", "pipe", "pipe"] });
|
|
164
|
+
let buffer = "";
|
|
165
|
+
let stderr = "";
|
|
166
|
+
const pending = new Map();
|
|
167
|
+
function cleanup() {
|
|
168
|
+
for (const [, request] of pending) {
|
|
169
|
+
clearTimeout(request.timer);
|
|
170
|
+
}
|
|
171
|
+
pending.clear();
|
|
172
|
+
if (!child.killed)
|
|
173
|
+
child.kill();
|
|
174
|
+
}
|
|
175
|
+
function failAll(error) {
|
|
176
|
+
for (const [, request] of pending) {
|
|
177
|
+
clearTimeout(request.timer);
|
|
178
|
+
request.reject(error);
|
|
179
|
+
}
|
|
180
|
+
pending.clear();
|
|
181
|
+
}
|
|
182
|
+
child.stderr?.setEncoding("utf8");
|
|
183
|
+
child.stderr?.on("data", (chunk) => {
|
|
184
|
+
stderr += chunk.toString();
|
|
185
|
+
});
|
|
186
|
+
child.stdout?.setEncoding("utf8");
|
|
187
|
+
child.stdout?.on("data", (chunk) => {
|
|
188
|
+
buffer += chunk.toString();
|
|
189
|
+
for (;;) {
|
|
190
|
+
const idx = buffer.indexOf("\n");
|
|
191
|
+
if (idx === -1)
|
|
192
|
+
break;
|
|
193
|
+
const line = buffer.slice(0, idx).trim();
|
|
194
|
+
buffer = buffer.slice(idx + 1);
|
|
195
|
+
if (!line)
|
|
196
|
+
continue;
|
|
197
|
+
let response;
|
|
198
|
+
try {
|
|
199
|
+
response = JSON.parse(line);
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
failAll(new Error(`MCP canary received malformed JSON: ${line}`));
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
const id = typeof response.id === "number" ? response.id : null;
|
|
206
|
+
if (id === null)
|
|
207
|
+
continue;
|
|
208
|
+
const request = pending.get(id);
|
|
209
|
+
if (!request)
|
|
210
|
+
continue;
|
|
211
|
+
pending.delete(id);
|
|
212
|
+
clearTimeout(request.timer);
|
|
213
|
+
request.resolve(response);
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
child.on("error", (error) => failAll(error));
|
|
217
|
+
child.on("close", (code, signal) => {
|
|
218
|
+
if (pending.size === 0)
|
|
219
|
+
return;
|
|
220
|
+
failAll(new Error(`MCP canary process closed before response code=${code} signal=${signal ?? "none"} stderr=${stderr.trim()}`));
|
|
221
|
+
});
|
|
222
|
+
let nextId = 1;
|
|
223
|
+
function request(method, params) {
|
|
224
|
+
return new Promise((resolve, reject) => {
|
|
225
|
+
if (!child.stdin?.writable) {
|
|
226
|
+
reject(new Error("MCP canary stdin is not writable"));
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
const id = nextId++;
|
|
230
|
+
const timer = setTimeout(() => {
|
|
231
|
+
pending.delete(id);
|
|
232
|
+
reject(new Error(`MCP canary timed out waiting for ${method}; stderr=${stderr.trim()}`));
|
|
233
|
+
}, timeoutMs);
|
|
234
|
+
pending.set(id, { resolve, reject, timer });
|
|
235
|
+
child.stdin.write(JSON.stringify({ jsonrpc: "2.0", id, method, params }) + "\n");
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
try {
|
|
239
|
+
await request("initialize", {
|
|
240
|
+
protocolVersion: MCP_PROTOCOL_VERSION,
|
|
241
|
+
capabilities: {},
|
|
242
|
+
clientInfo: { name: "ouro-mcp-canary", version: "1.0" },
|
|
243
|
+
});
|
|
244
|
+
child.stdin?.write(JSON.stringify({ jsonrpc: "2.0", method: "initialized" }) + "\n");
|
|
245
|
+
const statusResponse = await request("tools/call", {
|
|
246
|
+
name: "status",
|
|
247
|
+
arguments: {},
|
|
248
|
+
});
|
|
249
|
+
const result = statusResponse.result;
|
|
250
|
+
if (result && typeof result === "object" && !Array.isArray(result) && result.isError === true) {
|
|
251
|
+
throw new Error(responseText(statusResponse));
|
|
252
|
+
}
|
|
253
|
+
const parsed = parseMcpStatusText(responseText(statusResponse));
|
|
254
|
+
const canary = validateMcpStatus(parsed, requiredSenses, {
|
|
255
|
+
ignoreOverviewHealth: options.ignoreOverviewHealth,
|
|
256
|
+
});
|
|
257
|
+
(0, runtime_1.emitNervesEvent)({
|
|
258
|
+
component: "daemon",
|
|
259
|
+
event: canary.ok ? "daemon.mcp_canary_end" : "daemon.mcp_canary_error",
|
|
260
|
+
level: canary.ok ? "info" : "error",
|
|
261
|
+
message: canary.summary,
|
|
262
|
+
meta: { agent: options.agent, ok: canary.ok },
|
|
263
|
+
});
|
|
264
|
+
return canary;
|
|
265
|
+
}
|
|
266
|
+
catch (error) {
|
|
267
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
268
|
+
(0, runtime_1.emitNervesEvent)({
|
|
269
|
+
component: "daemon",
|
|
270
|
+
event: "daemon.mcp_canary_error",
|
|
271
|
+
level: "error",
|
|
272
|
+
message: "MCP status canary failed",
|
|
273
|
+
meta: { agent: options.agent, reason },
|
|
274
|
+
});
|
|
275
|
+
return { ok: false, summary: `mcp canary failed: ${reason}`, details: [reason] };
|
|
276
|
+
}
|
|
277
|
+
finally {
|
|
278
|
+
child.stdin?.end();
|
|
279
|
+
cleanup();
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
function formatMcpStatusCanaryResult(result) {
|
|
283
|
+
return [
|
|
284
|
+
result.ok ? "mcp canary: ok" : "mcp canary: failed",
|
|
285
|
+
result.summary,
|
|
286
|
+
...result.details.map((line) => ` ${line}`),
|
|
287
|
+
].join("\n");
|
|
288
|
+
}
|
|
289
|
+
function createMcpStatusCanaryProbe(options) {
|
|
290
|
+
return {
|
|
291
|
+
name: `mcp-canary:${options.agent}`,
|
|
292
|
+
check: async () => {
|
|
293
|
+
const result = await runMcpStatusCanary(options);
|
|
294
|
+
return { ok: result.ok, detail: result.summary };
|
|
295
|
+
},
|
|
296
|
+
};
|
|
297
|
+
}
|
|
@@ -35,9 +35,9 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.FileMessageRouter = void 0;
|
|
37
37
|
const fs = __importStar(require("fs"));
|
|
38
|
-
const os = __importStar(require("os"));
|
|
39
38
|
const path = __importStar(require("path"));
|
|
40
39
|
const runtime_1 = require("../../nerves/runtime");
|
|
40
|
+
const identity_1 = require("../identity");
|
|
41
41
|
function messageId(nowIso) {
|
|
42
42
|
return `msg-${nowIso.replace(/[^0-9]/g, "")}`;
|
|
43
43
|
}
|
|
@@ -45,7 +45,7 @@ class FileMessageRouter {
|
|
|
45
45
|
baseDir;
|
|
46
46
|
now;
|
|
47
47
|
constructor(options = {}) {
|
|
48
|
-
this.baseDir = options.baseDir ??
|
|
48
|
+
this.baseDir = options.baseDir ?? (0, identity_1.getAgentMessagesRoot)();
|
|
49
49
|
this.now = options.now ?? (() => new Date().toISOString());
|
|
50
50
|
fs.mkdirSync(this.baseDir, { recursive: true });
|
|
51
51
|
}
|