@ouro.bot/cli 0.1.0-alpha.56 → 0.1.0-alpha.561
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 +3604 -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 +251 -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/audio-routing.js +119 -0
- package/dist/senses/voice/elevenlabs.js +178 -0
- package/dist/senses/voice/golden-path.js +116 -0
- package/dist/senses/voice/index.js +26 -0
- package/dist/senses/voice/meeting.js +113 -0
- package/dist/senses/voice/playback.js +139 -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 +161 -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
|
@@ -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/ouroborosbot/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/ouroborosbot/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,131 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* User profile model and vault storage.
|
|
4
|
+
*
|
|
5
|
+
* Profiles are stored as secure notes in the agent's Vaultwarden vault,
|
|
6
|
+
* keyed by friend ID: `user-profile/{friendId}`.
|
|
7
|
+
*
|
|
8
|
+
* The storage layer uses the existing CredentialStore interface — the profile
|
|
9
|
+
* JSON is stored in the `password` field of a login item (the vault's most
|
|
10
|
+
* reliable field for arbitrary data). Field-level access ensures the full
|
|
11
|
+
* profile is never dumped to model context unnecessarily.
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.storeUserProfile = storeUserProfile;
|
|
15
|
+
exports.getUserProfile = getUserProfile;
|
|
16
|
+
exports.getUserProfileField = getUserProfileField;
|
|
17
|
+
exports.deleteUserProfile = deleteUserProfile;
|
|
18
|
+
exports.updateUserProfileFields = updateUserProfileFields;
|
|
19
|
+
const runtime_1 = require("../nerves/runtime");
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Storage key
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
function profileKey(friendId) {
|
|
24
|
+
return `user-profile/${friendId}`;
|
|
25
|
+
}
|
|
26
|
+
function isMissingUserProfileError(err) {
|
|
27
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
28
|
+
return /no credential found/i.test(message) || /field "password" not found/i.test(message);
|
|
29
|
+
}
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
// CRUD operations
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
/**
|
|
34
|
+
* Store a complete user profile in the vault.
|
|
35
|
+
* Overwrites any existing profile for the given friend ID.
|
|
36
|
+
*/
|
|
37
|
+
async function storeUserProfile(friendId, profile, store) {
|
|
38
|
+
(0, runtime_1.emitNervesEvent)({
|
|
39
|
+
event: "repertoire.user_profile_store",
|
|
40
|
+
component: "repertoire",
|
|
41
|
+
message: `storing user profile for ${friendId}`,
|
|
42
|
+
meta: { friendId },
|
|
43
|
+
});
|
|
44
|
+
const key = profileKey(friendId);
|
|
45
|
+
/* v8 ignore start -- platform-dependent v8 branch counting on await @preserve */
|
|
46
|
+
await store.store(key, {
|
|
47
|
+
password: JSON.stringify(profile),
|
|
48
|
+
notes: "user-profile",
|
|
49
|
+
});
|
|
50
|
+
/* v8 ignore stop */
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Retrieve the full user profile for a friend.
|
|
54
|
+
* Returns null if no profile exists. Throws if the stored data is malformed
|
|
55
|
+
* or the vault cannot be read.
|
|
56
|
+
*/
|
|
57
|
+
async function getUserProfile(friendId, store) {
|
|
58
|
+
(0, runtime_1.emitNervesEvent)({
|
|
59
|
+
event: "repertoire.user_profile_get",
|
|
60
|
+
component: "repertoire",
|
|
61
|
+
message: `getting user profile for ${friendId}`,
|
|
62
|
+
meta: { friendId },
|
|
63
|
+
});
|
|
64
|
+
try {
|
|
65
|
+
const raw = await store.getRawSecret(profileKey(friendId), "password");
|
|
66
|
+
const parsed = JSON.parse(raw);
|
|
67
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
68
|
+
throw new Error(`stored user profile for ${friendId} is malformed`);
|
|
69
|
+
}
|
|
70
|
+
return parsed;
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
if (err instanceof SyntaxError) {
|
|
74
|
+
throw new Error(`stored user profile for ${friendId} is malformed`);
|
|
75
|
+
}
|
|
76
|
+
if (isMissingUserProfileError(err)) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
throw err;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Retrieve a specific field from a user profile.
|
|
84
|
+
* Returns undefined if the profile doesn't exist or the field is not set.
|
|
85
|
+
*/
|
|
86
|
+
async function getUserProfileField(friendId, field, store) {
|
|
87
|
+
const profile = await getUserProfile(friendId, store);
|
|
88
|
+
if (!profile)
|
|
89
|
+
return undefined;
|
|
90
|
+
return profile[field];
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Delete a user profile from the vault.
|
|
94
|
+
* Returns true if the profile was deleted, false if it didn't exist.
|
|
95
|
+
*/
|
|
96
|
+
async function deleteUserProfile(friendId, store) {
|
|
97
|
+
(0, runtime_1.emitNervesEvent)({
|
|
98
|
+
event: "repertoire.user_profile_delete",
|
|
99
|
+
component: "repertoire",
|
|
100
|
+
message: `deleting user profile for ${friendId}`,
|
|
101
|
+
meta: { friendId },
|
|
102
|
+
});
|
|
103
|
+
return store.delete(profileKey(friendId));
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Update specific fields on a user profile, merging with existing data.
|
|
107
|
+
* Creates the profile if it doesn't exist.
|
|
108
|
+
* Preferences are merged (not replaced) at the key level.
|
|
109
|
+
*/
|
|
110
|
+
async function updateUserProfileFields(friendId, fields, store) {
|
|
111
|
+
(0, runtime_1.emitNervesEvent)({
|
|
112
|
+
event: "repertoire.user_profile_update",
|
|
113
|
+
component: "repertoire",
|
|
114
|
+
message: `updating user profile fields for ${friendId}`,
|
|
115
|
+
meta: { friendId, fieldCount: Object.keys(fields).length },
|
|
116
|
+
});
|
|
117
|
+
const existing = await getUserProfile(friendId, store);
|
|
118
|
+
let merged;
|
|
119
|
+
if (existing) {
|
|
120
|
+
// Merge preferences at key level
|
|
121
|
+
const mergedPreferences = {
|
|
122
|
+
...existing.preferences,
|
|
123
|
+
...(fields.preferences ?? {}),
|
|
124
|
+
};
|
|
125
|
+
merged = { ...existing, ...fields, preferences: mergedPreferences };
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
merged = fields;
|
|
129
|
+
}
|
|
130
|
+
await storeUserProfile(friendId, merged, store);
|
|
131
|
+
}
|