@ouro.bot/cli 0.1.0-alpha.34 → 0.1.0-alpha.340
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 +188 -187
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/agent.json +3 -2
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/SOUL.md +1 -1
- package/changelog.json +2031 -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 +832 -0
- package/dist/heart/agent-entry.js +37 -2
- 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 +463 -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 +53 -21
- package/dist/heart/core.js +695 -195
- package/dist/heart/cross-chat-delivery.js +131 -0
- package/dist/heart/daemon/agent-config-check.js +292 -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 +170 -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 +591 -0
- package/dist/heart/daemon/cli-exec.js +2297 -0
- package/dist/heart/daemon/cli-help.js +306 -0
- package/dist/heart/daemon/cli-parse.js +824 -0
- package/dist/heart/daemon/cli-render-doctor.js +57 -0
- package/dist/heart/daemon/cli-render.js +512 -0
- package/dist/heart/daemon/cli-types.js +8 -0
- package/dist/heart/daemon/daemon-cli.js +30 -1171
- package/dist/heart/daemon/daemon-entry.js +358 -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 +751 -58
- package/dist/heart/daemon/doctor-types.js +8 -0
- package/dist/heart/daemon/doctor.js +401 -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 +91 -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 +1 -1
- package/dist/heart/daemon/process-manager.js +201 -0
- package/dist/heart/daemon/provider-discovery.js +105 -0
- package/dist/heart/daemon/pulse.js +463 -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 +101 -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 +72 -3
- 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 +32 -120
- package/dist/heart/{daemon → hatch}/hatch-specialist.js +3 -3
- package/dist/heart/{daemon → hatch}/specialist-prompt.js +10 -7
- package/dist/heart/{daemon → hatch}/specialist-tools.js +49 -3
- package/dist/heart/identity.js +154 -59
- package/dist/heart/kicks.js +2 -20
- package/dist/heart/mcp/mcp-server.js +653 -0
- package/dist/heart/migrate-config.js +127 -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 +194 -0
- package/dist/heart/outlook/readers/agent-machine.js +355 -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 +231 -0
- package/dist/heart/outlook/readers/shared.js +111 -0
- package/dist/heart/progress-story.js +42 -0
- package/dist/heart/provider-failover.js +135 -0
- package/dist/heart/provider-models.js +81 -0
- package/dist/heart/provider-ping.js +234 -0
- package/dist/heart/providers/anthropic-token.js +163 -0
- package/dist/heart/providers/anthropic.js +171 -50
- package/dist/heart/providers/azure.js +97 -11
- package/dist/heart/providers/error-classification.js +63 -0
- package/dist/heart/providers/github-copilot.js +135 -0
- package/dist/heart/providers/minimax-vlm.js +189 -0
- package/dist/heart/providers/minimax.js +23 -6
- package/dist/heart/providers/openai-codex.js +33 -23
- package/dist/heart/session-activity.js +190 -0
- package/dist/heart/session-events.js +726 -0
- package/dist/heart/session-recall.js +162 -0
- package/dist/heart/start-of-turn-packet.js +341 -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 +358 -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/{daemon → versioning}/ouro-path-installer.js +78 -35
- 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/associative-recall.js +137 -66
- package/dist/mind/bundle-manifest.js +7 -1
- package/dist/mind/context.js +89 -93
- 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 +56 -0
- package/dist/mind/friends/group-context.js +144 -0
- package/dist/mind/friends/resolver.js +37 -0
- package/dist/mind/friends/store-file.js +58 -3
- package/dist/mind/friends/trust-explanation.js +74 -0
- package/dist/mind/friends/types.js +8 -0
- package/dist/mind/journal-index.js +161 -0
- package/dist/mind/obligation-steering.js +221 -0
- package/dist/mind/pending.js +74 -7
- package/dist/mind/prompt.js +999 -111
- package/dist/mind/provenance-trust.js +26 -0
- package/dist/mind/scrutiny.js +173 -0
- package/dist/mind/token-estimate.js +8 -12
- package/dist/nerves/cli-logging.js +7 -1
- package/dist/nerves/coverage/audit.js +1 -1
- 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-DC7sZefn.js +61 -0
- package/dist/outlook-ui/assets/index-LwChZTgL.css +1 -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 +319 -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 +527 -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 +375 -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 +14 -23
- 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 +28 -10
- 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 -79
- package/dist/repertoire/tool-results.js +29 -0
- package/dist/repertoire/tools-attachments.js +316 -0
- package/dist/repertoire/tools-base.js +45 -771
- 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-memory.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 +12 -62
- package/dist/repertoire/tools-travel.js +125 -0
- package/dist/repertoire/tools-user-profile.js +144 -0
- package/dist/repertoire/tools-vault.js +110 -0
- package/dist/repertoire/tools.js +144 -138
- package/dist/repertoire/travel-api-client.js +360 -0
- package/dist/repertoire/user-profile.js +118 -0
- package/dist/repertoire/vault-setup.js +241 -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} +225 -9
- package/dist/senses/bluebubbles/entry.js +13 -0
- package/dist/senses/bluebubbles/inbound-log.js +113 -0
- package/dist/senses/bluebubbles/index.js +1590 -0
- package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +121 -70
- package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +43 -12
- package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +46 -6
- 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 +1 -1
- package/dist/senses/cli-layout.js +187 -0
- package/dist/senses/cli.js +595 -246
- package/dist/senses/commands.js +65 -1
- 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 +633 -86
- package/dist/senses/pipeline.js +567 -0
- package/dist/senses/shared-turn.js +199 -0
- package/dist/senses/surface-tool.js +68 -0
- package/dist/senses/teams.js +665 -160
- package/dist/senses/trust-gate.js +112 -2
- package/package.json +29 -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 +81 -0
- package/skills/travel-planning.md +138 -0
- package/dist/heart/daemon/subagent-installer.js +0 -134
- package/dist/senses/bluebubbles-entry.js +0 -11
- package/dist/senses/bluebubbles.js +0 -547
- package/dist/senses/debug-activity.js +0 -124
- package/subagents/README.md +0 -73
- 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-serpent.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,134 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.createRealOsCronDeps = createRealOsCronDeps;
|
|
37
|
+
exports.createRealCrontabDeps = createRealCrontabDeps;
|
|
38
|
+
exports.resolveOuroBinaryPath = resolveOuroBinaryPath;
|
|
39
|
+
const child_process_1 = require("child_process");
|
|
40
|
+
const fs = __importStar(require("fs"));
|
|
41
|
+
const os = __importStar(require("os"));
|
|
42
|
+
const path = __importStar(require("path"));
|
|
43
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
44
|
+
function createRealOsCronDeps() {
|
|
45
|
+
(0, runtime_1.emitNervesEvent)({
|
|
46
|
+
component: "daemon",
|
|
47
|
+
event: "daemon.os_cron_deps_created",
|
|
48
|
+
message: "created real OS cron deps",
|
|
49
|
+
meta: { platform: process.platform },
|
|
50
|
+
});
|
|
51
|
+
return {
|
|
52
|
+
exec: (cmd) => {
|
|
53
|
+
try {
|
|
54
|
+
(0, child_process_1.execSync)(cmd, { stdio: "ignore" });
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
/* best effort */
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
writeFile: (p, c) => fs.writeFileSync(p, c, "utf-8"),
|
|
61
|
+
removeFile: (p) => {
|
|
62
|
+
try {
|
|
63
|
+
fs.unlinkSync(p);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
/* best effort — file may already be gone */
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
existsFile: (p) => fs.existsSync(p),
|
|
70
|
+
listDir: (dir) => {
|
|
71
|
+
try {
|
|
72
|
+
return fs.readdirSync(dir);
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return [];
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
mkdirp: (dir) => fs.mkdirSync(dir, { recursive: true }),
|
|
79
|
+
homeDir: os.homedir(),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function createRealCrontabDeps() {
|
|
83
|
+
(0, runtime_1.emitNervesEvent)({
|
|
84
|
+
component: "daemon",
|
|
85
|
+
event: "daemon.crontab_deps_created",
|
|
86
|
+
message: "created real crontab deps",
|
|
87
|
+
meta: {},
|
|
88
|
+
});
|
|
89
|
+
/* v8 ignore start -- crontab exec closures: calling these in tests would modify the real system crontab @preserve */
|
|
90
|
+
return {
|
|
91
|
+
execOutput: (cmd) => (0, child_process_1.execSync)(cmd, { encoding: "utf-8" }),
|
|
92
|
+
execWrite: (cmd, stdin) => {
|
|
93
|
+
(0, child_process_1.execSync)(cmd, { input: stdin, stdio: ["pipe", "ignore", "ignore"] });
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
/* v8 ignore stop */
|
|
97
|
+
}
|
|
98
|
+
/* v8 ignore start -- ouro path resolution: probes process.argv, filesystem layout, and PATH; branches depend on install method and runtime environment @preserve */
|
|
99
|
+
function resolveOuroBinaryPath() {
|
|
100
|
+
// Try to resolve from process.argv[1] — the script being run
|
|
101
|
+
const scriptPath = process.argv[1];
|
|
102
|
+
if (scriptPath) {
|
|
103
|
+
// If running via node dist/heart/daemon/daemon-entry.js, resolve the ouro wrapper
|
|
104
|
+
// The ouro binary is typically at the package root's bin
|
|
105
|
+
const distDir = path.resolve(path.dirname(scriptPath));
|
|
106
|
+
const packageBin = path.resolve(distDir, "..", "..", "..", "node_modules", ".bin", "ouro");
|
|
107
|
+
if (fs.existsSync(packageBin)) {
|
|
108
|
+
return packageBin;
|
|
109
|
+
}
|
|
110
|
+
// Try the repo-local scripts/ouro.sh
|
|
111
|
+
const repoOuro = path.resolve(distDir, "..", "..", "..", "scripts", "ouro.sh");
|
|
112
|
+
if (fs.existsSync(repoOuro)) {
|
|
113
|
+
return repoOuro;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Try which ouro
|
|
117
|
+
try {
|
|
118
|
+
const result = (0, child_process_1.execSync)("which ouro", { encoding: "utf-8" }).trim();
|
|
119
|
+
if (result.length > 0)
|
|
120
|
+
return result;
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
/* not on PATH */
|
|
124
|
+
}
|
|
125
|
+
// Fallback: use "ouro" and rely on PATH
|
|
126
|
+
(0, runtime_1.emitNervesEvent)({
|
|
127
|
+
component: "daemon",
|
|
128
|
+
event: "daemon.ouro_path_fallback",
|
|
129
|
+
message: "could not resolve full ouro binary path, falling back to 'ouro'",
|
|
130
|
+
meta: {},
|
|
131
|
+
});
|
|
132
|
+
return "ouro";
|
|
133
|
+
}
|
|
134
|
+
/* v8 ignore stop */
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
4
|
const runtime_1 = require("../../nerves/runtime");
|
|
5
5
|
const runtime_logging_1 = require("./runtime-logging");
|
|
6
|
-
const ouro_bot_wrapper_1 = require("
|
|
6
|
+
const ouro_bot_wrapper_1 = require("../versioning/ouro-bot-wrapper");
|
|
7
7
|
(0, runtime_logging_1.configureDaemonRuntimeLogger)("ouro-bot");
|
|
8
8
|
(0, runtime_1.emitNervesEvent)({
|
|
9
9
|
component: "daemon",
|
|
@@ -51,15 +51,70 @@ class DaemonProcessManager {
|
|
|
51
51
|
now;
|
|
52
52
|
setTimeoutFn;
|
|
53
53
|
clearTimeoutFn;
|
|
54
|
+
cooldownRecoveryMs;
|
|
55
|
+
maxCooldownRetries;
|
|
56
|
+
existsSyncFn;
|
|
57
|
+
configCheckFn;
|
|
58
|
+
statusWriterFn;
|
|
59
|
+
onSnapshotChangeFn;
|
|
60
|
+
/**
|
|
61
|
+
* Notify the snapshot-change observer (if registered). Swallows any
|
|
62
|
+
* errors from the observer so process lifecycle code never fails
|
|
63
|
+
* because the observer threw.
|
|
64
|
+
*/
|
|
65
|
+
notifySnapshotChange(snapshot) {
|
|
66
|
+
if (!this.onSnapshotChangeFn)
|
|
67
|
+
return;
|
|
68
|
+
try {
|
|
69
|
+
this.onSnapshotChangeFn(snapshot);
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
(0, runtime_1.emitNervesEvent)({
|
|
73
|
+
level: "warn",
|
|
74
|
+
component: "daemon",
|
|
75
|
+
event: "daemon.snapshot_change_observer_error",
|
|
76
|
+
message: "snapshot-change observer threw",
|
|
77
|
+
meta: {
|
|
78
|
+
agent: snapshot.name,
|
|
79
|
+
error: error instanceof Error ? error.message : /* v8 ignore next -- defensive: non-Error catch branch @preserve */ String(error),
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
writeStatus(agent, text) {
|
|
85
|
+
try {
|
|
86
|
+
this.statusWriterFn(text);
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
(0, runtime_1.emitNervesEvent)({
|
|
90
|
+
level: "warn",
|
|
91
|
+
component: "daemon",
|
|
92
|
+
event: "daemon.status_writer_error",
|
|
93
|
+
message: "daemon status writer threw",
|
|
94
|
+
meta: {
|
|
95
|
+
agent,
|
|
96
|
+
error: error instanceof Error ? error.message : String(error),
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
54
101
|
constructor(options) {
|
|
55
102
|
this.maxRestartsPerHour = options.maxRestartsPerHour ?? 10;
|
|
56
103
|
this.stabilityThresholdMs = options.stabilityThresholdMs ?? 60_000;
|
|
57
104
|
this.initialBackoffMs = options.initialBackoffMs ?? 1_000;
|
|
58
105
|
this.maxBackoffMs = options.maxBackoffMs ?? 60_000;
|
|
106
|
+
this.cooldownRecoveryMs = options.cooldownRecoveryMs ?? 5 * 60 * 1_000;
|
|
107
|
+
this.maxCooldownRetries = options.maxCooldownRetries ?? 3;
|
|
59
108
|
this.spawnFn = options.spawn ?? ((command, args, spawnOptions) => (0, child_process_1.spawn)(command, args, spawnOptions));
|
|
60
109
|
this.now = options.now ?? (() => Date.now());
|
|
61
110
|
this.setTimeoutFn = options.setTimeoutFn ?? ((cb, delay) => setTimeout(cb, delay));
|
|
62
111
|
this.clearTimeoutFn = options.clearTimeoutFn ?? ((timer) => clearTimeout(timer));
|
|
112
|
+
this.existsSyncFn = options.existsSync ?? null;
|
|
113
|
+
this.configCheckFn = options.configCheck ?? null;
|
|
114
|
+
this.statusWriterFn = options.statusWriter ?? ((text) => {
|
|
115
|
+
process.stderr.write(text);
|
|
116
|
+
});
|
|
117
|
+
this.onSnapshotChangeFn = options.onSnapshotChange ?? null;
|
|
63
118
|
for (const agent of options.agents) {
|
|
64
119
|
this.agents.set(agent.name, {
|
|
65
120
|
config: agent,
|
|
@@ -67,6 +122,9 @@ class DaemonProcessManager {
|
|
|
67
122
|
restartTimer: null,
|
|
68
123
|
crashTimestamps: [],
|
|
69
124
|
stopRequested: false,
|
|
125
|
+
cooldownTimer: null,
|
|
126
|
+
cooldownRetryCount: 0,
|
|
127
|
+
fastCrashCount: 0,
|
|
70
128
|
snapshot: {
|
|
71
129
|
name: agent.name,
|
|
72
130
|
channel: agent.channel,
|
|
@@ -76,6 +134,10 @@ class DaemonProcessManager {
|
|
|
76
134
|
startedAt: null,
|
|
77
135
|
lastCrashAt: null,
|
|
78
136
|
backoffMs: this.initialBackoffMs,
|
|
137
|
+
lastExitCode: null,
|
|
138
|
+
lastSignal: null,
|
|
139
|
+
errorReason: null,
|
|
140
|
+
fixHint: null,
|
|
79
141
|
},
|
|
80
142
|
});
|
|
81
143
|
}
|
|
@@ -94,14 +156,62 @@ class DaemonProcessManager {
|
|
|
94
156
|
this.clearRestartTimer(state);
|
|
95
157
|
state.stopRequested = false;
|
|
96
158
|
state.snapshot.status = "starting";
|
|
159
|
+
if (this.configCheckFn) {
|
|
160
|
+
const result = await this.configCheckFn(agent);
|
|
161
|
+
if (!result.ok) {
|
|
162
|
+
state.snapshot.status = "crashed";
|
|
163
|
+
// Surface the error and fix to the snapshot so sibling agents can
|
|
164
|
+
// read it via the pulse. Without this, the diagnosis stayed
|
|
165
|
+
// trapped in the nerves event and stderr — visible to humans
|
|
166
|
+
// running `ouro status` or grepping logs, but invisible to
|
|
167
|
+
// peer agents trying to coordinate around the broken state.
|
|
168
|
+
state.snapshot.errorReason = result.error ?? "agent config validation failed";
|
|
169
|
+
state.snapshot.fixHint = result.fix ?? null;
|
|
170
|
+
(0, runtime_1.emitNervesEvent)({
|
|
171
|
+
level: "error",
|
|
172
|
+
component: "daemon",
|
|
173
|
+
event: "daemon.agent_config_invalid",
|
|
174
|
+
message: result.error ?? "agent config validation failed",
|
|
175
|
+
meta: { agent, fix: result.fix ?? null },
|
|
176
|
+
});
|
|
177
|
+
this.writeStatus(agent, `[daemon] ${agent}: ${result.error}\n` +
|
|
178
|
+
(result.fix ? ` Fix: ${result.fix}\n` : ""));
|
|
179
|
+
this.notifySnapshotChange(state.snapshot);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
// Config check passed — clear any prior error so the pulse stops
|
|
183
|
+
// reporting the broken state. This is the recovery path: the user
|
|
184
|
+
// fixed their secrets/config, the next startAgent attempt sees a
|
|
185
|
+
// valid config, and the pulse goes quiet.
|
|
186
|
+
state.snapshot.errorReason = null;
|
|
187
|
+
state.snapshot.fixHint = null;
|
|
188
|
+
}
|
|
97
189
|
const runCwd = (0, identity_1.getRepoRoot)();
|
|
98
190
|
const entryScript = path.join((0, identity_1.getRepoRoot)(), "dist", state.config.entry);
|
|
191
|
+
if (this.existsSyncFn && !this.existsSyncFn(entryScript)) {
|
|
192
|
+
state.snapshot.status = "crashed";
|
|
193
|
+
(0, runtime_1.emitNervesEvent)({
|
|
194
|
+
level: "error",
|
|
195
|
+
component: "daemon",
|
|
196
|
+
event: "daemon.agent_entry_missing",
|
|
197
|
+
message: "agent entry script does not exist — cannot spawn. Run 'ouro daemon install' from the correct location.",
|
|
198
|
+
meta: { agent, entryScript },
|
|
199
|
+
});
|
|
200
|
+
this.notifySnapshotChange(state.snapshot);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
99
203
|
const args = [entryScript, "--agent", state.config.agentArg ?? agent, ...(state.config.args ?? [])];
|
|
100
204
|
const child = this.spawnFn("node", args, {
|
|
101
205
|
cwd: runCwd,
|
|
102
206
|
env: state.config.env ? { ...process.env, ...state.config.env } : process.env,
|
|
103
207
|
stdio: ["ignore", "ignore", "ignore", "ipc"],
|
|
104
208
|
});
|
|
209
|
+
/* v8 ignore next 7 -- defensive: spawn should always return a ChildProcess @preserve */
|
|
210
|
+
if (!child) {
|
|
211
|
+
state.snapshot.status = "crashed";
|
|
212
|
+
(0, runtime_1.emitNervesEvent)({ level: "error", component: "daemon", event: "daemon.agent_spawn_failed", message: "spawn returned null", meta: { agent } });
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
105
215
|
state.process = child;
|
|
106
216
|
state.snapshot.status = "running";
|
|
107
217
|
state.snapshot.pid = child.pid ?? null;
|
|
@@ -112,6 +222,18 @@ class DaemonProcessManager {
|
|
|
112
222
|
message: "daemon started managed agent process",
|
|
113
223
|
meta: { agent, pid: child.pid ?? null, cwd: runCwd },
|
|
114
224
|
});
|
|
225
|
+
this.notifySnapshotChange(state.snapshot);
|
|
226
|
+
/* v8 ignore start — child process error handler; requires real spawn to trigger */
|
|
227
|
+
child.on("error", (err) => {
|
|
228
|
+
(0, runtime_1.emitNervesEvent)({
|
|
229
|
+
level: "warn",
|
|
230
|
+
component: "daemon",
|
|
231
|
+
event: "daemon.agent_process_error",
|
|
232
|
+
message: "managed agent process emitted error",
|
|
233
|
+
meta: { agent, error: err.message },
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
/* v8 ignore stop */
|
|
115
237
|
child.once("exit", (code, signal) => {
|
|
116
238
|
this.onExit(state, code, signal);
|
|
117
239
|
});
|
|
@@ -119,10 +241,12 @@ class DaemonProcessManager {
|
|
|
119
241
|
async stopAgent(agent) {
|
|
120
242
|
const state = this.requireAgent(agent);
|
|
121
243
|
this.clearRestartTimer(state);
|
|
244
|
+
this.clearCooldownTimer(state);
|
|
122
245
|
state.stopRequested = true;
|
|
123
246
|
if (!state.process) {
|
|
124
247
|
state.snapshot.status = "stopped";
|
|
125
248
|
state.snapshot.pid = null;
|
|
249
|
+
this.notifySnapshotChange(state.snapshot);
|
|
126
250
|
return;
|
|
127
251
|
}
|
|
128
252
|
const child = state.process;
|
|
@@ -141,6 +265,7 @@ class DaemonProcessManager {
|
|
|
141
265
|
meta: { agent },
|
|
142
266
|
});
|
|
143
267
|
}
|
|
268
|
+
this.notifySnapshotChange(state.snapshot);
|
|
144
269
|
}
|
|
145
270
|
async restartAgent(agent) {
|
|
146
271
|
await this.stopAgent(agent);
|
|
@@ -179,6 +304,8 @@ class DaemonProcessManager {
|
|
|
179
304
|
return;
|
|
180
305
|
state.process = null;
|
|
181
306
|
state.snapshot.pid = null;
|
|
307
|
+
state.snapshot.lastExitCode = code;
|
|
308
|
+
state.snapshot.lastSignal = signal;
|
|
182
309
|
const crashed = !state.stopRequested && code !== 0;
|
|
183
310
|
const now = this.now();
|
|
184
311
|
const startedAt = state.snapshot.startedAt ? Date.parse(state.snapshot.startedAt) : now;
|
|
@@ -195,13 +322,45 @@ class DaemonProcessManager {
|
|
|
195
322
|
if (runDuration >= this.stabilityThresholdMs) {
|
|
196
323
|
state.snapshot.backoffMs = this.initialBackoffMs;
|
|
197
324
|
}
|
|
325
|
+
this.notifySnapshotChange(state.snapshot);
|
|
198
326
|
return;
|
|
199
327
|
}
|
|
200
328
|
state.snapshot.lastCrashAt = new Date(now).toISOString();
|
|
329
|
+
// Fast-crash detection: if the agent dies within 5 seconds of starting, it's likely
|
|
330
|
+
// a configuration issue (missing credentials, bad provider, etc.) not a transient failure.
|
|
331
|
+
// After 3 consecutive fast crashes, stop retrying and mark as config-failed.
|
|
332
|
+
const FAST_CRASH_THRESHOLD_MS = 5000;
|
|
333
|
+
const FAST_CRASH_MAX = 3;
|
|
334
|
+
if (runDuration < FAST_CRASH_THRESHOLD_MS) {
|
|
335
|
+
state.fastCrashCount = state.fastCrashCount + 1;
|
|
336
|
+
if (state.fastCrashCount >= FAST_CRASH_MAX) {
|
|
337
|
+
state.snapshot.status = "crashed";
|
|
338
|
+
// Capture the fast-crash diagnosis on the snapshot so it surfaces
|
|
339
|
+
// via the pulse. The error message is prescriptive: it tells the
|
|
340
|
+
// user (and their sibling agents) exactly what to do.
|
|
341
|
+
state.snapshot.errorReason = `agent crashed ${FAST_CRASH_MAX} times within ${FAST_CRASH_THRESHOLD_MS}ms of startup — likely a configuration issue (missing credentials, bad provider).`;
|
|
342
|
+
state.snapshot.fixHint = `Fix the config and run \`ouro up\` to restart, or check daemon logs for the underlying error.`;
|
|
343
|
+
(0, runtime_1.emitNervesEvent)({
|
|
344
|
+
level: "error",
|
|
345
|
+
component: "daemon",
|
|
346
|
+
event: "daemon.agent_config_failure",
|
|
347
|
+
message: `agent crashed ${FAST_CRASH_MAX} times within ${FAST_CRASH_THRESHOLD_MS}ms of startup — likely a configuration issue (missing credentials, bad provider). Not retrying. Fix the config and run \`ouro up\` to restart.`,
|
|
348
|
+
meta: { agent: state.config.name, fastCrashCount: state.fastCrashCount, avgRunDurationMs: runDuration },
|
|
349
|
+
});
|
|
350
|
+
this.notifySnapshotChange(state.snapshot);
|
|
351
|
+
return; // Don't schedule cooldown recovery — this needs human/agent intervention
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
else {
|
|
355
|
+
// Reset fast-crash counter on a stable run
|
|
356
|
+
state.fastCrashCount = 0;
|
|
357
|
+
}
|
|
201
358
|
state.crashTimestamps = state.crashTimestamps.filter((crashTs) => crashTs >= startOfHour(now));
|
|
202
359
|
state.crashTimestamps.push(now);
|
|
203
360
|
if (state.crashTimestamps.length > this.maxRestartsPerHour) {
|
|
204
361
|
state.snapshot.status = "crashed";
|
|
362
|
+
state.snapshot.errorReason = `agent exceeded restart limit (${this.maxRestartsPerHour}/hr) — entering cooldown`;
|
|
363
|
+
state.snapshot.fixHint = "investigate why the agent keeps crashing; cooldown will retry shortly";
|
|
205
364
|
(0, runtime_1.emitNervesEvent)({
|
|
206
365
|
level: "error",
|
|
207
366
|
component: "daemon",
|
|
@@ -209,6 +368,8 @@ class DaemonProcessManager {
|
|
|
209
368
|
message: "managed agent exceeded restart limit and is marked crashed",
|
|
210
369
|
meta: { agent: state.config.name, maxRestartsPerHour: this.maxRestartsPerHour },
|
|
211
370
|
});
|
|
371
|
+
this.notifySnapshotChange(state.snapshot);
|
|
372
|
+
this.scheduleCooldownRecovery(state);
|
|
212
373
|
return;
|
|
213
374
|
}
|
|
214
375
|
state.snapshot.status = "starting";
|
|
@@ -219,6 +380,7 @@ class DaemonProcessManager {
|
|
|
219
380
|
state.restartTimer = this.setTimeoutFn(() => {
|
|
220
381
|
void this.startAgent(state.config.name);
|
|
221
382
|
}, waitMs);
|
|
383
|
+
this.notifySnapshotChange(state.snapshot);
|
|
222
384
|
}
|
|
223
385
|
clearRestartTimer(state) {
|
|
224
386
|
if (state.restartTimer === null)
|
|
@@ -226,6 +388,45 @@ class DaemonProcessManager {
|
|
|
226
388
|
this.clearTimeoutFn(state.restartTimer);
|
|
227
389
|
state.restartTimer = null;
|
|
228
390
|
}
|
|
391
|
+
scheduleCooldownRecovery(state) {
|
|
392
|
+
if (state.cooldownRetryCount >= this.maxCooldownRetries) {
|
|
393
|
+
(0, runtime_1.emitNervesEvent)({
|
|
394
|
+
level: "error",
|
|
395
|
+
component: "daemon",
|
|
396
|
+
event: "daemon.agent_permanent_failure",
|
|
397
|
+
message: "managed agent exhausted all cooldown retries — permanently stopped",
|
|
398
|
+
meta: { agent: state.config.name, cooldownRetryCount: state.cooldownRetryCount, maxCooldownRetries: this.maxCooldownRetries },
|
|
399
|
+
});
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
this.clearCooldownTimer(state);
|
|
403
|
+
state.cooldownTimer = this.setTimeoutFn(() => {
|
|
404
|
+
state.cooldownRetryCount += 1;
|
|
405
|
+
state.crashTimestamps = [];
|
|
406
|
+
state.snapshot.backoffMs = this.initialBackoffMs;
|
|
407
|
+
state.snapshot.status = "starting";
|
|
408
|
+
state.snapshot.restartCount += 1;
|
|
409
|
+
(0, runtime_1.emitNervesEvent)({
|
|
410
|
+
component: "daemon",
|
|
411
|
+
event: "daemon.agent_cooldown_recovery",
|
|
412
|
+
message: "attempting cooldown recovery for managed agent",
|
|
413
|
+
meta: { agent: state.config.name, cooldownRetryCount: state.cooldownRetryCount },
|
|
414
|
+
});
|
|
415
|
+
void this.startAgent(state.config.name);
|
|
416
|
+
}, this.cooldownRecoveryMs);
|
|
417
|
+
(0, runtime_1.emitNervesEvent)({
|
|
418
|
+
component: "daemon",
|
|
419
|
+
event: "daemon.agent_cooldown_scheduled",
|
|
420
|
+
message: `scheduled cooldown recovery in ${this.cooldownRecoveryMs}ms`,
|
|
421
|
+
meta: { agent: state.config.name, cooldownRecoveryMs: this.cooldownRecoveryMs, cooldownRetryCount: state.cooldownRetryCount },
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
clearCooldownTimer(state) {
|
|
425
|
+
if (state.cooldownTimer === null)
|
|
426
|
+
return;
|
|
427
|
+
this.clearTimeoutFn(state.cooldownTimer);
|
|
428
|
+
state.cooldownTimer = null;
|
|
429
|
+
}
|
|
229
430
|
requireAgent(agent) {
|
|
230
431
|
const state = this.agents.get(agent);
|
|
231
432
|
if (!state) {
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Shared provider discovery — single path for finding a working LLM provider.
|
|
4
|
+
*
|
|
5
|
+
* Scans disk credentials and environment variables, deduplicates by provider
|
|
6
|
+
* (disk first), then pings each candidate to validate credentials actually work.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.scanEnvVarCredentials = scanEnvVarCredentials;
|
|
10
|
+
exports.discoverWorkingProvider = discoverWorkingProvider;
|
|
11
|
+
const identity_1 = require("../identity");
|
|
12
|
+
const runtime_1 = require("../../nerves/runtime");
|
|
13
|
+
/**
|
|
14
|
+
* Scan environment variables for API keys using the canonical PROVIDER_CREDENTIALS descriptor.
|
|
15
|
+
* Returns one DiscoveredCredential per provider that has at least one required field set.
|
|
16
|
+
*/
|
|
17
|
+
function scanEnvVarCredentials(env) {
|
|
18
|
+
const results = [];
|
|
19
|
+
for (const [provider, desc] of Object.entries(identity_1.PROVIDER_CREDENTIALS)) {
|
|
20
|
+
const cred = {};
|
|
21
|
+
for (const [envVar, credKey] of Object.entries(desc.envVars)) {
|
|
22
|
+
const value = env[envVar];
|
|
23
|
+
if (value) {
|
|
24
|
+
cred[credKey] = value;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// Only register if at least one required field was found
|
|
28
|
+
const hasRequired = desc.required.some((key) => !!cred[key]);
|
|
29
|
+
if (hasRequired) {
|
|
30
|
+
results.push({
|
|
31
|
+
provider,
|
|
32
|
+
agentName: "env",
|
|
33
|
+
credentials: cred,
|
|
34
|
+
providerConfig: { ...cred },
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return results;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Discover the first working provider by scanning disk credentials and env vars,
|
|
42
|
+
* deduplicating by provider (disk first), and pinging each candidate.
|
|
43
|
+
*/
|
|
44
|
+
async function discoverWorkingProvider(deps) {
|
|
45
|
+
const diskCreds = deps.discoverExistingCredentials(deps.secretsRoot);
|
|
46
|
+
const envCreds = scanEnvVarCredentials(deps.env);
|
|
47
|
+
// Deduplicate: disk credentials first, env vars as fallback for providers not on disk
|
|
48
|
+
const seenProviders = new Set();
|
|
49
|
+
const candidates = [];
|
|
50
|
+
for (const cred of diskCreds) {
|
|
51
|
+
if (!seenProviders.has(cred.provider)) {
|
|
52
|
+
seenProviders.add(cred.provider);
|
|
53
|
+
candidates.push(cred);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
for (const cred of envCreds) {
|
|
57
|
+
if (!seenProviders.has(cred.provider)) {
|
|
58
|
+
seenProviders.add(cred.provider);
|
|
59
|
+
candidates.push(cred);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (candidates.length === 0) {
|
|
63
|
+
(0, runtime_1.emitNervesEvent)({
|
|
64
|
+
level: "info",
|
|
65
|
+
component: "daemon",
|
|
66
|
+
event: "daemon.provider_discovery_none",
|
|
67
|
+
message: "no provider credentials found on disk or in environment",
|
|
68
|
+
meta: {},
|
|
69
|
+
});
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
for (const candidate of candidates) {
|
|
73
|
+
const config = { ...candidate.providerConfig, ...candidate.credentials };
|
|
74
|
+
(0, runtime_1.emitNervesEvent)({
|
|
75
|
+
level: "info",
|
|
76
|
+
component: "daemon",
|
|
77
|
+
event: "daemon.provider_discovery_ping",
|
|
78
|
+
message: `pinging provider: ${candidate.provider}`,
|
|
79
|
+
meta: { provider: candidate.provider, source: candidate.agentName },
|
|
80
|
+
});
|
|
81
|
+
const result = await deps.pingProvider(candidate.provider, config);
|
|
82
|
+
if (result.ok) {
|
|
83
|
+
(0, runtime_1.emitNervesEvent)({
|
|
84
|
+
level: "info",
|
|
85
|
+
component: "daemon",
|
|
86
|
+
event: "daemon.provider_discovery_ok",
|
|
87
|
+
message: `provider discovery succeeded: ${candidate.provider}`,
|
|
88
|
+
meta: { provider: candidate.provider, source: candidate.agentName },
|
|
89
|
+
});
|
|
90
|
+
return {
|
|
91
|
+
provider: candidate.provider,
|
|
92
|
+
credentials: candidate.credentials,
|
|
93
|
+
providerConfig: candidate.providerConfig,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
(0, runtime_1.emitNervesEvent)({
|
|
98
|
+
level: "warn",
|
|
99
|
+
component: "daemon",
|
|
100
|
+
event: "daemon.provider_discovery_all_failed",
|
|
101
|
+
message: "all provider candidates failed ping",
|
|
102
|
+
meta: { candidateCount: candidates.length },
|
|
103
|
+
});
|
|
104
|
+
return null;
|
|
105
|
+
}
|