@ouro.bot/cli 0.1.0-alpha.74 → 0.1.0-alpha.740
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +154 -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/assets/bluebubbles-host +264 -0
- package/changelog.json +4724 -16
- package/deploy/unraid/Dockerfile +43 -0
- package/deploy/unraid/README.txt +1623 -0
- package/deploy/unraid/audit-container-spec.sh +4 -0
- package/deploy/unraid/container-runtime.json +1 -0
- package/deploy/unraid/sanctuary-acceptance-adapter.sh +37 -0
- package/deploy/unraid/sanctuary-acceptance-contract.json +200 -0
- package/deploy/unraid/sanctuary-acceptance-harness.sh +23 -0
- package/deploy/unraid/sanctuary-deployment-target.mjs +709 -0
- package/deploy/unraid/sanctuary-safe-rename.py +24 -0
- package/deploy/unraid/sanctuary-unit16-host-broker.mjs +1796 -0
- package/deploy/unraid/sanctuary-unit16-run.sh +520 -0
- package/deploy/unraid/sanctuary-unit18-target-audit.sh +27 -0
- package/deploy/unraid/sanctuary.ouro/agent.json +24 -0
- package/deploy/unraid/sanctuary.ouro/arc/README.md +3 -0
- package/deploy/unraid/sanctuary.ouro/bundle-meta.json +5 -0
- package/deploy/unraid/sanctuary.ouro/habits/sanctuary-health.md +8 -0
- package/deploy/unraid/sanctuary.ouro/provider-readiness.json +18 -0
- package/deploy/unraid/sanctuary.ouro/psyche/ASPIRATIONS.md +3 -0
- package/deploy/unraid/sanctuary.ouro/psyche/IDENTITY.md +3 -0
- package/deploy/unraid/sanctuary.ouro/psyche/LORE.md +3 -0
- package/deploy/unraid/sanctuary.ouro/psyche/SOUL.md +5 -0
- package/deploy/unraid/sanctuary.ouro/psyche/TACIT.md +7 -0
- package/deploy/unraid/sanctuary.ouro/tool-profiles.json +21 -0
- package/deploy/unraid/sanctuary.xml +25 -0
- package/dist/a2a/card.js +57 -0
- package/dist/a2a/client.js +180 -0
- package/dist/a2a/config.js +50 -0
- package/dist/a2a/delegation-stores.js +56 -0
- package/dist/a2a/did-resolution.js +93 -0
- package/dist/a2a/identity.js +170 -0
- package/dist/a2a/inbound-share.js +107 -0
- package/dist/a2a/mission-result-wire.js +196 -0
- package/dist/a2a/onboarding.js +115 -0
- package/dist/a2a/pin-store.js +132 -0
- package/dist/a2a/seen-ledger.js +120 -0
- package/dist/a2a/server.js +604 -0
- package/dist/a2a/task-store.js +69 -0
- package/dist/a2a/transport.js +45 -0
- package/dist/a2a/types.js +3 -0
- package/dist/arc/attention-types.js +8 -0
- package/dist/arc/cares.js +144 -0
- package/dist/arc/episodes.js +118 -0
- package/dist/arc/evolution.js +490 -0
- package/dist/arc/flight-recorder.js +1034 -0
- package/dist/arc/intentions.js +134 -0
- package/dist/arc/json-store.js +117 -0
- package/dist/arc/obligations.js +348 -0
- package/dist/arc/packets.js +336 -0
- package/dist/arc/presence.js +185 -0
- package/dist/arc/task-lifecycle.js +57 -0
- package/dist/commerce/store.js +755 -0
- package/dist/commerce/types.js +3 -0
- package/dist/heart/active-work.js +874 -43
- package/dist/heart/agent-entry.js +190 -3
- package/dist/heart/approval-files.js +113 -0
- package/dist/heart/approval-store.js +950 -0
- 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/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 +495 -0
- package/dist/heart/autonomy-budget.js +477 -0
- package/dist/heart/awaiting/await-alert.js +146 -0
- package/dist/heart/awaiting/await-expiry.js +108 -0
- package/dist/heart/awaiting/await-loader.js +91 -0
- package/dist/heart/awaiting/await-parser.js +141 -0
- package/dist/heart/awaiting/await-runtime-state.js +100 -0
- package/dist/heart/awaiting/await-scheduler.js +382 -0
- package/dist/heart/background-operations.js +281 -0
- package/dist/heart/bridges/manager.js +137 -17
- package/dist/heart/bridges/store.js +14 -2
- package/dist/heart/bundle-state.js +168 -0
- package/dist/heart/commitments.js +135 -0
- package/dist/heart/config-registry.js +331 -0
- package/dist/heart/config.js +128 -129
- package/dist/heart/context-loss-gauntlet.js +354 -0
- package/dist/heart/context-loss-sentinel.js +1155 -0
- package/dist/heart/core.js +1759 -331
- package/dist/heart/cross-chat-delivery.js +8 -22
- package/dist/heart/daemon/agent-config-check.js +451 -0
- package/dist/heart/daemon/agent-discovery.js +185 -3
- package/dist/heart/daemon/agent-service.js +542 -0
- package/dist/heart/daemon/agentic-repair.js +547 -0
- package/dist/heart/daemon/await-private-wake.js +59 -0
- package/dist/heart/daemon/bluebubbles-cli-integration.js +71 -0
- package/dist/heart/daemon/bluebubbles-health-diagnostics.js +122 -0
- package/dist/heart/daemon/bluebubbles-host-protocol.js +433 -0
- package/dist/heart/daemon/bluebubbles-host.js +273 -0
- package/dist/heart/daemon/boot-sync-probe.js +197 -0
- package/dist/heart/daemon/cadence.js +180 -0
- package/dist/heart/daemon/cli-defaults.js +832 -0
- package/dist/heart/daemon/cli-desk.js +322 -0
- package/dist/heart/daemon/cli-exec.js +8692 -0
- package/dist/heart/daemon/cli-help.js +710 -0
- package/dist/heart/daemon/cli-parse.js +2714 -0
- package/dist/heart/daemon/cli-render-doctor.js +57 -0
- package/dist/heart/daemon/cli-render.js +777 -0
- package/dist/heart/daemon/cli-types.js +8 -0
- package/dist/heart/daemon/connect-bay.js +323 -0
- package/dist/heart/daemon/container-credential-bootstrap.js +273 -0
- package/dist/heart/daemon/container-healthcheck.js +94 -0
- package/dist/heart/daemon/container-runtime.js +84 -0
- package/dist/heart/daemon/container-spec-auditor-main.js +156 -0
- package/dist/heart/daemon/container-spec-auditor.js +156 -0
- package/dist/heart/daemon/daemon-bootstrap-startup.js +41 -0
- package/dist/heart/daemon/daemon-cli.js +29 -2144
- package/dist/heart/daemon/daemon-entry.js +930 -10
- package/dist/heart/daemon/daemon-health.js +186 -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 +1698 -88
- package/dist/heart/daemon/dns-workflow.js +394 -0
- package/dist/heart/daemon/doctor-types.js +8 -0
- package/dist/heart/daemon/doctor.js +1755 -0
- package/dist/heart/daemon/freshness.js +345 -0
- package/dist/heart/daemon/habit-private-wake.js +61 -0
- package/dist/heart/daemon/health-monitor.js +142 -11
- package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
- package/dist/heart/daemon/hooks/bundle-meta.js +135 -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 +78 -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 +79 -10
- package/dist/heart/daemon/logs-prune.js +132 -0
- package/dist/heart/daemon/mcp-canary.js +524 -0
- package/dist/heart/daemon/message-router.js +45 -5
- package/dist/heart/daemon/migrate-to-desk.js +848 -0
- package/dist/heart/daemon/os-cron-deps.js +135 -0
- package/dist/heart/daemon/os-cron.js +22 -14
- package/dist/heart/daemon/ouro-bot-entry.js +4 -2
- package/dist/heart/daemon/ouro-entry.js +3 -1
- package/dist/heart/daemon/plugin-cli.js +432 -0
- package/dist/heart/daemon/process-manager.js +624 -61
- package/dist/heart/daemon/provider-discovery.js +137 -0
- package/dist/heart/daemon/provider-ping-progress.js +83 -0
- package/dist/heart/daemon/prunable-bundle.js +144 -0
- package/dist/heart/daemon/pulse.js +511 -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 +47 -14
- package/dist/heart/daemon/runtime-metadata.js +2 -30
- package/dist/heart/daemon/runtime-mode.js +13 -2
- package/dist/heart/daemon/safe-mode.js +161 -0
- package/dist/heart/daemon/sanctuary-acceptance-adapter.js +2365 -0
- package/dist/heart/daemon/sanctuary-acceptance-harness.js +1624 -0
- package/dist/heart/daemon/sanctuary-acceptance-marker.js +267 -0
- package/dist/heart/daemon/sanctuary-acceptance-scenarios.js +985 -0
- package/dist/heart/daemon/sanctuary-scheduler-liveness.js +227 -0
- package/dist/heart/daemon/sanctuary-scheduler-origin.js +126 -0
- package/dist/heart/daemon/sense-manager.js +689 -48
- package/dist/heart/daemon/session-id-resolver.js +131 -0
- package/dist/heart/daemon/skill-management-installer.js +1 -1
- package/dist/heart/daemon/socket-client.js +180 -12
- package/dist/heart/daemon/stale-bundle-prune.js +113 -0
- package/dist/heart/daemon/startup-tui.js +338 -0
- package/dist/heart/daemon/supercronic-supervisor.js +267 -0
- package/dist/heart/daemon/task-scheduler.js +117 -39
- package/dist/heart/daemon/terminal-ui.js +499 -0
- package/dist/heart/daemon/thoughts.js +255 -43
- package/dist/heart/daemon/up-progress.js +366 -0
- package/dist/heart/daemon/vault-items.js +56 -0
- package/dist/heart/delegation.js +1 -4
- package/dist/heart/external-events/router.js +121 -0
- package/dist/heart/habits/habit-cancel.js +810 -0
- package/dist/heart/habits/habit-lifecycle.js +1327 -0
- package/dist/heart/habits/habit-migration.js +194 -0
- package/dist/heart/habits/habit-parser.js +278 -0
- package/dist/heart/habits/habit-runtime-state.js +93 -0
- package/dist/heart/habits/habit-scheduler.js +653 -0
- package/dist/heart/habits/habit-session-summary.js +318 -0
- package/dist/heart/habits/habit-session.js +636 -0
- package/dist/heart/{daemon → hatch}/hatch-flow.js +55 -62
- 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 +53 -18
- package/dist/heart/identity.js +184 -59
- package/dist/heart/kept-notes.js +289 -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 +98 -0
- package/dist/heart/mailbox/mailbox-http-response.js +7 -0
- package/dist/heart/mailbox/mailbox-http-routes.js +398 -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 +39 -0
- package/dist/heart/mailbox/mailbox-types.js +27 -0
- package/dist/heart/mailbox/mailbox-view.js +197 -0
- package/dist/heart/mailbox/readers/agent-machine.js +428 -0
- package/dist/heart/mailbox/readers/continuity-readers.js +329 -0
- package/dist/heart/mailbox/readers/mail.js +375 -0
- package/dist/heart/mailbox/readers/runtime-readers.js +823 -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 +696 -0
- package/dist/heart/migrate-config.js +100 -0
- package/dist/heart/model-capabilities.js +11 -0
- package/dist/heart/orientation-frame.js +345 -0
- package/dist/heart/platform.js +81 -0
- package/dist/heart/primitives.js +1 -1
- package/dist/heart/private-runtime/decision-renderer.js +156 -0
- package/dist/heart/private-runtime/index.js +18 -0
- package/dist/heart/private-runtime/ledger.js +218 -0
- package/dist/heart/private-runtime/policy.js +214 -0
- package/dist/heart/private-runtime/types.js +2 -0
- package/dist/heart/progress-story.js +1 -1
- package/dist/heart/provider-attempt.js +137 -0
- package/dist/heart/provider-binding-resolver.js +273 -0
- package/dist/heart/provider-credentials.js +449 -0
- package/dist/heart/provider-failover.js +311 -0
- package/dist/heart/provider-models.js +89 -0
- package/dist/heart/provider-ping.js +265 -0
- package/dist/heart/provider-readiness-cache.js +119 -0
- package/dist/heart/provider-visibility.js +188 -0
- package/dist/heart/providers/anthropic-token.js +131 -0
- package/dist/heart/providers/anthropic.js +173 -60
- package/dist/heart/providers/azure.js +23 -11
- package/dist/heart/providers/error-classification.js +127 -0
- package/dist/heart/providers/github-copilot.js +34 -38
- package/dist/heart/providers/minimax-vlm.js +189 -0
- package/dist/heart/providers/minimax.js +26 -8
- package/dist/heart/providers/openai-codex-token.js +349 -0
- package/dist/heart/providers/openai-codex.js +55 -40
- package/dist/heart/providers/openai-compatible.js +177 -0
- package/dist/heart/run-ledger.js +247 -0
- package/dist/heart/runtime-capability-check.js +170 -0
- package/dist/heart/runtime-credentials.js +607 -0
- package/dist/heart/runtime-cwd.js +87 -0
- package/dist/heart/sense-truth.js +22 -4
- package/dist/heart/session-activity.js +90 -24
- package/dist/heart/session-events.js +1300 -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 +133 -0
- package/dist/heart/start-of-turn-packet.js +368 -0
- package/dist/heart/streaming.js +604 -207
- package/dist/heart/structured-output.js +196 -0
- package/dist/heart/sync-classification.js +176 -0
- package/dist/heart/sync.js +449 -0
- package/dist/heart/target-resolution.js +11 -7
- 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-approval.js +340 -0
- package/dist/heart/tool-description.js +155 -0
- package/dist/heart/tool-friction.js +55 -0
- package/dist/heart/tool-loop.js +200 -0
- package/dist/heart/turn-context.js +480 -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 +432 -0
- package/dist/heart/versioning/ouro-recovery-launcher.js +76 -0
- package/dist/heart/versioning/ouro-version-manager.js +409 -0
- package/dist/heart/{daemon → versioning}/staged-restart.js +40 -8
- package/dist/heart/{daemon → versioning}/update-checker.js +6 -1
- package/dist/heart/versioning/update-hooks.js +154 -0
- package/dist/heart/versioning/version-intent.js +114 -0
- package/dist/heart/work-card.js +380 -0
- package/dist/mailbox-ui/assets/index-Du_9G9WO.css +1 -0
- package/dist/mailbox-ui/assets/index-J8zDwHmE.js +1 -0
- package/dist/mailbox-ui/assets/vendor-CcN1XpQ9.js +61 -0
- package/dist/mailbox-ui/index.html +16 -0
- package/dist/mailroom/attention.js +167 -0
- package/dist/mailroom/autonomy.js +209 -0
- package/dist/mailroom/blob-store.js +889 -0
- package/dist/mailroom/body-cache.js +61 -0
- package/dist/mailroom/cache-sync-cli.js +58 -0
- package/dist/mailroom/core.js +803 -0
- package/dist/mailroom/entry.js +160 -0
- package/dist/mailroom/file-store.js +568 -0
- package/dist/mailroom/hosted-cache-sync.js +384 -0
- package/dist/mailroom/mbox-import.js +393 -0
- package/dist/mailroom/migration.js +164 -0
- package/dist/mailroom/outbound.js +380 -0
- package/dist/mailroom/policy.js +263 -0
- package/dist/mailroom/reader.js +292 -0
- package/dist/mailroom/search-cache.js +513 -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 +24 -2
- package/dist/mind/context.js +273 -111
- package/dist/mind/desk-section.js +362 -0
- package/dist/mind/diary-integrity.js +60 -0
- package/dist/mind/{memory.js → diary.js} +68 -77
- package/dist/mind/embedding-provider.js +60 -0
- package/dist/mind/file-state.js +179 -0
- package/dist/mind/{associative-recall.js → note-search.js} +47 -58
- package/dist/mind/obligation-steering.js +227 -0
- package/dist/mind/pending.js +13 -8
- package/dist/mind/prompt-budget.js +479 -0
- package/dist/mind/prompt-refresh.js +3 -2
- package/dist/mind/prompt.js +1067 -172
- package/dist/mind/provenance-trust.js +26 -0
- package/dist/mind/record-paths.js +312 -0
- package/dist/mind/scrutiny.js +173 -0
- package/dist/mind/session-transaction.js +379 -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 +139 -5
- package/dist/nerves/coverage/run-artifacts.js +55 -35
- package/dist/nerves/coverage/source-scanner.js +4 -4
- package/dist/nerves/event-buffer.js +111 -0
- package/dist/nerves/index.js +293 -9
- 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 +159 -0
- package/dist/nerves/runtime.js +17 -1
- package/dist/repertoire/ado-client.js +15 -56
- package/dist/repertoire/ado-semantic.js +16 -10
- package/dist/repertoire/api-client.js +97 -0
- package/dist/repertoire/bitwarden-store.js +1045 -0
- package/dist/repertoire/bundle-templates.js +71 -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 +331 -0
- package/dist/repertoire/coding/feedback.js +218 -30
- package/dist/repertoire/coding/index.js +15 -3
- package/dist/repertoire/coding/manager.js +166 -10
- package/dist/repertoire/coding/spawner.js +58 -11
- package/dist/repertoire/coding/tools.js +242 -14
- package/dist/repertoire/coding/workbench-client.js +272 -0
- package/dist/repertoire/coding/workbench-manager.js +346 -0
- 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/desk/classifier.js +362 -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 +812 -18
- package/dist/repertoire/mcp-client.js +44 -3
- package/dist/repertoire/mcp-manager.js +259 -20
- package/dist/repertoire/mcp-tools.js +83 -0
- package/dist/repertoire/plugin-mcp.js +175 -0
- package/dist/repertoire/plugins.js +253 -0
- package/dist/repertoire/shell-sessions.js +133 -0
- package/dist/repertoire/skills.js +48 -4
- package/dist/repertoire/stripe-client.js +131 -0
- package/dist/repertoire/tool-arguments.js +143 -0
- package/dist/repertoire/tool-results.js +29 -0
- package/dist/repertoire/tools-a2a.js +699 -0
- package/dist/repertoire/tools-attachments.js +317 -0
- package/dist/repertoire/tools-awaiting.js +372 -0
- package/dist/repertoire/tools-base.js +67 -1095
- package/dist/repertoire/tools-bluebubbles.js +2 -0
- package/dist/repertoire/tools-bridge.js +144 -0
- package/dist/repertoire/tools-bundle.js +993 -0
- package/dist/repertoire/tools-commerce.js +253 -0
- package/dist/repertoire/tools-config.js +186 -0
- package/dist/repertoire/tools-continuity.js +252 -0
- package/dist/repertoire/tools-credential.js +383 -0
- package/dist/repertoire/tools-evolution.js +527 -0
- package/dist/repertoire/tools-files.js +344 -0
- package/dist/repertoire/tools-flight.js +290 -0
- package/dist/repertoire/tools-flow.js +119 -0
- package/dist/repertoire/tools-github.js +3 -8
- package/dist/repertoire/tools-habits.js +103 -0
- package/dist/repertoire/tools-mail.js +1828 -0
- package/dist/repertoire/tools-notes.js +459 -0
- package/dist/repertoire/tools-obligations.js +182 -0
- package/dist/repertoire/tools-orientation.js +31 -0
- package/dist/repertoire/tools-record.js +482 -0
- package/dist/repertoire/tools-rsvp.js +139 -0
- package/dist/repertoire/tools-runtime.js +150 -0
- package/dist/repertoire/tools-session.js +881 -0
- package/dist/repertoire/tools-shell.js +133 -0
- package/dist/repertoire/tools-stripe.js +224 -0
- package/dist/repertoire/tools-surface.js +355 -0
- package/dist/repertoire/tools-teams.js +12 -39
- package/dist/repertoire/tools-travel.js +125 -0
- package/dist/repertoire/tools-trip.js +982 -0
- package/dist/repertoire/tools-unraid.js +353 -0
- package/dist/repertoire/tools-user-profile.js +146 -0
- package/dist/repertoire/tools-vault.js +40 -0
- package/dist/repertoire/tools-voice.js +145 -0
- package/dist/repertoire/tools.js +276 -79
- package/dist/repertoire/travel-api-client.js +360 -0
- package/dist/repertoire/unraid-client.js +155 -0
- package/dist/repertoire/unraid-restart.js +130 -0
- package/dist/repertoire/user-profile.js +131 -0
- package/dist/repertoire/vault-setup.js +246 -0
- package/dist/repertoire/vault-unlock.js +628 -0
- package/dist/rsvp/aisleplanner-client.js +354 -0
- package/dist/rsvp/cli.js +926 -0
- package/dist/rsvp/config.js +327 -0
- package/dist/rsvp/cutover.js +433 -0
- package/dist/rsvp/diagnostics.js +232 -0
- package/dist/rsvp/diff-renderer.js +176 -0
- package/dist/rsvp/habit-policy.js +110 -0
- package/dist/rsvp/habit-stage.js +123 -0
- package/dist/rsvp/incident-bundle.js +97 -0
- package/dist/rsvp/migration.js +204 -0
- package/dist/rsvp/native-habit-runner.js +630 -0
- package/dist/rsvp/outbound-state.js +628 -0
- package/dist/rsvp/query.js +118 -0
- package/dist/rsvp/replay.js +233 -0
- package/dist/rsvp/snapshot.js +232 -0
- package/dist/rsvp/spend-ledger.js +175 -0
- package/dist/scripts/claude-code-hook.js +41 -0
- package/dist/scripts/claude-code-stop-hook.js +47 -0
- package/dist/senses/a2a-entry.js +115 -0
- package/dist/senses/attention-queue.js +186 -0
- package/dist/senses/await-turn-message.js +58 -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} +355 -40
- package/dist/senses/bluebubbles/context-packet.js +207 -0
- package/dist/senses/bluebubbles/context-smoke.js +144 -0
- package/dist/senses/bluebubbles/entry.js +90 -0
- package/dist/senses/{bluebubbles-inbound-log.js → bluebubbles/inbound-log.js} +21 -3
- package/dist/senses/bluebubbles/index.js +3839 -0
- package/dist/senses/bluebubbles/latest-turn.js +311 -0
- package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +121 -71
- package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +159 -28
- package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +24 -13
- package/dist/senses/bluebubbles/outbound-state.js +308 -0
- package/dist/senses/bluebubbles/processed-log.js +133 -0
- package/dist/senses/bluebubbles/reaction-policy.js +47 -0
- package/dist/senses/bluebubbles/replay.js +284 -0
- package/dist/senses/{bluebubbles-runtime-state.js → bluebubbles/runtime-state.js} +30 -2
- package/dist/senses/bluebubbles/semantic-receipts.js +1986 -0
- package/dist/senses/{bluebubbles-session-cleanup.js → bluebubbles/session-cleanup.js} +1 -1
- package/dist/senses/bluebubbles/webhook-registration.js +228 -0
- package/dist/senses/bluebubbles-meta-guard.js +39 -0
- 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 +241 -0
- package/dist/senses/cli/virtual-list.js +35 -0
- package/dist/senses/cli-entry.js +60 -8
- package/dist/senses/cli-layout.js +100 -0
- package/dist/senses/cli.js +621 -321
- package/dist/senses/commands.js +66 -3
- package/dist/senses/context-packet-ledger.js +371 -0
- package/dist/senses/context-packets.js +154 -0
- package/dist/senses/habit-lifecycle-guard.js +24 -0
- package/dist/senses/habit-turn-message.js +160 -0
- package/dist/senses/ingress-evidence.js +2 -0
- package/dist/senses/mail-entry.js +66 -0
- package/dist/senses/mail.js +524 -0
- package/dist/senses/pipeline.js +1075 -176
- package/dist/senses/private-runtime-worker.js +741 -0
- package/dist/senses/private-runtime.js +1212 -0
- package/dist/senses/proactive-content-guard.js +51 -0
- package/dist/senses/sanctuary-grounding.js +118 -0
- package/dist/senses/sanctuary-health-acceptance-probe-entry.js +12 -0
- package/dist/senses/sanctuary-health-acceptance-probe.js +772 -0
- package/dist/senses/sanctuary-health-runner.js +146 -0
- package/dist/senses/sanctuary-health.js +447 -0
- package/dist/senses/sanctuary-interactive-control.js +306 -0
- package/dist/senses/sanctuary-runtime.js +206 -0
- package/dist/senses/shared-turn.js +432 -0
- package/dist/senses/surface-tool.js +109 -0
- package/dist/senses/teams-entry.js +60 -8
- package/dist/senses/teams.js +515 -217
- package/dist/senses/telegram-approval-runtime.js +526 -0
- package/dist/senses/telegram-audit-ledger.js +220 -0
- package/dist/senses/telegram-client.js +1471 -0
- package/dist/senses/telegram-entry.js +80 -0
- package/dist/senses/telegram.js +1232 -0
- package/dist/senses/trust-gate.js +104 -9
- package/dist/senses/voice/audio-playback.js +237 -0
- package/dist/senses/voice/audio-routing.js +119 -0
- package/dist/senses/voice/elevenlabs.js +202 -0
- package/dist/senses/voice/floor-control.js +431 -0
- package/dist/senses/voice/floor-controller.js +115 -0
- package/dist/senses/voice/golden-path.js +116 -0
- package/dist/senses/voice/index.js +29 -0
- package/dist/senses/voice/meeting.js +113 -0
- package/dist/senses/voice/outbound.js +190 -0
- package/dist/senses/voice/phone.js +33 -0
- package/dist/senses/voice/playback.js +139 -0
- package/dist/senses/voice/realtime-eval.js +496 -0
- package/dist/senses/voice/realtime-trace.js +531 -0
- package/dist/senses/voice/transcript.js +70 -0
- package/dist/senses/voice/turn.js +191 -0
- package/dist/senses/voice/twilio-phone-runtime.js +807 -0
- package/dist/senses/voice/twilio-phone.js +5081 -0
- package/dist/senses/voice/types.js +2 -0
- package/dist/senses/voice/whisper.js +161 -0
- package/dist/senses/voice-entry.js +81 -0
- package/dist/senses/voice-realtime-eval-command.js +99 -0
- package/dist/senses/voice-realtime-eval-entry.js +21 -0
- package/dist/senses/voice-twilio-entry.js +87 -0
- package/dist/trips/core.js +138 -0
- package/dist/trips/store.js +265 -0
- package/dist/util/frontmatter.js +69 -0
- package/npm-shrinkwrap.json +8151 -0
- package/package.json +69 -12
- package/skills/agent-commerce.md +113 -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 +99 -0
- package/skills/travel-planning.md +138 -0
- package/dist/heart/daemon/auth-flow.js +0 -430
- package/dist/heart/daemon/ouro-path-installer.js +0 -178
- package/dist/heart/daemon/update-hooks.js +0 -138
- package/dist/heart/safe-workspace.js +0 -228
- package/dist/heart/session-recall.js +0 -116
- package/dist/mind/friends/channel.js +0 -84
- package/dist/mind/friends/group-context.js +0 -144
- package/dist/mind/friends/resolver.js +0 -84
- package/dist/mind/friends/store-file.js +0 -190
- package/dist/mind/friends/store.js +0 -4
- package/dist/mind/friends/tokens.js +0 -26
- package/dist/mind/friends/trust-explanation.js +0 -74
- package/dist/mind/friends/types.js +0 -29
- package/dist/repertoire/tasks/board.js +0 -134
- package/dist/repertoire/tasks/index.js +0 -224
- package/dist/repertoire/tasks/lifecycle.js +0 -80
- package/dist/repertoire/tasks/middleware.js +0 -65
- package/dist/repertoire/tasks/parser.js +0 -173
- package/dist/repertoire/tasks/scanner.js +0 -132
- package/dist/repertoire/tasks/transitions.js +0 -144
- package/dist/senses/bluebubbles-entry.js +0 -13
- package/dist/senses/bluebubbles.js +0 -1180
- package/dist/senses/debug-activity.js +0 -148
- package/dist/senses/inner-dialog-worker.js +0 -90
- package/dist/senses/inner-dialog.js +0 -480
- package/subagents/README.md +0 -7
- /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/{repertoire/tasks/types.js → heart/attachments/sources/adapter.js} +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,360 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Travel API client module.
|
|
4
|
+
*
|
|
5
|
+
* Provides HTTP clients for:
|
|
6
|
+
* - Open-Meteo weather forecast (no auth, WMO weather codes)
|
|
7
|
+
* - US State Dept Travel Advisories (no auth)
|
|
8
|
+
* - Nominatim Geocoding (no auth, requires User-Agent)
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.TRAVEL_ADVISORY_RSS_URL = exports.WMO_WEATHER_CODES = void 0;
|
|
12
|
+
exports.wmoWeatherDescription = wmoWeatherDescription;
|
|
13
|
+
exports.getWeather = getWeather;
|
|
14
|
+
exports.getWeatherByCity = getWeatherByCity;
|
|
15
|
+
exports.isoToCountryName = isoToCountryName;
|
|
16
|
+
exports.parseAdvisoryLevel = parseAdvisoryLevel;
|
|
17
|
+
exports.parseCountryName = parseCountryName;
|
|
18
|
+
exports.parseAdvisoryText = parseAdvisoryText;
|
|
19
|
+
exports.getTravelAdvisory = getTravelAdvisory;
|
|
20
|
+
exports.geocode = geocode;
|
|
21
|
+
exports.reverseGeocode = reverseGeocode;
|
|
22
|
+
exports.searchPOI = searchPOI;
|
|
23
|
+
const runtime_1 = require("../nerves/runtime");
|
|
24
|
+
// --- Open-Meteo Weather (zero auth) ---
|
|
25
|
+
const OPEN_METEO_FORECAST = "https://api.open-meteo.com/v1/forecast";
|
|
26
|
+
const OPEN_METEO_GEOCODING = "https://geocoding-api.open-meteo.com/v1/search";
|
|
27
|
+
const OPEN_METEO_HEADERS = {
|
|
28
|
+
"User-Agent": "Ouroboros/1.0 (https://github.com/ourostack/ouroboros)",
|
|
29
|
+
Accept: "application/json",
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* WMO weather interpretation codes (WW) to human-readable descriptions.
|
|
33
|
+
* Spec: https://www.nodc.noaa.gov/archive/arc0021/0002199/1.1/data/0-data/HTML/WMO-CODE/WMO4677.HTM
|
|
34
|
+
*/
|
|
35
|
+
exports.WMO_WEATHER_CODES = {
|
|
36
|
+
0: "clear sky",
|
|
37
|
+
1: "mainly clear",
|
|
38
|
+
2: "partly cloudy",
|
|
39
|
+
3: "overcast",
|
|
40
|
+
45: "fog",
|
|
41
|
+
48: "depositing rime fog",
|
|
42
|
+
51: "light drizzle",
|
|
43
|
+
53: "moderate drizzle",
|
|
44
|
+
55: "dense drizzle",
|
|
45
|
+
56: "light freezing drizzle",
|
|
46
|
+
57: "dense freezing drizzle",
|
|
47
|
+
61: "slight rain",
|
|
48
|
+
63: "moderate rain",
|
|
49
|
+
65: "heavy rain",
|
|
50
|
+
66: "light freezing rain",
|
|
51
|
+
67: "heavy freezing rain",
|
|
52
|
+
71: "slight snowfall",
|
|
53
|
+
73: "moderate snowfall",
|
|
54
|
+
75: "heavy snowfall",
|
|
55
|
+
77: "snow grains",
|
|
56
|
+
80: "slight rain showers",
|
|
57
|
+
81: "moderate rain showers",
|
|
58
|
+
82: "violent rain showers",
|
|
59
|
+
85: "slight snow showers",
|
|
60
|
+
86: "heavy snow showers",
|
|
61
|
+
95: "thunderstorm",
|
|
62
|
+
96: "thunderstorm with slight hail",
|
|
63
|
+
99: "thunderstorm with heavy hail",
|
|
64
|
+
};
|
|
65
|
+
/** Convert a WMO weather code to a human-readable description. */
|
|
66
|
+
function wmoWeatherDescription(code) {
|
|
67
|
+
return exports.WMO_WEATHER_CODES[code] ?? "unknown";
|
|
68
|
+
}
|
|
69
|
+
async function getWeather(lat, lon) {
|
|
70
|
+
return withNervesEvents("getWeather", { lat, lon }, async () => {
|
|
71
|
+
const url = `${OPEN_METEO_FORECAST}?latitude=${lat}&longitude=${lon}` +
|
|
72
|
+
`¤t=temperature_2m,apparent_temperature,relative_humidity_2m,wind_speed_10m,weather_code` +
|
|
73
|
+
`&daily=temperature_2m_max,temperature_2m_min,weather_code` +
|
|
74
|
+
`&timezone=auto`;
|
|
75
|
+
const res = await fetch(url, { headers: OPEN_METEO_HEADERS });
|
|
76
|
+
if (!res.ok) {
|
|
77
|
+
throw new Error(`Open-Meteo API error: ${res.status} ${res.statusText}`);
|
|
78
|
+
}
|
|
79
|
+
const data = await res.json();
|
|
80
|
+
return parseOpenMeteoResponse(data, lat, lon);
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
async function getWeatherByCity(city) {
|
|
84
|
+
return withNervesEvents("getWeatherByCity", { city }, async () => {
|
|
85
|
+
// Step 1: geocode the city name via Open-Meteo geocoding
|
|
86
|
+
const geoUrl = `${OPEN_METEO_GEOCODING}?name=${encodeURIComponent(city)}&count=1&language=en`;
|
|
87
|
+
const geoRes = await fetch(geoUrl, { headers: OPEN_METEO_HEADERS });
|
|
88
|
+
if (!geoRes.ok) {
|
|
89
|
+
throw new Error(`Open-Meteo geocoding error: ${geoRes.status} ${geoRes.statusText}`);
|
|
90
|
+
}
|
|
91
|
+
const geoData = await geoRes.json();
|
|
92
|
+
const results = geoData.results;
|
|
93
|
+
if (!results || results.length === 0) {
|
|
94
|
+
throw new Error(`No location found for city "${city}"`);
|
|
95
|
+
}
|
|
96
|
+
const loc = results[0];
|
|
97
|
+
// Step 2: fetch forecast for the resolved coordinates
|
|
98
|
+
const url = `${OPEN_METEO_FORECAST}?latitude=${loc.latitude}&longitude=${loc.longitude}` +
|
|
99
|
+
`¤t=temperature_2m,apparent_temperature,relative_humidity_2m,wind_speed_10m,weather_code` +
|
|
100
|
+
`&daily=temperature_2m_max,temperature_2m_min,weather_code` +
|
|
101
|
+
`&timezone=auto`;
|
|
102
|
+
const res = await fetch(url, { headers: OPEN_METEO_HEADERS });
|
|
103
|
+
if (!res.ok) {
|
|
104
|
+
throw new Error(`Open-Meteo API error: ${res.status} ${res.statusText}`);
|
|
105
|
+
}
|
|
106
|
+
const data = await res.json();
|
|
107
|
+
return parseOpenMeteoResponse(data, loc.latitude, loc.longitude, loc.name, loc.country_code);
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
function parseOpenMeteoResponse(data, lat, lon, cityName, countryCode) {
|
|
111
|
+
const current = data.current ?? {};
|
|
112
|
+
const daily = data.daily ?? {};
|
|
113
|
+
return {
|
|
114
|
+
temperature: current.temperature_2m ?? 0,
|
|
115
|
+
feelsLike: current.apparent_temperature ?? 0,
|
|
116
|
+
description: wmoWeatherDescription(current.weather_code ?? -1),
|
|
117
|
+
humidity: current.relative_humidity_2m ?? 0,
|
|
118
|
+
windSpeed: current.wind_speed_10m ?? 0,
|
|
119
|
+
city: cityName ?? `${lat},${lon}`,
|
|
120
|
+
country: countryCode ?? "",
|
|
121
|
+
dailyHigh: daily.temperature_2m_max?.[0],
|
|
122
|
+
dailyLow: daily.temperature_2m_min?.[0],
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
// --- US State Dept Travel Advisories ---
|
|
126
|
+
/**
|
|
127
|
+
* Official US State Department travel advisories RSS feed.
|
|
128
|
+
* This is the canonical public feed maintained by travel.state.gov.
|
|
129
|
+
* Returns XML/RSS with per-country advisory items.
|
|
130
|
+
*/
|
|
131
|
+
exports.TRAVEL_ADVISORY_RSS_URL = "https://travel.state.gov/_res/rss/TAsTWs.xml";
|
|
132
|
+
/**
|
|
133
|
+
* Mapping of ISO 3166-1 alpha-2 codes to country names, ONLY for codes
|
|
134
|
+
* where ISO differs from FIPS 10-4 (which the State Dept RSS feed uses).
|
|
135
|
+
*
|
|
136
|
+
* The RSS feed Country-Tag uses FIPS codes. Callers typically pass ISO codes.
|
|
137
|
+
* When the codes match (e.g. AF for Afghanistan), no lookup is needed.
|
|
138
|
+
* This table handles the ~30 codes where they diverge.
|
|
139
|
+
*/
|
|
140
|
+
const ISO_FIPS_DIVERGENCE = {
|
|
141
|
+
AG: "Antigua and Barbuda", // FIPS: AC
|
|
142
|
+
AT: "Austria", // FIPS: AU
|
|
143
|
+
AU: "Australia", // FIPS: AS
|
|
144
|
+
BD: "Bangladesh", // FIPS: BG
|
|
145
|
+
BH: "Bahrain", // FIPS: BA
|
|
146
|
+
BN: "Brunei", // FIPS: BX
|
|
147
|
+
BO: "Bolivia", // FIPS: BL
|
|
148
|
+
BS: "Bahamas", // FIPS: BF
|
|
149
|
+
BW: "Botswana", // FIPS: BC
|
|
150
|
+
CD: "Congo (Kinshasa)", // FIPS: CG
|
|
151
|
+
CH: "Switzerland", // FIPS: SZ
|
|
152
|
+
CI: "Cote d'Ivoire", // FIPS: IV
|
|
153
|
+
CL: "Chile", // FIPS: CI
|
|
154
|
+
CN: "China", // FIPS: CH
|
|
155
|
+
CZ: "Czechia", // FIPS: EZ
|
|
156
|
+
DE: "Germany", // FIPS: GM
|
|
157
|
+
DK: "Denmark", // FIPS: DA
|
|
158
|
+
DO: "Dominican Republic", // FIPS: DR
|
|
159
|
+
DZ: "Algeria", // FIPS: AG
|
|
160
|
+
ES: "Spain", // FIPS: SP
|
|
161
|
+
FI: "Finland", // FIPS: FI → same, but title might say "Finland"
|
|
162
|
+
GQ: "Equatorial Guinea", // FIPS: EK
|
|
163
|
+
HR: "Croatia", // FIPS: HR → same
|
|
164
|
+
IE: "Ireland", // FIPS: EI
|
|
165
|
+
IL: "Israel", // FIPS: IS
|
|
166
|
+
JP: "Japan", // FIPS: JA
|
|
167
|
+
KP: "North Korea", // FIPS: KN
|
|
168
|
+
KR: "South Korea", // FIPS: KS
|
|
169
|
+
LT: "Lithuania", // FIPS: LH
|
|
170
|
+
MA: "Morocco", // FIPS: MO
|
|
171
|
+
MM: "Burma", // FIPS: BM (State Dept uses "Burma (Myanmar)")
|
|
172
|
+
NG: "Nigeria", // FIPS: NI
|
|
173
|
+
NO: "Norway", // FIPS: NO → same
|
|
174
|
+
PH: "Philippines", // FIPS: RP
|
|
175
|
+
PT: "Portugal", // FIPS: PO
|
|
176
|
+
RO: "Romania", // FIPS: RO → same
|
|
177
|
+
SE: "Sweden", // FIPS: SW
|
|
178
|
+
SG: "Singapore", // FIPS: SN
|
|
179
|
+
TL: "Timor-Leste", // FIPS: TT
|
|
180
|
+
TR: "Turkey", // FIPS: TU
|
|
181
|
+
TW: "Taiwan", // FIPS: TW → same
|
|
182
|
+
UA: "Ukraine", // FIPS: UP
|
|
183
|
+
VA: "Holy See", // FIPS: VT
|
|
184
|
+
VN: "Vietnam", // FIPS: VM
|
|
185
|
+
YE: "Yemen", // FIPS: YM
|
|
186
|
+
ZA: "South Africa", // FIPS: SF
|
|
187
|
+
};
|
|
188
|
+
/**
|
|
189
|
+
* Look up the expected country name for an ISO alpha-2 code,
|
|
190
|
+
* but ONLY when that code diverges from the FIPS code used in the RSS feed.
|
|
191
|
+
* Returns undefined for codes where ISO == FIPS (no title fallback needed).
|
|
192
|
+
*/
|
|
193
|
+
function isoToCountryName(isoCode) {
|
|
194
|
+
return ISO_FIPS_DIVERGENCE[isoCode.toUpperCase()];
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Parse advisory level (1-4) from an RSS item title like
|
|
198
|
+
* "Afghanistan - Level 4: Do Not Travel"
|
|
199
|
+
*/
|
|
200
|
+
function parseAdvisoryLevel(title) {
|
|
201
|
+
const match = title.match(/Level\s+(\d)/);
|
|
202
|
+
return match ? parseInt(match[1], 10) : 0;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Parse country name from an RSS item title like
|
|
206
|
+
* "Afghanistan - Level 4: Do Not Travel"
|
|
207
|
+
*/
|
|
208
|
+
function parseCountryName(title) {
|
|
209
|
+
const dashIdx = title.indexOf(" - ");
|
|
210
|
+
return dashIdx > 0 ? title.slice(0, dashIdx).trim() : title.trim();
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Extract advisory text from title (the part after "Level N: ").
|
|
214
|
+
*/
|
|
215
|
+
function parseAdvisoryText(title) {
|
|
216
|
+
const match = title.match(/Level\s+\d:\s*(.+)/);
|
|
217
|
+
return match ? match[1].trim() : title.trim();
|
|
218
|
+
}
|
|
219
|
+
async function getTravelAdvisory(countryCode) {
|
|
220
|
+
return withNervesEvents("getTravelAdvisory", { countryCode }, async () => {
|
|
221
|
+
const res = await fetch(exports.TRAVEL_ADVISORY_RSS_URL, {
|
|
222
|
+
headers: {
|
|
223
|
+
Accept: "application/xml, text/xml",
|
|
224
|
+
},
|
|
225
|
+
});
|
|
226
|
+
if (!res.ok) {
|
|
227
|
+
throw new Error(`Travel advisory API error: ${res.status} ${res.statusText}`);
|
|
228
|
+
}
|
|
229
|
+
const xml = await res.text();
|
|
230
|
+
const upperCode = countryCode.toUpperCase();
|
|
231
|
+
// When the caller's ISO code diverges from FIPS, we need a title-based fallback.
|
|
232
|
+
const expectedCountryName = isoToCountryName(upperCode);
|
|
233
|
+
// Parse RSS items via regex (no XML parser dependency needed).
|
|
234
|
+
// Each <item> contains <title>, <pubDate>, and <category domain="Country-Tag">.
|
|
235
|
+
const itemPattern = /<item>([\s\S]*?)<\/item>/g;
|
|
236
|
+
let titleFallbackMatch = null;
|
|
237
|
+
let match;
|
|
238
|
+
while ((match = itemPattern.exec(xml)) !== null) {
|
|
239
|
+
const block = match[1];
|
|
240
|
+
const titleMatch = block.match(/<title>(.*?)<\/title>/);
|
|
241
|
+
const title = titleMatch?.[1] ?? "";
|
|
242
|
+
const pubDateMatch = block.match(/<pubDate>(.*?)<\/pubDate>/);
|
|
243
|
+
const pubDate = pubDateMatch?.[1]?.trim() ?? "";
|
|
244
|
+
// Extract country tag from <category domain="Country-Tag">XX</category>
|
|
245
|
+
const tagMatch = block.match(/<category\s+domain="Country-Tag">(.*?)<\/category>/);
|
|
246
|
+
const tag = tagMatch?.[1]?.trim().toUpperCase();
|
|
247
|
+
// Primary match: FIPS tag matches the requested code directly.
|
|
248
|
+
// Skip this path when the caller's ISO code is known to diverge from FIPS —
|
|
249
|
+
// e.g. ISO "ES" = Spain, but FIPS "ES" = El Salvador. In that case, only
|
|
250
|
+
// the title-based fallback should match.
|
|
251
|
+
if (tag === upperCode && !expectedCountryName) {
|
|
252
|
+
return {
|
|
253
|
+
countryCode,
|
|
254
|
+
countryName: parseCountryName(title),
|
|
255
|
+
advisoryLevel: parseAdvisoryLevel(title),
|
|
256
|
+
advisoryText: parseAdvisoryText(title),
|
|
257
|
+
lastUpdated: pubDate,
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
// Title-based match: when ISO differs from FIPS, match by country name.
|
|
261
|
+
// Uses startsWith to handle names like "Burma (Myanmar)" matching "Burma".
|
|
262
|
+
if (expectedCountryName && !titleFallbackMatch) {
|
|
263
|
+
const titleCountry = parseCountryName(title);
|
|
264
|
+
if (titleCountry.toLowerCase().startsWith(expectedCountryName.toLowerCase())) {
|
|
265
|
+
titleFallbackMatch = {
|
|
266
|
+
countryCode,
|
|
267
|
+
countryName: titleCountry,
|
|
268
|
+
advisoryLevel: parseAdvisoryLevel(title),
|
|
269
|
+
advisoryText: parseAdvisoryText(title),
|
|
270
|
+
lastUpdated: pubDate,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
if (titleFallbackMatch)
|
|
276
|
+
return titleFallbackMatch;
|
|
277
|
+
throw new Error(`No travel advisory found for country code "${countryCode}"`);
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
// --- Nominatim Geocoding ---
|
|
281
|
+
const NOMINATIM_BASE = "https://nominatim.openstreetmap.org";
|
|
282
|
+
const NOMINATIM_HEADERS = {
|
|
283
|
+
"User-Agent": "Ouroboros/1.0 (https://github.com/ourostack/ouroboros)",
|
|
284
|
+
Accept: "application/json",
|
|
285
|
+
};
|
|
286
|
+
async function geocode(query) {
|
|
287
|
+
return withNervesEvents("geocode", { query }, async () => {
|
|
288
|
+
const url = `${NOMINATIM_BASE}/search?q=${encodeURIComponent(query)}&format=json`;
|
|
289
|
+
const res = await fetch(url, { headers: NOMINATIM_HEADERS });
|
|
290
|
+
if (!res.ok) {
|
|
291
|
+
throw new Error(`Nominatim geocode error: ${res.status} ${res.statusText}`);
|
|
292
|
+
}
|
|
293
|
+
const data = await res.json();
|
|
294
|
+
return data.map(parseGeoLocation);
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
async function reverseGeocode(lat, lon) {
|
|
298
|
+
return withNervesEvents("reverseGeocode", { lat, lon }, async () => {
|
|
299
|
+
const url = `${NOMINATIM_BASE}/reverse?lat=${lat}&lon=${lon}&format=json`;
|
|
300
|
+
const res = await fetch(url, { headers: NOMINATIM_HEADERS });
|
|
301
|
+
if (!res.ok) {
|
|
302
|
+
throw new Error(`Nominatim reverse geocode error: ${res.status} ${res.statusText}`);
|
|
303
|
+
}
|
|
304
|
+
const data = await res.json();
|
|
305
|
+
return parseGeoLocation(data);
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
async function searchPOI(query, lat, lon, radiusKm) {
|
|
309
|
+
return withNervesEvents("searchPOI", { query, lat, lon, radiusKm }, async () => {
|
|
310
|
+
const radius = radiusKm ?? 10;
|
|
311
|
+
// Nominatim uses viewbox for bounded search (approximate degrees from km)
|
|
312
|
+
const degOffset = radius / 111; // ~111 km per degree
|
|
313
|
+
const viewbox = `${lon - degOffset},${lat + degOffset},${lon + degOffset},${lat - degOffset}`;
|
|
314
|
+
const url = `${NOMINATIM_BASE}/search?q=${encodeURIComponent(query)}&format=json&viewbox=${viewbox}&bounded=1`;
|
|
315
|
+
const res = await fetch(url, { headers: NOMINATIM_HEADERS });
|
|
316
|
+
if (!res.ok) {
|
|
317
|
+
throw new Error(`Nominatim POI search error: ${res.status} ${res.statusText}`);
|
|
318
|
+
}
|
|
319
|
+
const data = await res.json();
|
|
320
|
+
return data.map(parseGeoLocation);
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
function parseGeoLocation(item) {
|
|
324
|
+
return {
|
|
325
|
+
lat: parseFloat(item.lat),
|
|
326
|
+
lon: parseFloat(item.lon),
|
|
327
|
+
displayName: item.display_name,
|
|
328
|
+
type: item.type ?? "unknown",
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
// --- Shared nerves event wrapper ---
|
|
332
|
+
async function withNervesEvents(operation, meta, fn) {
|
|
333
|
+
(0, runtime_1.emitNervesEvent)({
|
|
334
|
+
event: "client.request_start",
|
|
335
|
+
component: "clients",
|
|
336
|
+
message: `travel API ${operation} starting`,
|
|
337
|
+
meta: { operation, ...meta },
|
|
338
|
+
});
|
|
339
|
+
try {
|
|
340
|
+
const result = await fn();
|
|
341
|
+
(0, runtime_1.emitNervesEvent)({
|
|
342
|
+
event: "client.request_end",
|
|
343
|
+
component: "clients",
|
|
344
|
+
message: `travel API ${operation} complete`,
|
|
345
|
+
meta: { operation, ...meta },
|
|
346
|
+
});
|
|
347
|
+
return result;
|
|
348
|
+
}
|
|
349
|
+
catch (err) {
|
|
350
|
+
(0, runtime_1.emitNervesEvent)({
|
|
351
|
+
level: "error",
|
|
352
|
+
event: "client.error",
|
|
353
|
+
component: "clients",
|
|
354
|
+
message: `travel API ${operation} failed`,
|
|
355
|
+
/* v8 ignore next -- defensive: callers throw Error instances @preserve */
|
|
356
|
+
meta: { operation, reason: err instanceof Error ? err.message : String(err), ...meta },
|
|
357
|
+
});
|
|
358
|
+
throw err;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UnraidClient = exports.UnraidClientError = void 0;
|
|
4
|
+
const runtime_1 = require("../nerves/runtime");
|
|
5
|
+
class UnraidClientError extends Error {
|
|
6
|
+
code;
|
|
7
|
+
ambiguous;
|
|
8
|
+
constructor(code, message, ambiguous = false, options = {}) {
|
|
9
|
+
super(limitUtf8(message, 240), options.cause === undefined ? undefined : { cause: options.cause });
|
|
10
|
+
this.code = code;
|
|
11
|
+
this.ambiguous = ambiguous;
|
|
12
|
+
this.name = "UnraidClientError";
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
exports.UnraidClientError = UnraidClientError;
|
|
16
|
+
function limitUtf8(value, maxBytes) {
|
|
17
|
+
const bytes = Buffer.from(value, "utf8");
|
|
18
|
+
if (bytes.length <= maxBytes)
|
|
19
|
+
return value;
|
|
20
|
+
return bytes.subarray(0, Math.max(0, maxBytes - 3)).toString("utf8").replace(/\uFFFD+$/u, "") + "...";
|
|
21
|
+
}
|
|
22
|
+
function canonicalEndpoint(value) {
|
|
23
|
+
let url;
|
|
24
|
+
try {
|
|
25
|
+
url = new URL(value);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
throw new Error("Unraid GraphQL endpoint is invalid");
|
|
29
|
+
}
|
|
30
|
+
if ((url.protocol !== "http:" && url.protocol !== "https:") || url.username || url.password || url.search || url.hash) {
|
|
31
|
+
throw new Error("Unraid GraphQL endpoint is invalid");
|
|
32
|
+
}
|
|
33
|
+
return url.toString();
|
|
34
|
+
}
|
|
35
|
+
function statusError(status) {
|
|
36
|
+
if (status === 401)
|
|
37
|
+
return new UnraidClientError("unauthorized", "Unraid GraphQL request was unauthorized");
|
|
38
|
+
if (status === 403)
|
|
39
|
+
return new UnraidClientError("forbidden", "Unraid GraphQL request was forbidden");
|
|
40
|
+
return new UnraidClientError("transport", `Unraid GraphQL request failed with HTTP ${status}`);
|
|
41
|
+
}
|
|
42
|
+
class UnraidClient {
|
|
43
|
+
endpoint;
|
|
44
|
+
apiKey;
|
|
45
|
+
fetchImpl;
|
|
46
|
+
sleep;
|
|
47
|
+
readTimeoutMs;
|
|
48
|
+
mutationTimeoutMs;
|
|
49
|
+
constructor(options) {
|
|
50
|
+
this.endpoint = canonicalEndpoint(options.endpoint);
|
|
51
|
+
this.apiKey = options.apiKey.trim();
|
|
52
|
+
if (!this.apiKey)
|
|
53
|
+
throw new Error("Unraid API key is missing");
|
|
54
|
+
this.fetchImpl = options.fetch ?? fetch;
|
|
55
|
+
this.sleep = options.sleep ?? ((milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)));
|
|
56
|
+
this.readTimeoutMs = options.readTimeoutMs ?? 10_000;
|
|
57
|
+
this.mutationTimeoutMs = options.mutationTimeoutMs ?? 15_000;
|
|
58
|
+
}
|
|
59
|
+
static assertPrefixedId(value) {
|
|
60
|
+
if (Buffer.byteLength(value, "utf8") > 256 || !/^[^:]+:[^:]+$/u.test(value)) {
|
|
61
|
+
throw new Error("Unraid identifier must be one bounded prefixed ID");
|
|
62
|
+
}
|
|
63
|
+
return value;
|
|
64
|
+
}
|
|
65
|
+
read(document, variables, signal) {
|
|
66
|
+
return this.execute("read", document, variables, signal);
|
|
67
|
+
}
|
|
68
|
+
mutate(document, variables, signal) {
|
|
69
|
+
return this.execute("mutation", document, variables, signal);
|
|
70
|
+
}
|
|
71
|
+
async execute(kind, document, variables, signal) {
|
|
72
|
+
(0, runtime_1.emitNervesEvent)({
|
|
73
|
+
component: "repertoire",
|
|
74
|
+
event: "repertoire.unraid_request_start",
|
|
75
|
+
message: "Unraid GraphQL request started",
|
|
76
|
+
meta: { kind },
|
|
77
|
+
});
|
|
78
|
+
const delays = [250, 1000];
|
|
79
|
+
const maxAttempts = kind === "read" ? 3 : 1;
|
|
80
|
+
for (let attempt = 0;; attempt += 1) {
|
|
81
|
+
const timeout = AbortSignal.timeout(kind === "read" ? this.readTimeoutMs : this.mutationTimeoutMs);
|
|
82
|
+
const requestSignal = signal ? AbortSignal.any([signal, timeout]) : timeout;
|
|
83
|
+
let response;
|
|
84
|
+
try {
|
|
85
|
+
response = await this.fetchImpl(this.endpoint, {
|
|
86
|
+
method: "POST",
|
|
87
|
+
headers: { "x-api-key": this.apiKey, "Content-Type": "application/json" },
|
|
88
|
+
body: JSON.stringify({ query: document, variables }),
|
|
89
|
+
signal: requestSignal,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
const timedOut = timeout.aborted && !signal?.aborted;
|
|
94
|
+
if (kind === "read" && !timedOut && !signal?.aborted && attempt < maxAttempts - 1) {
|
|
95
|
+
await this.sleep(delays[attempt]);
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
const normalized = new UnraidClientError(timedOut ? "timeout" : "transport", timedOut ? "Unraid GraphQL request timed out" : "Unraid GraphQL transport failed", kind === "mutation" && !timedOut, { cause: error });
|
|
99
|
+
this.emitError(kind, normalized);
|
|
100
|
+
throw normalized;
|
|
101
|
+
}
|
|
102
|
+
if (!response.ok) {
|
|
103
|
+
if (kind === "read" && [429, 502, 503, 504].includes(response.status) && attempt < maxAttempts - 1) {
|
|
104
|
+
await this.sleep(delays[attempt]);
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
const error = statusError(response.status);
|
|
108
|
+
this.emitError(kind, error);
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
let envelope;
|
|
112
|
+
try {
|
|
113
|
+
envelope = await response.json();
|
|
114
|
+
}
|
|
115
|
+
catch (cause) {
|
|
116
|
+
const error = new UnraidClientError("invalid_response", "Unraid GraphQL returned invalid JSON", false, { cause });
|
|
117
|
+
this.emitError(kind, error);
|
|
118
|
+
throw error;
|
|
119
|
+
}
|
|
120
|
+
if (!envelope || typeof envelope !== "object" || Array.isArray(envelope)) {
|
|
121
|
+
const error = new UnraidClientError("invalid_response", "Unraid GraphQL response envelope is invalid");
|
|
122
|
+
this.emitError(kind, error);
|
|
123
|
+
throw error;
|
|
124
|
+
}
|
|
125
|
+
const record = envelope;
|
|
126
|
+
if (Array.isArray(record.errors) && record.errors.length > 0) {
|
|
127
|
+
const error = new UnraidClientError("graphql", "Unraid GraphQL operation failed");
|
|
128
|
+
this.emitError(kind, error);
|
|
129
|
+
throw error;
|
|
130
|
+
}
|
|
131
|
+
if (!record.data || typeof record.data !== "object" || Array.isArray(record.data)) {
|
|
132
|
+
const error = new UnraidClientError("invalid_response", "Unraid GraphQL response omitted data");
|
|
133
|
+
this.emitError(kind, error);
|
|
134
|
+
throw error;
|
|
135
|
+
}
|
|
136
|
+
(0, runtime_1.emitNervesEvent)({
|
|
137
|
+
component: "repertoire",
|
|
138
|
+
event: "repertoire.unraid_request_end",
|
|
139
|
+
message: "Unraid GraphQL request completed",
|
|
140
|
+
meta: { kind, attempt: attempt + 1 },
|
|
141
|
+
});
|
|
142
|
+
return record.data;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
emitError(kind, error) {
|
|
146
|
+
(0, runtime_1.emitNervesEvent)({
|
|
147
|
+
level: "error",
|
|
148
|
+
component: "repertoire",
|
|
149
|
+
event: "repertoire.unraid_request_error",
|
|
150
|
+
message: "Unraid GraphQL request failed",
|
|
151
|
+
meta: { kind, code: error.code, ambiguous: error.ambiguous },
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
exports.UnraidClient = UnraidClient;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SANCTUARY_RESTART_MUTATION = void 0;
|
|
4
|
+
exports.createApprovedUnraidRestartExecutor = createApprovedUnraidRestartExecutor;
|
|
5
|
+
const node_crypto_1 = require("node:crypto");
|
|
6
|
+
const runtime_1 = require("../nerves/runtime");
|
|
7
|
+
const unraid_client_1 = require("./unraid-client");
|
|
8
|
+
exports.SANCTUARY_RESTART_MUTATION = `mutation SanctuaryRestart($id: PrefixedID!) {
|
|
9
|
+
docker { restart(id: $id) { id names state status autoStart } }
|
|
10
|
+
}`;
|
|
11
|
+
function failure(code, message) {
|
|
12
|
+
return { ok: false, error: { code, message, degraded: true } };
|
|
13
|
+
}
|
|
14
|
+
function exactTarget(result, name) {
|
|
15
|
+
if (!result.ok)
|
|
16
|
+
return failure("invalid_response", result.error.message);
|
|
17
|
+
const matches = result.data.containers.filter((container) => container.name === name);
|
|
18
|
+
if (matches.length === 0)
|
|
19
|
+
return failure("not_found", `container '${name}' was not found`);
|
|
20
|
+
if (matches.length !== 1)
|
|
21
|
+
return failure("ambiguous", `container '${name}' is ambiguous`);
|
|
22
|
+
return matches[0];
|
|
23
|
+
}
|
|
24
|
+
function validArgument(value) {
|
|
25
|
+
return typeof value === "string" && value.length > 0 && Buffer.byteLength(value, "utf8") <= 128 && !value.includes("\uFFFD");
|
|
26
|
+
}
|
|
27
|
+
function acknowledgedIdentity(data, target) {
|
|
28
|
+
const docker = data.docker;
|
|
29
|
+
if (!docker || typeof docker !== "object" || Array.isArray(docker))
|
|
30
|
+
return false;
|
|
31
|
+
const restart = docker.restart;
|
|
32
|
+
if (!restart || typeof restart !== "object" || Array.isArray(restart))
|
|
33
|
+
return false;
|
|
34
|
+
const record = restart;
|
|
35
|
+
return record.id === target.id && Array.isArray(record.names)
|
|
36
|
+
&& record.names.length === 1 && String(record.names[0]).replace(/^\//u, "") === target.name;
|
|
37
|
+
}
|
|
38
|
+
function createApprovedUnraidRestartExecutor(options) {
|
|
39
|
+
const createClient = options.createClient ?? ((input) => new unraid_client_1.UnraidClient(input));
|
|
40
|
+
const sleep = options.sleep ?? ((milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)));
|
|
41
|
+
const now = options.now ?? (() => new Date());
|
|
42
|
+
const observationTimeoutMs = options.observationTimeoutMs ?? 30_000;
|
|
43
|
+
return async (args) => {
|
|
44
|
+
if (!validArgument(args.container))
|
|
45
|
+
return failure("invalid_response", "container must be one bounded exact name");
|
|
46
|
+
const resolved = exactTarget(await options.listContainers(), args.container);
|
|
47
|
+
if ("ok" in resolved)
|
|
48
|
+
return resolved;
|
|
49
|
+
const fresh = exactTarget(await options.listContainers(), args.container);
|
|
50
|
+
if ("ok" in fresh)
|
|
51
|
+
return fresh;
|
|
52
|
+
if (fresh.id !== resolved.id)
|
|
53
|
+
return failure("stale_target", "container identity changed before restart");
|
|
54
|
+
const scenarioHandleDigest = options.acceptanceScenarioHandleDigest?.();
|
|
55
|
+
const approval = options.acceptanceApproval?.();
|
|
56
|
+
const attempt = {
|
|
57
|
+
container: { id: fresh.id, name: fresh.name },
|
|
58
|
+
beforeState: fresh.state,
|
|
59
|
+
observedAt: now().toISOString(),
|
|
60
|
+
actionDigest: (0, node_crypto_1.createHash)("sha256").update(JSON.stringify({ operation: "restart", container: { id: fresh.id, name: fresh.name } })).digest("hex"),
|
|
61
|
+
argumentDigest: (0, node_crypto_1.createHash)("sha256").update(JSON.stringify({ container: args.container })).digest("hex"),
|
|
62
|
+
...(scenarioHandleDigest ? { scenarioHandleDigest } : {}),
|
|
63
|
+
...(approval ? { approvalId: approval.approvalId } : {}),
|
|
64
|
+
attemptId: (0, node_crypto_1.randomUUID)(),
|
|
65
|
+
mutationAcknowledged: false,
|
|
66
|
+
afterState: null,
|
|
67
|
+
};
|
|
68
|
+
if (approval && approval.argumentDigest !== attempt.argumentDigest)
|
|
69
|
+
return failure("stale_target", "approval arguments changed before restart");
|
|
70
|
+
await options.persistAttempt?.({ ...attempt, observedAt: now().toISOString(), state: "attempt_not_started" });
|
|
71
|
+
const apiKey = await options.loadWriteApiKey();
|
|
72
|
+
if (!apiKey.trim())
|
|
73
|
+
return failure("invalid_response", "Unraid write credential is unavailable");
|
|
74
|
+
const client = createClient({ endpoint: options.endpoint, apiKey });
|
|
75
|
+
await options.persistAttempt?.({ ...attempt, observedAt: now().toISOString(), state: "attempting" });
|
|
76
|
+
(0, runtime_1.emitNervesEvent)({ component: "repertoire", event: "repertoire.unraid_restart_start", message: "approved Unraid restart started", meta: { containerId: fresh.id, containerName: fresh.name } });
|
|
77
|
+
const persistTerminal = async (terminal) => {
|
|
78
|
+
try {
|
|
79
|
+
await options.persistAttempt?.(terminal);
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
(0, runtime_1.emitNervesEvent)({ level: "error", component: "repertoire", event: "repertoire.unraid_restart_error", message: "approved Unraid restart terminal receipt persistence failed", meta: { containerId: fresh.id, category: error instanceof Error ? error.name : "unknown" } });
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
let acknowledged = false;
|
|
88
|
+
try {
|
|
89
|
+
const mutation = await client.mutate(exports.SANCTUARY_RESTART_MUTATION, { id: fresh.id });
|
|
90
|
+
acknowledged = acknowledgedIdentity(mutation, fresh);
|
|
91
|
+
if (!acknowledged)
|
|
92
|
+
throw new Error("restart response identity was invalid");
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
acknowledged = false;
|
|
96
|
+
}
|
|
97
|
+
const startedAt = now().getTime();
|
|
98
|
+
let sawRestarting = false;
|
|
99
|
+
try {
|
|
100
|
+
do {
|
|
101
|
+
const observed = exactTarget(await options.listContainers(), args.container);
|
|
102
|
+
if (!("ok" in observed)) {
|
|
103
|
+
if (observed.id !== fresh.id) {
|
|
104
|
+
await persistTerminal({ ...attempt, observedAt: now().toISOString(), state: "attempted_or_indeterminate", mutationAcknowledged: acknowledged });
|
|
105
|
+
return failure("ambiguous", "container identity changed after restart attempt");
|
|
106
|
+
}
|
|
107
|
+
sawRestarting ||= observed.state === "restarting";
|
|
108
|
+
if (observed.state === "running" && !observed.degraded && (acknowledged || sawRestarting)) {
|
|
109
|
+
if (!await persistTerminal({ ...attempt, observedAt: now().toISOString(), state: "succeeded", mutationAcknowledged: acknowledged, afterState: observed.state })) {
|
|
110
|
+
return failure("ambiguous", "restart succeeded but its terminal receipt could not be persisted; it was not retried");
|
|
111
|
+
}
|
|
112
|
+
(0, runtime_1.emitNervesEvent)({ component: "repertoire", event: "repertoire.unraid_restart_end", message: "approved Unraid restart completed", meta: { containerId: fresh.id, observedRestart: true } });
|
|
113
|
+
return { ok: true, data: { container: { id: fresh.id, name: fresh.name }, beforeState: fresh.state, afterState: observed.state, observedRestart: true, degraded: false } };
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (now().getTime() - startedAt >= observationTimeoutMs)
|
|
117
|
+
break;
|
|
118
|
+
await sleep(1_000);
|
|
119
|
+
} while (true);
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
await persistTerminal({ ...attempt, observedAt: now().toISOString(), state: "attempted_or_indeterminate", mutationAcknowledged: acknowledged });
|
|
123
|
+
(0, runtime_1.emitNervesEvent)({ level: "error", component: "repertoire", event: "repertoire.unraid_restart_error", message: "approved Unraid restart observation failed", meta: { containerId: fresh.id, acknowledged } });
|
|
124
|
+
return failure("ambiguous", "restart was attempted but observation failed; it was not retried");
|
|
125
|
+
}
|
|
126
|
+
await persistTerminal({ ...attempt, observedAt: now().toISOString(), state: "attempted_or_indeterminate", mutationAcknowledged: acknowledged });
|
|
127
|
+
(0, runtime_1.emitNervesEvent)({ level: "error", component: "repertoire", event: "repertoire.unraid_restart_error", message: "approved Unraid restart outcome was ambiguous", meta: { containerId: fresh.id, acknowledged } });
|
|
128
|
+
return failure("ambiguous", "restart was attempted but could not be proven; it was not retried");
|
|
129
|
+
};
|
|
130
|
+
}
|