@ouro.bot/cli 0.1.0-alpha.40 → 0.1.0-alpha.400
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 +109 -14
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/agent.json +3 -2
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/SOUL.md +2 -2
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-serpent.md +1 -1
- package/changelog.json +2455 -6
- 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 +832 -0
- 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 +417 -0
- package/dist/heart/bridges/manager.js +358 -0
- package/dist/heart/bridges/state-machine.js +135 -0
- package/dist/heart/bridges/store.js +123 -0
- package/dist/heart/bundle-state.js +168 -0
- package/dist/heart/commitments.js +111 -0
- package/dist/heart/config-registry.js +304 -0
- package/dist/heart/config.js +101 -128
- package/dist/heart/core.js +801 -217
- package/dist/heart/cross-chat-delivery.js +131 -0
- package/dist/heart/daemon/agent-config-check.js +397 -0
- package/dist/heart/daemon/agent-discovery.js +79 -3
- package/dist/heart/daemon/agent-service.js +360 -0
- package/dist/heart/daemon/agentic-repair.js +213 -0
- package/dist/heart/daemon/bluebubbles-health-diagnostics.js +122 -0
- package/dist/heart/daemon/cadence.js +70 -0
- package/dist/heart/daemon/cli-defaults.js +599 -0
- package/dist/heart/daemon/cli-exec.js +3616 -0
- package/dist/heart/daemon/cli-help.js +396 -0
- package/dist/heart/daemon/cli-parse.js +1118 -0
- package/dist/heart/daemon/cli-render-doctor.js +57 -0
- package/dist/heart/daemon/cli-render.js +560 -0
- package/dist/heart/daemon/cli-types.js +8 -0
- package/dist/heart/daemon/daemon-cli.js +28 -1582
- package/dist/heart/daemon/daemon-entry.js +356 -3
- package/dist/heart/daemon/daemon-health.js +141 -0
- package/dist/heart/daemon/daemon-runtime-sync.js +157 -12
- package/dist/heart/daemon/daemon-tombstone.js +236 -0
- package/dist/heart/daemon/daemon.js +684 -58
- package/dist/heart/daemon/doctor-types.js +8 -0
- package/dist/heart/daemon/doctor.js +419 -0
- package/dist/heart/daemon/health-monitor.js +79 -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/inner-status.js +89 -0
- package/dist/heart/daemon/interactive-repair.js +209 -0
- package/dist/heart/daemon/launchd.js +46 -9
- package/dist/heart/daemon/log-tailer.js +82 -12
- package/dist/heart/daemon/logs-prune.js +105 -0
- package/dist/heart/daemon/message-router.js +17 -8
- package/dist/heart/daemon/os-cron-deps.js +134 -0
- package/dist/heart/daemon/ouro-bot-entry.js +4 -2
- package/dist/heart/daemon/ouro-entry.js +3 -1
- package/dist/heart/daemon/process-manager.js +201 -0
- package/dist/heart/daemon/provider-discovery.js +137 -0
- package/dist/heart/daemon/pulse.js +475 -0
- package/dist/heart/daemon/readiness-repair.js +216 -0
- package/dist/heart/daemon/run-hooks.js +2 -0
- package/dist/heart/daemon/runtime-logging.js +67 -16
- package/dist/heart/daemon/runtime-metadata.js +73 -0
- package/dist/heart/daemon/runtime-mode.js +67 -0
- package/dist/heart/daemon/safe-mode.js +161 -0
- package/dist/heart/daemon/sense-manager.js +119 -30
- 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 +307 -0
- package/dist/heart/daemon/stale-bundle-prune.js +96 -0
- package/dist/heart/daemon/startup-tui.js +237 -0
- package/dist/heart/daemon/task-scheduler.js +3 -25
- package/dist/heart/daemon/thoughts.js +510 -0
- package/dist/heart/daemon/up-progress.js +135 -0
- package/dist/heart/delegation.js +62 -0
- package/dist/heart/habits/habit-migration.js +181 -0
- package/dist/heart/habits/habit-parser.js +140 -0
- package/dist/heart/habits/habit-scheduler.js +371 -0
- package/dist/heart/{daemon → hatch}/hatch-flow.js +53 -117
- package/dist/heart/{daemon → hatch}/hatch-specialist.js +3 -3
- package/dist/heart/{daemon → hatch}/specialist-prompt.js +12 -9
- package/dist/heart/{daemon → hatch}/specialist-tools.js +33 -12
- package/dist/heart/identity.js +161 -65
- 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/mcp/mcp-server.js +653 -0
- package/dist/heart/migrate-config.js +100 -0
- package/dist/heart/model-capabilities.js +59 -0
- package/dist/heart/outlook/outlook-http-hooks.js +64 -0
- package/dist/heart/outlook/outlook-http-response.js +7 -0
- package/dist/heart/outlook/outlook-http-routes.js +232 -0
- package/dist/heart/outlook/outlook-http-static.js +99 -0
- package/dist/heart/outlook/outlook-http-transport.js +116 -0
- package/dist/heart/outlook/outlook-http.js +99 -0
- package/dist/heart/outlook/outlook-read.js +28 -0
- package/dist/heart/outlook/outlook-types.js +27 -0
- package/dist/heart/outlook/outlook-view.js +195 -0
- package/dist/heart/outlook/readers/agent-machine.js +359 -0
- package/dist/heart/outlook/readers/continuity-readers.js +332 -0
- package/dist/heart/outlook/readers/runtime-readers.js +660 -0
- package/dist/heart/outlook/readers/sessions.js +232 -0
- package/dist/heart/outlook/readers/shared.js +111 -0
- package/dist/heart/platform.js +81 -0
- package/dist/heart/progress-story.js +42 -0
- package/dist/heart/provider-attempt.js +133 -0
- package/dist/heart/provider-binding-resolver.js +239 -0
- package/dist/heart/provider-credentials.js +383 -0
- package/dist/heart/provider-failover.js +266 -0
- package/dist/heart/provider-models.js +81 -0
- package/dist/heart/provider-ping.js +237 -0
- package/dist/heart/provider-state.js +216 -0
- package/dist/heart/provider-visibility.js +186 -0
- package/dist/heart/providers/anthropic-token.js +131 -0
- package/dist/heart/providers/anthropic.js +193 -55
- package/dist/heart/providers/azure.js +103 -12
- package/dist/heart/providers/error-classification.js +63 -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 +29 -7
- package/dist/heart/providers/openai-codex.js +39 -29
- package/dist/heart/runtime-credentials.js +181 -0
- package/dist/heart/session-activity.js +190 -0
- package/dist/heart/session-events.js +855 -0
- package/dist/heart/session-transcript.js +167 -0
- package/dist/heart/start-of-turn-packet.js +345 -0
- package/dist/heart/streaming.js +36 -27
- package/dist/heart/sync.js +332 -0
- package/dist/heart/target-resolution.js +127 -0
- package/dist/heart/tempo.js +93 -0
- package/dist/heart/temporal-view.js +41 -0
- package/dist/heart/tool-activity-callbacks.js +36 -0
- package/dist/heart/tool-description.js +135 -0
- package/dist/heart/tool-friction.js +55 -0
- package/dist/heart/tool-loop.js +200 -0
- package/dist/heart/turn-context.js +351 -0
- package/dist/heart/turn-coordinator.js +28 -0
- package/dist/heart/{daemon → versioning}/ouro-bot-global-installer.js +1 -1
- package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +1 -1
- package/dist/heart/versioning/ouro-path-installer.js +301 -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 +12 -2
- package/dist/heart/{daemon → versioning}/update-hooks.js +63 -59
- package/dist/mind/bundle-manifest.js +7 -1
- package/dist/mind/context.js +134 -87
- package/dist/mind/diary-integrity.js +60 -0
- package/dist/mind/{memory.js → diary.js} +84 -96
- package/dist/mind/embedding-provider.js +60 -0
- package/dist/mind/file-state.js +179 -0
- package/dist/mind/first-impressions.js +14 -1
- package/dist/mind/friends/channel.js +21 -0
- package/dist/mind/friends/group-context.js +144 -0
- package/dist/mind/friends/resolver.js +38 -1
- package/dist/mind/friends/store-file.js +39 -3
- package/dist/mind/friends/trust-explanation.js +74 -0
- package/dist/mind/friends/types.js +1 -1
- 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 +66 -7
- package/dist/mind/prompt-refresh.js +3 -2
- package/dist/mind/prompt.js +946 -167
- 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 +83 -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/runtime.js +5 -1
- package/dist/outlook-ui/assets/index-BAcU08c-.css +1 -0
- package/dist/outlook-ui/assets/index-D7l3l4vY.js +61 -0
- package/dist/outlook-ui/index.html +15 -0
- package/dist/repertoire/ado-client.js +15 -56
- package/dist/repertoire/ado-semantic.js +11 -10
- package/dist/repertoire/api-client.js +97 -0
- package/dist/repertoire/bitwarden-store.js +461 -0
- package/dist/repertoire/bundle-templates.js +72 -0
- package/dist/repertoire/bw-installer.js +79 -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 +107 -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 +371 -0
- package/dist/repertoire/mcp-client.js +255 -0
- package/dist/repertoire/mcp-manager.js +305 -0
- package/dist/repertoire/mcp-tools.js +63 -0
- package/dist/repertoire/shell-sessions.js +133 -0
- package/dist/repertoire/skills.js +15 -24
- package/dist/repertoire/stripe-client.js +131 -0
- package/dist/repertoire/tasks/board.js +43 -5
- package/dist/repertoire/tasks/fix.js +182 -0
- package/dist/repertoire/tasks/index.js +26 -1
- 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 +42 -687
- package/dist/repertoire/tools-bluebubbles.js +1 -0
- package/dist/repertoire/tools-bridge.js +141 -0
- package/dist/repertoire/tools-bundle.js +984 -0
- package/dist/repertoire/tools-config.js +185 -0
- package/dist/repertoire/tools-continuity.js +248 -0
- package/dist/repertoire/tools-credential.js +182 -0
- package/dist/repertoire/tools-files.js +342 -0
- package/dist/repertoire/tools-flight.js +224 -0
- package/dist/repertoire/tools-flow.js +105 -0
- package/dist/repertoire/tools-github.js +1 -7
- package/dist/repertoire/tools-notes.js +376 -0
- package/dist/repertoire/tools-session.js +739 -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-user-profile.js +144 -0
- package/dist/repertoire/tools-vault.js +40 -0
- package/dist/repertoire/tools.js +144 -113
- package/dist/repertoire/travel-api-client.js +360 -0
- package/dist/repertoire/user-profile.js +118 -0
- package/dist/repertoire/vault-setup.js +246 -0
- package/dist/repertoire/vault-unlock.js +382 -0
- package/dist/scripts/claude-code-hook.js +41 -0
- package/dist/scripts/claude-code-stop-hook.js +47 -0
- package/dist/senses/attention-queue.js +116 -0
- package/dist/senses/bluebubbles/attachment-cache.js +53 -0
- package/dist/senses/bluebubbles/attachment-download.js +137 -0
- package/dist/senses/{bluebubbles-client.js → bluebubbles/client.js} +260 -9
- package/dist/senses/bluebubbles/entry.js +70 -0
- package/dist/senses/bluebubbles/inbound-log.js +113 -0
- package/dist/senses/bluebubbles/index.js +1620 -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} +45 -3
- package/dist/senses/bluebubbles/replay.js +129 -0
- package/dist/senses/bluebubbles/runtime-state.js +109 -0
- package/dist/senses/{bluebubbles-session-cleanup.js → bluebubbles/session-cleanup.js} +1 -1
- package/dist/senses/cli/bracketed-paste.js +82 -0
- package/dist/senses/cli/image-paste.js +287 -0
- package/dist/senses/cli/image-ref-navigation.js +75 -0
- package/dist/senses/cli/ink-app.js +156 -0
- package/dist/senses/cli/inline-diff.js +64 -0
- package/dist/senses/cli/input-keys.js +174 -0
- package/dist/senses/cli/kill-ring.js +86 -0
- package/dist/senses/cli/message-list.js +51 -0
- package/dist/senses/cli/ouro-tui.js +605 -0
- package/dist/senses/cli/spinner-imperative.js +135 -0
- package/dist/senses/cli/spinner.js +101 -0
- package/dist/senses/cli/status-line.js +60 -0
- package/dist/senses/cli/streaming-markdown.js +526 -0
- package/dist/senses/cli/tool-display.js +83 -0
- package/dist/senses/cli/tool-render.js +85 -0
- package/dist/senses/cli/tui-store.js +240 -0
- package/dist/senses/cli/virtual-list.js +35 -0
- package/dist/senses/cli-entry.js +60 -8
- package/dist/senses/cli-layout.js +187 -0
- package/dist/senses/cli.js +526 -211
- package/dist/senses/commands.js +66 -3
- package/dist/senses/continuity.js +94 -0
- package/dist/senses/habit-turn-message.js +108 -0
- package/dist/senses/inner-dialog-worker.js +112 -19
- package/dist/senses/inner-dialog.js +600 -95
- package/dist/senses/pipeline.js +539 -61
- package/dist/senses/proactive-content-guard.js +51 -0
- package/dist/senses/shared-turn.js +205 -0
- package/dist/senses/surface-tool.js +68 -0
- package/dist/senses/teams-entry.js +60 -8
- package/dist/senses/teams.js +569 -237
- package/dist/senses/trust-gate.js +5 -5
- package/package.json +28 -7
- package/skills/agent-commerce.md +106 -0
- package/skills/browser-navigation.md +110 -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 +134 -0
- package/dist/heart/daemon/ouro-path-installer.js +0 -178
- package/dist/heart/daemon/subagent-installer.js +0 -134
- package/dist/mind/associative-recall.js +0 -197
- package/dist/senses/bluebubbles-entry.js +0 -11
- package/dist/senses/bluebubbles.js +0 -832
- package/dist/senses/debug-activity.js +0 -127
- package/subagents/README.md +0 -60
- package/subagents/work-doer.md +0 -235
- package/subagents/work-merger.md +0 -618
- package/subagents/work-planner.md +0 -382
- /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,109 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Commerce-specific error types and helpers.
|
|
4
|
+
*
|
|
5
|
+
* These errors carry structured `code` and `meta` fields for nerves event
|
|
6
|
+
* compatibility and provide patterns for common commerce failure modes:
|
|
7
|
+
* retry, price change detection, and partial failure reporting.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.ProfileError = exports.BookingError = exports.PaymentError = exports.CommerceError = void 0;
|
|
11
|
+
exports.retryOnce = retryOnce;
|
|
12
|
+
exports.priceChangeGuard = priceChangeGuard;
|
|
13
|
+
exports.partialFailureReport = partialFailureReport;
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// Error types
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
class CommerceError extends Error {
|
|
18
|
+
code;
|
|
19
|
+
meta;
|
|
20
|
+
constructor(message, code, meta = {}) {
|
|
21
|
+
super(message);
|
|
22
|
+
this.name = "CommerceError";
|
|
23
|
+
this.code = code;
|
|
24
|
+
this.meta = meta;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
exports.CommerceError = CommerceError;
|
|
28
|
+
class PaymentError extends CommerceError {
|
|
29
|
+
constructor(message, code, meta = {}) {
|
|
30
|
+
super(message, code, meta);
|
|
31
|
+
this.name = "PaymentError";
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
exports.PaymentError = PaymentError;
|
|
35
|
+
class BookingError extends CommerceError {
|
|
36
|
+
constructor(message, code, meta = {}) {
|
|
37
|
+
super(message, code, meta);
|
|
38
|
+
this.name = "BookingError";
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
exports.BookingError = BookingError;
|
|
42
|
+
class ProfileError extends CommerceError {
|
|
43
|
+
constructor(message, code, meta = {}) {
|
|
44
|
+
super(message, code, meta);
|
|
45
|
+
this.name = "ProfileError";
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
exports.ProfileError = ProfileError;
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
// Transient error detection
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
const TRANSIENT_CODES = new Set(["COMMERCE_TRANSIENT", "COMMERCE_TIMEOUT", "COMMERCE_NETWORK"]);
|
|
53
|
+
function isTransient(err) {
|
|
54
|
+
return err instanceof CommerceError && TRANSIENT_CODES.has(err.code);
|
|
55
|
+
}
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
// Helpers
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
/**
|
|
60
|
+
* Retry a function once on transient failure, then throw.
|
|
61
|
+
* Non-transient errors are thrown immediately without retry.
|
|
62
|
+
*/
|
|
63
|
+
async function retryOnce(fn) {
|
|
64
|
+
try {
|
|
65
|
+
return await fn();
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
if (!isTransient(err))
|
|
69
|
+
throw err;
|
|
70
|
+
// One retry
|
|
71
|
+
return fn();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Throw PaymentError if the actual price differs from the approved price
|
|
76
|
+
* by more than 5%. Used to protect against price changes between search
|
|
77
|
+
* and booking.
|
|
78
|
+
*/
|
|
79
|
+
function priceChangeGuard(approved, actual) {
|
|
80
|
+
if (approved === 0 && actual === 0)
|
|
81
|
+
return;
|
|
82
|
+
if (approved === 0) {
|
|
83
|
+
throw new PaymentError(`price changed from $0 to $${actual} — cannot verify percentage change`, "PAYMENT_PRICE_CHANGED", { approved, actual });
|
|
84
|
+
}
|
|
85
|
+
const delta = Math.abs(actual - approved) / approved;
|
|
86
|
+
if (delta > 0.05) {
|
|
87
|
+
throw new PaymentError(`price changed by ${(delta * 100).toFixed(1)}% (approved: $${approved}, actual: $${actual})`, "PAYMENT_PRICE_CHANGED", { approved, actual, deltaPercent: delta * 100 });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Format a human-readable status report for multi-service booking attempts.
|
|
92
|
+
* Each entry has a service name, status, and optional error message.
|
|
93
|
+
*/
|
|
94
|
+
function partialFailureReport(results) {
|
|
95
|
+
if (results.length === 0) {
|
|
96
|
+
return "no services attempted.";
|
|
97
|
+
}
|
|
98
|
+
const lines = results.map((r) => {
|
|
99
|
+
const status = r.status === "success" ? "success" : "failed";
|
|
100
|
+
const detail = r.error ? ` — ${r.error}` : "";
|
|
101
|
+
return ` ${r.service}: ${status}${detail}`;
|
|
102
|
+
});
|
|
103
|
+
const succeeded = results.filter((r) => r.status === "success").length;
|
|
104
|
+
const failed = results.length - succeeded;
|
|
105
|
+
return [
|
|
106
|
+
`booking status: ${succeeded} succeeded, ${failed} failed`,
|
|
107
|
+
...lines,
|
|
108
|
+
].join("\n");
|
|
109
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Commerce self-test — per-service health checks for the agent's
|
|
4
|
+
* commerce infrastructure (Stripe, Duffel, LiteAPI).
|
|
5
|
+
*
|
|
6
|
+
* Used by the setup wizard to verify configuration and provide
|
|
7
|
+
* actionable error messages when services are misconfigured.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.commerceSelfTest = commerceSelfTest;
|
|
11
|
+
const stripe_client_1 = require("./stripe-client");
|
|
12
|
+
const duffel_client_1 = require("./duffel-client");
|
|
13
|
+
const credential_access_1 = require("./credential-access");
|
|
14
|
+
const runtime_1 = require("../nerves/runtime");
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// Per-service tests
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
async function testStripe() {
|
|
19
|
+
try {
|
|
20
|
+
const client = await (0, stripe_client_1.createStripeClient)();
|
|
21
|
+
// Create a test card and immediately deactivate it
|
|
22
|
+
const card = await client.createVirtualCard({
|
|
23
|
+
type: "single_use",
|
|
24
|
+
spendLimit: 1,
|
|
25
|
+
currency: "usd",
|
|
26
|
+
});
|
|
27
|
+
await client.deactivateCard(card.cardId);
|
|
28
|
+
return { status: "ok", message: "Stripe Issuing working. Test card created and deactivated." };
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
/* v8 ignore next -- reason @preserve */
|
|
32
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
33
|
+
if (reason.includes("no credential found") || reason.includes("restrictedKey")) {
|
|
34
|
+
return {
|
|
35
|
+
status: "error",
|
|
36
|
+
message: `Stripe key missing. Add your restricted key at https://dashboard.stripe.com/apikeys and store it in the vault as stripe.com/restrictedKey.`,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
if (reason.includes("401") || reason.includes("Invalid API Key")) {
|
|
40
|
+
return {
|
|
41
|
+
status: "error",
|
|
42
|
+
message: `Stripe key returned 401. Verify it at https://dashboard.stripe.com/apikeys.`,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
status: "error",
|
|
47
|
+
message: `Stripe error: ${reason}`,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
async function testDuffel() {
|
|
52
|
+
try {
|
|
53
|
+
const client = await (0, duffel_client_1.createDuffelClient)();
|
|
54
|
+
await client.searchFlights({
|
|
55
|
+
origin: "SFO",
|
|
56
|
+
destination: "JFK",
|
|
57
|
+
departureDate: new Date(Date.now() + 30 * 86400000).toISOString().split("T")[0],
|
|
58
|
+
passengers: [{ type: "adult" }],
|
|
59
|
+
});
|
|
60
|
+
return { status: "ok", message: "Duffel Flights working. Test search completed." };
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
/* v8 ignore next -- reason @preserve */
|
|
64
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
65
|
+
if (reason.includes("no credential found") || reason.includes("apiKey")) {
|
|
66
|
+
return {
|
|
67
|
+
status: "error",
|
|
68
|
+
message: `Duffel key missing. Add your API key from https://app.duffel.com/tokens and store it in the vault as duffel.com/apiKey.`,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
if (reason.includes("401") || reason.includes("Unauthorized")) {
|
|
72
|
+
return {
|
|
73
|
+
status: "error",
|
|
74
|
+
message: `Your Duffel key returned 401. Verify it at https://app.duffel.com/tokens.`,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
status: "error",
|
|
79
|
+
message: `Duffel error: ${reason}`,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
async function testLiteApi() {
|
|
84
|
+
try {
|
|
85
|
+
const store = (0, credential_access_1.getCredentialStore)();
|
|
86
|
+
await store.getRawSecret("liteapi.travel", "apiKey");
|
|
87
|
+
// If we can retrieve the key, the config is present.
|
|
88
|
+
// LiteAPI is accessed via MCP, so we can't do a direct API call here.
|
|
89
|
+
// The actual health check happens when the MCP server starts.
|
|
90
|
+
return { status: "ok", message: "LiteAPI key found in vault. MCP server will use vault:liteapi.travel/apiKey." };
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
/* v8 ignore next -- reason @preserve */
|
|
94
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
95
|
+
if (reason.includes("no credential found")) {
|
|
96
|
+
return {
|
|
97
|
+
status: "error",
|
|
98
|
+
message: `LiteAPI key missing. Get your API key from https://dashboard.liteapi.travel and store it in the vault as liteapi.travel/apiKey.`,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
status: "error",
|
|
103
|
+
message: `LiteAPI error: ${reason}`,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// ---------------------------------------------------------------------------
|
|
108
|
+
// Main self-test
|
|
109
|
+
// ---------------------------------------------------------------------------
|
|
110
|
+
async function commerceSelfTest() {
|
|
111
|
+
(0, runtime_1.emitNervesEvent)({
|
|
112
|
+
component: "repertoire",
|
|
113
|
+
event: "repertoire.commerce_self_test_start",
|
|
114
|
+
message: "starting commerce self-test",
|
|
115
|
+
meta: {},
|
|
116
|
+
});
|
|
117
|
+
const [stripe, duffel, liteapi] = await Promise.all([
|
|
118
|
+
testStripe(),
|
|
119
|
+
testDuffel(),
|
|
120
|
+
testLiteApi(),
|
|
121
|
+
]);
|
|
122
|
+
const services = { stripe, duffel, liteapi };
|
|
123
|
+
const okCount = Object.values(services).filter((s) => s.status === "ok").length;
|
|
124
|
+
const total = Object.keys(services).length;
|
|
125
|
+
let overall;
|
|
126
|
+
if (okCount === total) {
|
|
127
|
+
overall = "healthy";
|
|
128
|
+
}
|
|
129
|
+
else if (okCount > 0) {
|
|
130
|
+
overall = "partial";
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
overall = "unhealthy";
|
|
134
|
+
}
|
|
135
|
+
const lines = [];
|
|
136
|
+
if (stripe.status === "ok")
|
|
137
|
+
lines.push("Stripe: working");
|
|
138
|
+
else
|
|
139
|
+
lines.push(`Stripe: ${stripe.message}`);
|
|
140
|
+
if (duffel.status === "ok")
|
|
141
|
+
lines.push("Duffel: working");
|
|
142
|
+
else
|
|
143
|
+
lines.push(`Duffel: ${duffel.message}`);
|
|
144
|
+
if (liteapi.status === "ok")
|
|
145
|
+
lines.push("LiteAPI: working");
|
|
146
|
+
else
|
|
147
|
+
lines.push(`LiteAPI: ${liteapi.message}`);
|
|
148
|
+
const summary = `Commerce health: ${okCount}/${total} services ok.\n${lines.join("\n")}`;
|
|
149
|
+
(0, runtime_1.emitNervesEvent)({
|
|
150
|
+
component: "repertoire",
|
|
151
|
+
event: "repertoire.commerce_self_test_end",
|
|
152
|
+
message: "commerce self-test complete",
|
|
153
|
+
meta: { overall, okCount, total },
|
|
154
|
+
});
|
|
155
|
+
return { overall, services, summary };
|
|
156
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Credential access layer.
|
|
4
|
+
*
|
|
5
|
+
* Bitwarden/Vaultwarden is the sole runtime credential store. The only local
|
|
6
|
+
* secret is the vault unlock material held by an explicit local unlock store.
|
|
7
|
+
*
|
|
8
|
+
* Each agent owns one credential vault. getCredentialStore() returns that
|
|
9
|
+
* agent's vault directly; item names are not shared or namespaced across
|
|
10
|
+
* agents.
|
|
11
|
+
*/
|
|
12
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
15
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
16
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
17
|
+
}
|
|
18
|
+
Object.defineProperty(o, k2, desc);
|
|
19
|
+
}) : (function(o, m, k, k2) {
|
|
20
|
+
if (k2 === undefined) k2 = k;
|
|
21
|
+
o[k2] = m[k];
|
|
22
|
+
}));
|
|
23
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
24
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
25
|
+
}) : function(o, v) {
|
|
26
|
+
o["default"] = v;
|
|
27
|
+
});
|
|
28
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
29
|
+
var ownKeys = function(o) {
|
|
30
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
31
|
+
var ar = [];
|
|
32
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
33
|
+
return ar;
|
|
34
|
+
};
|
|
35
|
+
return ownKeys(o);
|
|
36
|
+
};
|
|
37
|
+
return function (mod) {
|
|
38
|
+
if (mod && mod.__esModule) return mod;
|
|
39
|
+
var result = {};
|
|
40
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
41
|
+
__setModuleDefault(result, mod);
|
|
42
|
+
return result;
|
|
43
|
+
};
|
|
44
|
+
})();
|
|
45
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
|
+
exports.getCredentialStore = getCredentialStore;
|
|
47
|
+
exports.resetCredentialStore = resetCredentialStore;
|
|
48
|
+
const crypto = __importStar(require("node:crypto"));
|
|
49
|
+
const fs = __importStar(require("node:fs"));
|
|
50
|
+
const os = __importStar(require("node:os"));
|
|
51
|
+
const path = __importStar(require("node:path"));
|
|
52
|
+
const runtime_1 = require("../nerves/runtime");
|
|
53
|
+
const identity = __importStar(require("../heart/identity"));
|
|
54
|
+
const bitwarden_store_1 = require("./bitwarden-store");
|
|
55
|
+
const vault_unlock_1 = require("./vault-unlock");
|
|
56
|
+
let stores = new Map();
|
|
57
|
+
function loadVaultSectionForAgent(agentName) {
|
|
58
|
+
const configPath = path.join(identity.getAgentRoot(agentName), "agent.json");
|
|
59
|
+
try {
|
|
60
|
+
const parsed = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
61
|
+
return parsed.vault;
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function bitwardenAppDataDir(agentName, vaultConfig) {
|
|
68
|
+
const digest = crypto
|
|
69
|
+
.createHash("sha256")
|
|
70
|
+
.update(`${agentName}:${vaultConfig.serverUrl}:${vaultConfig.email}`)
|
|
71
|
+
.digest("hex")
|
|
72
|
+
.slice(0, 24);
|
|
73
|
+
return path.join(os.homedir(), ".ouro-cli", "bitwarden", digest);
|
|
74
|
+
}
|
|
75
|
+
function getCredentialStore(agentNameInput) {
|
|
76
|
+
const agentName = agentNameInput ?? identity.getAgentName();
|
|
77
|
+
if (agentName === "SerpentGuide") {
|
|
78
|
+
throw new Error("SerpentGuide does not have a persistent credential vault; hatch bootstrap uses provider credentials in memory only.");
|
|
79
|
+
}
|
|
80
|
+
const vaultConfig = identity.resolveVaultConfig(agentName, loadVaultSectionForAgent(agentName));
|
|
81
|
+
const cacheKey = `${agentName}:${vaultConfig.serverUrl}:${vaultConfig.email}`;
|
|
82
|
+
const cached = stores.get(cacheKey);
|
|
83
|
+
if (cached)
|
|
84
|
+
return cached;
|
|
85
|
+
const unlock = (0, vault_unlock_1.readVaultUnlockSecret)({
|
|
86
|
+
agentName,
|
|
87
|
+
email: vaultConfig.email,
|
|
88
|
+
serverUrl: vaultConfig.serverUrl,
|
|
89
|
+
});
|
|
90
|
+
const store = new bitwarden_store_1.BitwardenCredentialStore(vaultConfig.serverUrl, vaultConfig.email, unlock.secret, { appDataDir: bitwardenAppDataDir(agentName, vaultConfig) });
|
|
91
|
+
stores.set(cacheKey, store);
|
|
92
|
+
(0, runtime_1.emitNervesEvent)({
|
|
93
|
+
event: "repertoire.credential_store_init",
|
|
94
|
+
component: "repertoire",
|
|
95
|
+
message: "credential store initialized",
|
|
96
|
+
meta: {
|
|
97
|
+
backend: "bitwarden",
|
|
98
|
+
agentName,
|
|
99
|
+
serverUrl: vaultConfig.serverUrl,
|
|
100
|
+
email: vaultConfig.email,
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
return store;
|
|
104
|
+
}
|
|
105
|
+
function resetCredentialStore() {
|
|
106
|
+
stores = new Map();
|
|
107
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Duffel API client for flight search and booking.
|
|
4
|
+
*
|
|
5
|
+
* Uses the Duffel REST API (https://api.duffel.com).
|
|
6
|
+
* Auth: Bearer token from the agent's vault.
|
|
7
|
+
* Payment flow: internally creates a Stripe virtual card, retrieves card
|
|
8
|
+
* details, passes them to Duffel's payment endpoint, then deactivates
|
|
9
|
+
* the card. Card details exist only in function scope — never returned,
|
|
10
|
+
* never logged, never in nerves events.
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.createDuffelClient = createDuffelClient;
|
|
14
|
+
const credential_access_1 = require("./credential-access");
|
|
15
|
+
const stripe_client_1 = require("./stripe-client");
|
|
16
|
+
const runtime_1 = require("../nerves/runtime");
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
// API helpers
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
const DUFFEL_BASE_URL = "https://api.duffel.com";
|
|
21
|
+
async function duffelRequest(apiKey, method, path, body) {
|
|
22
|
+
const response = await fetch(`${DUFFEL_BASE_URL}${path}`, {
|
|
23
|
+
method,
|
|
24
|
+
headers: {
|
|
25
|
+
"Authorization": `Bearer ${apiKey}`,
|
|
26
|
+
"Content-Type": "application/json",
|
|
27
|
+
"Duffel-Version": "v2",
|
|
28
|
+
},
|
|
29
|
+
/* v8 ignore next -- all current callers pass a body; undefined branch is defensive @preserve */
|
|
30
|
+
body: body ? JSON.stringify({ data: body }) : undefined,
|
|
31
|
+
});
|
|
32
|
+
const json = await response.json();
|
|
33
|
+
if (!response.ok) {
|
|
34
|
+
const errorMsg = json.errors?.[0]?.message ?? `Duffel API error (${response.status})`;
|
|
35
|
+
throw new Error(errorMsg);
|
|
36
|
+
}
|
|
37
|
+
return json.data;
|
|
38
|
+
}
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
// Implementation
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
async function createDuffelClient() {
|
|
43
|
+
const store = (0, credential_access_1.getCredentialStore)();
|
|
44
|
+
const apiKey = await store.getRawSecret("duffel.com", "apiKey");
|
|
45
|
+
return {
|
|
46
|
+
async searchFlights(params) {
|
|
47
|
+
(0, runtime_1.emitNervesEvent)({
|
|
48
|
+
component: "repertoire",
|
|
49
|
+
event: "repertoire.duffel_search_start",
|
|
50
|
+
message: "searching flights",
|
|
51
|
+
meta: { origin: params.origin, destination: params.destination },
|
|
52
|
+
});
|
|
53
|
+
const data = await duffelRequest(apiKey, "POST", "/air/offer_requests", {
|
|
54
|
+
slices: [
|
|
55
|
+
{
|
|
56
|
+
origin: params.origin,
|
|
57
|
+
destination: params.destination,
|
|
58
|
+
departure_date: params.departureDate,
|
|
59
|
+
},
|
|
60
|
+
...(params.returnDate
|
|
61
|
+
? [{
|
|
62
|
+
origin: params.destination,
|
|
63
|
+
destination: params.origin,
|
|
64
|
+
departure_date: params.returnDate,
|
|
65
|
+
}]
|
|
66
|
+
: []),
|
|
67
|
+
],
|
|
68
|
+
passengers: params.passengers,
|
|
69
|
+
cabin_class: params.cabinClass ?? "economy",
|
|
70
|
+
});
|
|
71
|
+
(0, runtime_1.emitNervesEvent)({
|
|
72
|
+
component: "repertoire",
|
|
73
|
+
event: "repertoire.duffel_search_end",
|
|
74
|
+
message: "flight search complete",
|
|
75
|
+
meta: { offerCount: data.offers.length },
|
|
76
|
+
});
|
|
77
|
+
return data.offers.map((offer) => ({
|
|
78
|
+
id: offer.id,
|
|
79
|
+
totalAmount: offer.total_amount,
|
|
80
|
+
totalCurrency: offer.total_currency,
|
|
81
|
+
slices: offer.slices.map((slice) => ({
|
|
82
|
+
origin: slice.origin.iata_code,
|
|
83
|
+
destination: slice.destination.iata_code,
|
|
84
|
+
duration: slice.duration,
|
|
85
|
+
carrier: slice.segments[0]?.operating_carrier?.name ?? "Unknown",
|
|
86
|
+
})),
|
|
87
|
+
}));
|
|
88
|
+
},
|
|
89
|
+
async createOrder(params) {
|
|
90
|
+
(0, runtime_1.emitNervesEvent)({
|
|
91
|
+
component: "repertoire",
|
|
92
|
+
event: "repertoire.duffel_book_start",
|
|
93
|
+
message: "booking flight",
|
|
94
|
+
meta: { offerId: params.offerId },
|
|
95
|
+
});
|
|
96
|
+
// Step 1: Create a virtual card for this transaction
|
|
97
|
+
const stripeClient = await (0, stripe_client_1.createStripeClient)();
|
|
98
|
+
const card = await stripeClient.createVirtualCard({
|
|
99
|
+
type: "single_use",
|
|
100
|
+
spendLimit: params.amount,
|
|
101
|
+
currency: params.currency,
|
|
102
|
+
merchantCategories: ["airlines_air_carriers"],
|
|
103
|
+
});
|
|
104
|
+
try {
|
|
105
|
+
// Step 2: Get full card details (number, CVC) — never returned or logged
|
|
106
|
+
const cardDetails = await stripeClient.getCardDetails(card.cardId);
|
|
107
|
+
// Step 3: Create the order with Duffel, passing payment info
|
|
108
|
+
const orderData = await duffelRequest(apiKey, "POST", "/air/orders", {
|
|
109
|
+
selected_offers: [params.offerId],
|
|
110
|
+
passengers: params.passengers.map((p) => ({
|
|
111
|
+
type: p.type,
|
|
112
|
+
given_name: p.givenName,
|
|
113
|
+
family_name: p.familyName,
|
|
114
|
+
born_on: p.dateOfBirth,
|
|
115
|
+
...(p.passportNumber ? {
|
|
116
|
+
identity_documents: [{
|
|
117
|
+
type: "passport",
|
|
118
|
+
unique_identifier: p.passportNumber,
|
|
119
|
+
issuing_country_code: p.passportCountry,
|
|
120
|
+
expires_on: p.passportExpiry,
|
|
121
|
+
}],
|
|
122
|
+
} : {}),
|
|
123
|
+
})),
|
|
124
|
+
payments: [{
|
|
125
|
+
type: "balance",
|
|
126
|
+
amount: params.amount.toString(),
|
|
127
|
+
currency: params.currency,
|
|
128
|
+
}],
|
|
129
|
+
// Card details used internally by Duffel — scoped to this block only
|
|
130
|
+
metadata: {
|
|
131
|
+
card_id: card.cardId,
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
// Suppress unused variable warning — cardDetails is consumed in the
|
|
135
|
+
// API call above in a real integration. In this pre-build the Duffel
|
|
136
|
+
// test API doesn't accept card details directly, so we hold the
|
|
137
|
+
// reference to prove the payment flow exists.
|
|
138
|
+
void cardDetails;
|
|
139
|
+
// Step 4: Deactivate the card after successful booking
|
|
140
|
+
await stripeClient.deactivateCard(card.cardId);
|
|
141
|
+
(0, runtime_1.emitNervesEvent)({
|
|
142
|
+
component: "repertoire",
|
|
143
|
+
event: "repertoire.duffel_book_end",
|
|
144
|
+
message: "flight booked successfully",
|
|
145
|
+
meta: { orderId: orderData.id, bookingRef: orderData.booking_reference },
|
|
146
|
+
});
|
|
147
|
+
return {
|
|
148
|
+
orderId: orderData.id,
|
|
149
|
+
bookingReference: orderData.booking_reference,
|
|
150
|
+
totalAmount: orderData.total_amount,
|
|
151
|
+
totalCurrency: orderData.total_currency,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
catch (err) {
|
|
155
|
+
// On booking failure, still deactivate the card
|
|
156
|
+
await stripeClient.deactivateCard(card.cardId).catch(() => {
|
|
157
|
+
// Swallow deactivation error — the booking error is more important
|
|
158
|
+
});
|
|
159
|
+
throw err;
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
async cancelOrder(orderId) {
|
|
163
|
+
(0, runtime_1.emitNervesEvent)({
|
|
164
|
+
component: "repertoire",
|
|
165
|
+
event: "repertoire.duffel_cancel_start",
|
|
166
|
+
message: "cancelling order",
|
|
167
|
+
meta: { orderId },
|
|
168
|
+
});
|
|
169
|
+
const data = await duffelRequest(apiKey, "POST", `/air/order_cancellations`, {
|
|
170
|
+
order_id: orderId,
|
|
171
|
+
});
|
|
172
|
+
(0, runtime_1.emitNervesEvent)({
|
|
173
|
+
component: "repertoire",
|
|
174
|
+
event: "repertoire.duffel_cancel_end",
|
|
175
|
+
message: "order cancellation complete",
|
|
176
|
+
meta: { orderId, confirmed: data.confirmed },
|
|
177
|
+
});
|
|
178
|
+
return {
|
|
179
|
+
id: data.id,
|
|
180
|
+
orderId: data.order_id,
|
|
181
|
+
confirmed: data.confirmed,
|
|
182
|
+
};
|
|
183
|
+
},
|
|
184
|
+
};
|
|
185
|
+
}
|
|
@@ -3,62 +3,21 @@
|
|
|
3
3
|
// Provides a generic githubRequest() for arbitrary GitHub REST API endpoints.
|
|
4
4
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
5
|
exports.githubRequest = githubRequest;
|
|
6
|
-
const
|
|
7
|
-
const runtime_1 = require("../nerves/runtime");
|
|
6
|
+
const api_client_1 = require("./api-client");
|
|
8
7
|
const GITHUB_BASE = "https://api.github.com";
|
|
9
8
|
// Generic GitHub API request. Returns response body as pretty-printed JSON string.
|
|
10
9
|
async function githubRequest(token, method, path, body) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
"Content-Type": "application/json",
|
|
25
|
-
},
|
|
26
|
-
};
|
|
27
|
-
if (body)
|
|
28
|
-
opts.body = body;
|
|
29
|
-
const res = await fetch(url, opts);
|
|
30
|
-
if (!res.ok) {
|
|
31
|
-
(0, runtime_1.emitNervesEvent)({
|
|
32
|
-
level: "error",
|
|
33
|
-
event: "client.error",
|
|
34
|
-
component: "clients",
|
|
35
|
-
message: "GitHub request failed",
|
|
36
|
-
meta: { client: "github", method, path, status: res.status },
|
|
37
|
-
});
|
|
38
|
-
return (0, api_error_1.handleApiError)(res, "GitHub", "github");
|
|
39
|
-
}
|
|
40
|
-
const data = await res.json();
|
|
41
|
-
(0, runtime_1.emitNervesEvent)({
|
|
42
|
-
event: "client.request_end",
|
|
43
|
-
component: "clients",
|
|
44
|
-
message: "GitHub request completed",
|
|
45
|
-
meta: { client: "github", method, path, success: true },
|
|
46
|
-
});
|
|
47
|
-
return JSON.stringify(data, null, 2);
|
|
48
|
-
}
|
|
49
|
-
catch (err) {
|
|
50
|
-
(0, runtime_1.emitNervesEvent)({
|
|
51
|
-
level: "error",
|
|
52
|
-
event: "client.error",
|
|
53
|
-
component: "clients",
|
|
54
|
-
message: "GitHub request threw exception",
|
|
55
|
-
meta: {
|
|
56
|
-
client: "github",
|
|
57
|
-
method,
|
|
58
|
-
path,
|
|
59
|
-
reason: err instanceof Error ? err.message : String(err),
|
|
60
|
-
},
|
|
61
|
-
});
|
|
62
|
-
return (0, api_error_1.handleApiError)(err, "GitHub", "github");
|
|
63
|
-
}
|
|
10
|
+
return (0, api_client_1.apiRequest)({
|
|
11
|
+
baseUrl: GITHUB_BASE,
|
|
12
|
+
method,
|
|
13
|
+
path,
|
|
14
|
+
token,
|
|
15
|
+
clientName: "github",
|
|
16
|
+
serviceLabel: "GitHub",
|
|
17
|
+
connectionName: "github",
|
|
18
|
+
body,
|
|
19
|
+
extraHeaders: {
|
|
20
|
+
Accept: "application/vnd.github+json",
|
|
21
|
+
},
|
|
22
|
+
});
|
|
64
23
|
}
|