@ouro.bot/cli 0.1.0-alpha.17 → 0.1.0-alpha.170
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/AdoptionSpecialist.ouro/agent.json +3 -2
- package/AdoptionSpecialist.ouro/psyche/SOUL.md +3 -3
- package/AdoptionSpecialist.ouro/psyche/identities/monty.md +2 -2
- package/README.md +187 -191
- package/changelog.json +1145 -0
- package/dist/heart/active-work.js +633 -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/commitments.js +105 -0
- package/dist/heart/config.js +40 -30
- package/dist/heart/core.js +608 -123
- package/dist/heart/cross-chat-delivery.js +146 -0
- package/dist/heart/daemon/agent-discovery.js +81 -0
- package/dist/heart/daemon/agent-service.js +360 -0
- package/dist/heart/daemon/auth-flow.js +477 -0
- package/dist/heart/daemon/cadence.js +70 -0
- package/dist/heart/daemon/daemon-cli.js +2473 -206
- package/dist/heart/daemon/daemon-entry.js +140 -4
- package/dist/heart/daemon/daemon-health.js +136 -0
- package/dist/heart/daemon/daemon-runtime-sync.js +212 -0
- package/dist/heart/daemon/daemon-tombstone.js +123 -0
- package/dist/heart/daemon/daemon.js +446 -17
- package/dist/heart/daemon/habit-migration.js +181 -0
- package/dist/heart/daemon/habit-parser.js +119 -0
- package/dist/heart/daemon/habit-scheduler.js +369 -0
- package/dist/heart/daemon/hatch-animation.js +10 -3
- package/dist/heart/daemon/hatch-flow.js +30 -111
- package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
- package/dist/heart/daemon/hooks/bundle-meta.js +92 -0
- package/dist/heart/daemon/inner-status.js +89 -0
- package/dist/heart/daemon/launchd.js +161 -0
- package/dist/heart/daemon/log-tailer.js +4 -3
- package/dist/heart/daemon/mcp-server.js +591 -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-global-installer.js +128 -0
- package/dist/heart/daemon/ouro-path-installer.js +78 -34
- package/dist/heart/daemon/ouro-version-manager.js +171 -0
- package/dist/heart/daemon/outlook-http.js +399 -0
- package/dist/heart/daemon/outlook-read.js +1291 -0
- package/dist/heart/daemon/outlook-render.js +1032 -0
- package/dist/heart/daemon/outlook-types.js +7 -0
- package/dist/heart/daemon/outlook-view.js +194 -0
- package/dist/heart/daemon/process-manager.js +75 -0
- package/dist/heart/daemon/run-hooks.js +39 -0
- package/dist/heart/daemon/runtime-logging.js +58 -15
- package/dist/heart/daemon/runtime-metadata.js +219 -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 +50 -2
- 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 +202 -0
- package/dist/heart/daemon/specialist-orchestrator.js +2 -2
- package/dist/heart/daemon/specialist-prompt.js +13 -8
- package/dist/heart/daemon/specialist-tools.js +53 -4
- package/dist/heart/daemon/staged-restart.js +114 -0
- package/dist/heart/daemon/task-scheduler.js +3 -25
- package/dist/heart/daemon/thoughts.js +507 -0
- package/dist/heart/daemon/update-checker.js +111 -0
- package/dist/heart/daemon/update-hooks.js +138 -0
- package/dist/heart/daemon/wrapper-publish-guard.js +86 -0
- package/dist/heart/delegation.js +62 -0
- package/dist/heart/identity.js +136 -55
- package/dist/heart/kicks.js +2 -20
- package/dist/heart/migrate-config.js +127 -0
- package/dist/heart/model-capabilities.js +48 -0
- package/dist/heart/obligations.js +197 -0
- package/dist/heart/progress-story.js +42 -0
- package/dist/heart/provider-failover.js +88 -0
- package/dist/heart/provider-ping.js +172 -0
- package/dist/heart/providers/anthropic-token.js +163 -0
- package/dist/heart/providers/anthropic.js +196 -36
- package/dist/heart/providers/azure.js +116 -10
- package/dist/heart/providers/github-copilot.js +157 -0
- package/dist/heart/providers/minimax.js +33 -3
- package/dist/heart/providers/openai-codex.js +51 -16
- package/dist/heart/session-activity.js +172 -0
- package/dist/heart/session-recall.js +216 -0
- package/dist/heart/streaming.js +62 -32
- package/dist/heart/target-resolution.js +127 -0
- package/dist/heart/tool-activity-callbacks.js +36 -0
- package/dist/heart/tool-description.js +135 -0
- package/dist/heart/tool-loop.js +200 -0
- package/dist/heart/turn-coordinator.js +28 -0
- package/dist/mind/associative-recall.js +122 -32
- package/dist/mind/bundle-manifest.js +14 -0
- package/dist/mind/context.js +125 -14
- package/dist/mind/{memory.js → diary.js} +70 -56
- package/dist/mind/first-impressions.js +16 -2
- package/dist/mind/friends/channel.js +56 -0
- package/dist/mind/friends/group-context.js +144 -0
- package/dist/mind/friends/store-file.js +19 -0
- 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/obligations.js +134 -0
- package/dist/mind/pending.js +76 -9
- package/dist/mind/phrases.js +1 -0
- package/dist/mind/prompt.js +649 -89
- package/dist/mind/token-estimate.js +8 -12
- package/dist/nerves/cli-logging.js +15 -2
- package/dist/nerves/coverage/run-artifacts.js +1 -1
- package/dist/nerves/event-buffer.js +111 -0
- package/dist/nerves/index.js +63 -3
- package/dist/nerves/observation.js +20 -0
- package/dist/nerves/runtime.js +5 -1
- package/dist/outlook-ui/assets/index-CBueh0d3.css +1 -0
- package/dist/outlook-ui/assets/index-GtHoFpRM.js +61 -0
- package/dist/outlook-ui/index.html +15 -0
- package/dist/repertoire/ado-semantic.js +6 -0
- package/dist/repertoire/coding/codex-jsonl.js +64 -0
- package/dist/repertoire/coding/context-pack.js +254 -0
- package/dist/repertoire/coding/feedback.js +197 -30
- package/dist/repertoire/coding/manager.js +149 -2
- package/dist/repertoire/coding/spawner.js +55 -9
- package/dist/repertoire/coding/tools.js +135 -2
- package/dist/repertoire/guardrails.js +291 -0
- package/dist/repertoire/mcp-client.js +254 -0
- package/dist/repertoire/mcp-manager.js +198 -0
- package/dist/repertoire/skills.js +14 -23
- 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 +11 -28
- package/dist/repertoire/tools-base.js +991 -272
- package/dist/repertoire/tools-bluebubbles.js +94 -0
- package/dist/repertoire/tools-github.js +1 -6
- package/dist/repertoire/tools-teams.js +12 -59
- package/dist/repertoire/tools.js +281 -122
- 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 +97 -0
- package/dist/senses/bluebubbles-client.js +102 -3
- package/dist/senses/bluebubbles-entry.js +2 -0
- package/dist/senses/bluebubbles-inbound-log.js +109 -0
- package/dist/senses/bluebubbles-media.js +118 -23
- package/dist/senses/bluebubbles-model.js +12 -4
- package/dist/senses/bluebubbles-mutation-log.js +45 -5
- package/dist/senses/bluebubbles-runtime-state.js +109 -0
- package/dist/senses/bluebubbles-session-cleanup.js +72 -0
- package/dist/senses/bluebubbles.js +1008 -102
- package/dist/senses/cli-entry.js +1 -1
- package/dist/senses/cli-layout.js +187 -0
- package/dist/senses/cli.js +419 -152
- package/dist/senses/commands.js +38 -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 +592 -84
- package/dist/senses/pipeline.js +502 -0
- package/dist/senses/shared-turn.js +196 -0
- package/dist/senses/surface-tool.js +95 -0
- package/dist/senses/teams.js +667 -120
- package/dist/senses/trust-gate.js +112 -2
- package/package.json +13 -5
- package/skills/configure-dev-tools.md +81 -0
- package/subagents/README.md +4 -70
- package/dist/heart/daemon/subagent-installer.js +0 -134
- package/dist/senses/debug-activity.js +0 -107
- package/subagents/work-doer.md +0 -235
- package/subagents/work-merger.md +0 -618
- package/subagents/work-planner.md +0 -382
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// v1 → v2 agent.json migration.
|
|
3
|
+
// Moves model from secrets.json into agent.json under humanFacing/agentFacing.
|
|
4
|
+
// Uses raw fs — NO import from config.ts (avoids circular dependency).
|
|
5
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
8
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
9
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
10
|
+
}
|
|
11
|
+
Object.defineProperty(o, k2, desc);
|
|
12
|
+
}) : (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
o[k2] = m[k];
|
|
15
|
+
}));
|
|
16
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
17
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
18
|
+
}) : function(o, v) {
|
|
19
|
+
o["default"] = v;
|
|
20
|
+
});
|
|
21
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
22
|
+
var ownKeys = function(o) {
|
|
23
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
24
|
+
var ar = [];
|
|
25
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
26
|
+
return ar;
|
|
27
|
+
};
|
|
28
|
+
return ownKeys(o);
|
|
29
|
+
};
|
|
30
|
+
return function (mod) {
|
|
31
|
+
if (mod && mod.__esModule) return mod;
|
|
32
|
+
var result = {};
|
|
33
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
34
|
+
__setModuleDefault(result, mod);
|
|
35
|
+
return result;
|
|
36
|
+
};
|
|
37
|
+
})();
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.migrateAgentConfigV1ToV2 = migrateAgentConfigV1ToV2;
|
|
40
|
+
const fs = __importStar(require("fs"));
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
const runtime_1 = require("../nerves/runtime");
|
|
43
|
+
const MODEL_FIELD = {
|
|
44
|
+
azure: "modelName",
|
|
45
|
+
minimax: "model",
|
|
46
|
+
anthropic: "model",
|
|
47
|
+
"openai-codex": "model",
|
|
48
|
+
"github-copilot": "model",
|
|
49
|
+
};
|
|
50
|
+
function resolveSecretsPath(agentRoot, deps) {
|
|
51
|
+
const agentName = path.basename(agentRoot).replace(/\.ouro$/, "");
|
|
52
|
+
const secretsBase = deps?.secretsRoot ?? path.join(require("os").homedir(), ".agentsecrets");
|
|
53
|
+
return path.join(secretsBase, agentName, "secrets.json");
|
|
54
|
+
}
|
|
55
|
+
function migrateAgentConfigV1ToV2(agentRoot, deps) {
|
|
56
|
+
const configPath = path.join(agentRoot, "agent.json");
|
|
57
|
+
let raw;
|
|
58
|
+
try {
|
|
59
|
+
raw = fs.readFileSync(configPath, "utf-8");
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return; // No agent.json — nothing to migrate
|
|
63
|
+
}
|
|
64
|
+
let config;
|
|
65
|
+
try {
|
|
66
|
+
config = JSON.parse(raw);
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
throw new Error(`Cannot parse agent.json at ${configPath} — invalid JSON`);
|
|
70
|
+
}
|
|
71
|
+
const version = typeof config.version === "number" ? config.version : 1;
|
|
72
|
+
if (version >= 2) {
|
|
73
|
+
(0, runtime_1.emitNervesEvent)({
|
|
74
|
+
component: "config/identity",
|
|
75
|
+
event: "config_identity.migrate_skip",
|
|
76
|
+
message: "agent config already v2, skipping migration",
|
|
77
|
+
meta: { agentRoot },
|
|
78
|
+
});
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
(0, runtime_1.emitNervesEvent)({
|
|
82
|
+
component: "config/identity",
|
|
83
|
+
event: "config_identity.migrate_start",
|
|
84
|
+
message: "migrating agent config v1 → v2",
|
|
85
|
+
meta: { agentRoot },
|
|
86
|
+
});
|
|
87
|
+
const provider = config.provider;
|
|
88
|
+
let model = "";
|
|
89
|
+
if (provider) {
|
|
90
|
+
const secretsPath = resolveSecretsPath(agentRoot, deps);
|
|
91
|
+
try {
|
|
92
|
+
const secretsRaw = fs.readFileSync(secretsPath, "utf-8");
|
|
93
|
+
const secrets = JSON.parse(secretsRaw);
|
|
94
|
+
const providers = secrets.providers;
|
|
95
|
+
if (providers) {
|
|
96
|
+
const providerSecrets = providers[provider];
|
|
97
|
+
if (providerSecrets) {
|
|
98
|
+
/* v8 ignore next -- fallback: all known providers are in MODEL_FIELD @preserve */
|
|
99
|
+
const fieldName = MODEL_FIELD[provider] ?? "model";
|
|
100
|
+
const rawModel = providerSecrets[fieldName];
|
|
101
|
+
model = typeof rawModel === "string" ? rawModel : "";
|
|
102
|
+
// Strip model from secrets
|
|
103
|
+
delete providerSecrets[fieldName];
|
|
104
|
+
fs.writeFileSync(secretsPath, JSON.stringify(secrets, null, 2) + "\n", "utf-8");
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
// Missing or malformed secrets.json — leave model as empty string
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// Write v2 config
|
|
113
|
+
const { provider: _removed, ...rest } = config;
|
|
114
|
+
const v2Config = {
|
|
115
|
+
...rest,
|
|
116
|
+
version: 2,
|
|
117
|
+
humanFacing: { provider: provider ?? "anthropic", model },
|
|
118
|
+
agentFacing: { provider: provider ?? "anthropic", model },
|
|
119
|
+
};
|
|
120
|
+
fs.writeFileSync(configPath, JSON.stringify(v2Config, null, 2) + "\n", "utf-8");
|
|
121
|
+
(0, runtime_1.emitNervesEvent)({
|
|
122
|
+
component: "config/identity",
|
|
123
|
+
event: "config_identity.migrate_end",
|
|
124
|
+
message: "agent config migration complete",
|
|
125
|
+
meta: { agentRoot, provider: provider ?? "unknown", model },
|
|
126
|
+
});
|
|
127
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MODEL_CAPABILITIES = void 0;
|
|
4
|
+
exports.getModelCapabilities = getModelCapabilities;
|
|
5
|
+
const runtime_1 = require("../nerves/runtime");
|
|
6
|
+
exports.MODEL_CAPABILITIES = {
|
|
7
|
+
"claude-opus-4-6": {
|
|
8
|
+
reasoningEffort: ["low", "medium", "high", "max"],
|
|
9
|
+
thinkingFormat: "anthropic",
|
|
10
|
+
maxOutputTokens: 128000,
|
|
11
|
+
},
|
|
12
|
+
"claude-sonnet-4-6": {
|
|
13
|
+
reasoningEffort: ["low", "medium", "high"],
|
|
14
|
+
thinkingFormat: "anthropic",
|
|
15
|
+
maxOutputTokens: 64000,
|
|
16
|
+
},
|
|
17
|
+
"claude-opus-4.6": {
|
|
18
|
+
reasoningEffort: ["low", "medium", "high", "max"],
|
|
19
|
+
maxOutputTokens: 128000,
|
|
20
|
+
},
|
|
21
|
+
"claude-sonnet-4.6": {
|
|
22
|
+
reasoningEffort: ["low", "medium", "high"],
|
|
23
|
+
maxOutputTokens: 64000,
|
|
24
|
+
},
|
|
25
|
+
"gpt-5.4": {
|
|
26
|
+
reasoningEffort: ["low", "medium", "high"],
|
|
27
|
+
phase: true,
|
|
28
|
+
maxOutputTokens: 100000,
|
|
29
|
+
},
|
|
30
|
+
"gpt-5.3-codex": {
|
|
31
|
+
reasoningEffort: ["low", "medium", "high"],
|
|
32
|
+
phase: true,
|
|
33
|
+
maxOutputTokens: 100000,
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
const EMPTY_CAPABILITIES = Object.freeze({});
|
|
37
|
+
function getModelCapabilities(modelId) {
|
|
38
|
+
(0, runtime_1.emitNervesEvent)({
|
|
39
|
+
component: "engine",
|
|
40
|
+
event: "engine.model_capabilities_lookup",
|
|
41
|
+
message: `model capabilities lookup: ${modelId}`,
|
|
42
|
+
meta: { modelId, found: modelId in exports.MODEL_CAPABILITIES },
|
|
43
|
+
});
|
|
44
|
+
const entry = exports.MODEL_CAPABILITIES[modelId];
|
|
45
|
+
if (entry)
|
|
46
|
+
return entry;
|
|
47
|
+
return { ...EMPTY_CAPABILITIES };
|
|
48
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
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.isOpenObligationStatus = isOpenObligationStatus;
|
|
37
|
+
exports.isOpenObligation = isOpenObligation;
|
|
38
|
+
exports.createObligation = createObligation;
|
|
39
|
+
exports.readObligations = readObligations;
|
|
40
|
+
exports.readPendingObligations = readPendingObligations;
|
|
41
|
+
exports.advanceObligation = advanceObligation;
|
|
42
|
+
exports.fulfillObligation = fulfillObligation;
|
|
43
|
+
exports.findPendingObligationForOrigin = findPendingObligationForOrigin;
|
|
44
|
+
const fs = __importStar(require("fs"));
|
|
45
|
+
const path = __importStar(require("path"));
|
|
46
|
+
const runtime_1 = require("../nerves/runtime");
|
|
47
|
+
function obligationsDir(agentRoot) {
|
|
48
|
+
return path.join(agentRoot, "state", "obligations");
|
|
49
|
+
}
|
|
50
|
+
function obligationFilePath(agentRoot, id) {
|
|
51
|
+
return path.join(obligationsDir(agentRoot), `${id}.json`);
|
|
52
|
+
}
|
|
53
|
+
function generateId() {
|
|
54
|
+
const timestamp = Date.now();
|
|
55
|
+
const random = Math.random().toString(36).slice(2, 10);
|
|
56
|
+
return `${timestamp}-${random}`;
|
|
57
|
+
}
|
|
58
|
+
function isOpenObligationStatus(status) {
|
|
59
|
+
return status !== "fulfilled";
|
|
60
|
+
}
|
|
61
|
+
function isOpenObligation(obligation) {
|
|
62
|
+
return isOpenObligationStatus(obligation.status);
|
|
63
|
+
}
|
|
64
|
+
function createObligation(agentRoot, input) {
|
|
65
|
+
const now = new Date().toISOString();
|
|
66
|
+
const id = generateId();
|
|
67
|
+
const obligation = {
|
|
68
|
+
id,
|
|
69
|
+
origin: input.origin,
|
|
70
|
+
...(input.bridgeId ? { bridgeId: input.bridgeId } : {}),
|
|
71
|
+
content: input.content,
|
|
72
|
+
status: "pending",
|
|
73
|
+
createdAt: now,
|
|
74
|
+
updatedAt: now,
|
|
75
|
+
};
|
|
76
|
+
const dir = obligationsDir(agentRoot);
|
|
77
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
78
|
+
fs.writeFileSync(obligationFilePath(agentRoot, id), JSON.stringify(obligation, null, 2), "utf-8");
|
|
79
|
+
(0, runtime_1.emitNervesEvent)({
|
|
80
|
+
component: "engine",
|
|
81
|
+
event: "engine.obligation_created",
|
|
82
|
+
message: "obligation created",
|
|
83
|
+
meta: {
|
|
84
|
+
obligationId: id,
|
|
85
|
+
friendId: input.origin.friendId,
|
|
86
|
+
channel: input.origin.channel,
|
|
87
|
+
key: input.origin.key,
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
return obligation;
|
|
91
|
+
}
|
|
92
|
+
function readObligations(agentRoot) {
|
|
93
|
+
const dir = obligationsDir(agentRoot);
|
|
94
|
+
if (!fs.existsSync(dir))
|
|
95
|
+
return [];
|
|
96
|
+
let entries;
|
|
97
|
+
try {
|
|
98
|
+
entries = fs.readdirSync(dir);
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
/* v8 ignore next -- defensive: readdirSync race after existsSync @preserve */
|
|
102
|
+
return [];
|
|
103
|
+
}
|
|
104
|
+
const jsonFiles = entries.filter((entry) => entry.endsWith(".json")).sort();
|
|
105
|
+
const obligations = [];
|
|
106
|
+
for (const file of jsonFiles) {
|
|
107
|
+
try {
|
|
108
|
+
const raw = fs.readFileSync(path.join(dir, file), "utf-8");
|
|
109
|
+
const parsed = JSON.parse(raw);
|
|
110
|
+
if (typeof parsed.id === "string" && typeof parsed.content === "string") {
|
|
111
|
+
obligations.push(parsed);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
// skip malformed files
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return obligations;
|
|
119
|
+
}
|
|
120
|
+
function readPendingObligations(agentRoot) {
|
|
121
|
+
return readObligations(agentRoot).filter(isOpenObligation);
|
|
122
|
+
}
|
|
123
|
+
function advanceObligation(agentRoot, obligationId, update) {
|
|
124
|
+
const filePath = obligationFilePath(agentRoot, obligationId);
|
|
125
|
+
let obligation;
|
|
126
|
+
try {
|
|
127
|
+
const raw = fs.readFileSync(filePath, "utf-8");
|
|
128
|
+
obligation = JSON.parse(raw);
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
const previousStatus = obligation.status;
|
|
134
|
+
if (update.status) {
|
|
135
|
+
obligation.status = update.status;
|
|
136
|
+
if (update.status === "fulfilled") {
|
|
137
|
+
obligation.fulfilledAt = new Date().toISOString();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (update.currentSurface) {
|
|
141
|
+
obligation.currentSurface = update.currentSurface;
|
|
142
|
+
}
|
|
143
|
+
if (typeof update.currentArtifact === "string") {
|
|
144
|
+
obligation.currentArtifact = update.currentArtifact;
|
|
145
|
+
}
|
|
146
|
+
if (typeof update.nextAction === "string") {
|
|
147
|
+
obligation.nextAction = update.nextAction;
|
|
148
|
+
}
|
|
149
|
+
if (typeof update.latestNote === "string") {
|
|
150
|
+
obligation.latestNote = update.latestNote;
|
|
151
|
+
}
|
|
152
|
+
obligation.updatedAt = new Date().toISOString();
|
|
153
|
+
fs.writeFileSync(filePath, JSON.stringify(obligation, null, 2), "utf-8");
|
|
154
|
+
(0, runtime_1.emitNervesEvent)({
|
|
155
|
+
component: "engine",
|
|
156
|
+
event: "engine.obligation_advanced",
|
|
157
|
+
message: "obligation advanced",
|
|
158
|
+
meta: {
|
|
159
|
+
obligationId,
|
|
160
|
+
previousStatus,
|
|
161
|
+
status: obligation.status,
|
|
162
|
+
friendId: obligation.origin.friendId,
|
|
163
|
+
channel: obligation.origin.channel,
|
|
164
|
+
key: obligation.origin.key,
|
|
165
|
+
surfaceKind: obligation.currentSurface?.kind ?? null,
|
|
166
|
+
surfaceLabel: obligation.currentSurface?.label ?? null,
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
function fulfillObligation(agentRoot, obligationId) {
|
|
171
|
+
advanceObligation(agentRoot, obligationId, { status: "fulfilled" });
|
|
172
|
+
const filePath = obligationFilePath(agentRoot, obligationId);
|
|
173
|
+
let obligation;
|
|
174
|
+
try {
|
|
175
|
+
const raw = fs.readFileSync(filePath, "utf-8");
|
|
176
|
+
obligation = JSON.parse(raw);
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
(0, runtime_1.emitNervesEvent)({
|
|
182
|
+
component: "engine",
|
|
183
|
+
event: "engine.obligation_fulfilled",
|
|
184
|
+
message: "obligation fulfilled",
|
|
185
|
+
meta: {
|
|
186
|
+
obligationId,
|
|
187
|
+
friendId: obligation.origin.friendId,
|
|
188
|
+
channel: obligation.origin.channel,
|
|
189
|
+
key: obligation.origin.key,
|
|
190
|
+
},
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
function findPendingObligationForOrigin(agentRoot, origin) {
|
|
194
|
+
return readPendingObligations(agentRoot).find((ob) => ob.origin.friendId === origin.friendId
|
|
195
|
+
&& ob.origin.channel === origin.channel
|
|
196
|
+
&& ob.origin.key === origin.key);
|
|
197
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildProgressStory = buildProgressStory;
|
|
4
|
+
exports.renderProgressStory = renderProgressStory;
|
|
5
|
+
const runtime_1 = require("../nerves/runtime");
|
|
6
|
+
function labelForScope(scope) {
|
|
7
|
+
return scope === "inner-delegation" ? "inner work" : "shared work";
|
|
8
|
+
}
|
|
9
|
+
function compactDetail(text) {
|
|
10
|
+
if (typeof text !== "string")
|
|
11
|
+
return null;
|
|
12
|
+
const trimmed = text.trim();
|
|
13
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
14
|
+
}
|
|
15
|
+
function buildProgressStory(input) {
|
|
16
|
+
const detailLines = [
|
|
17
|
+
compactDetail(input.objective),
|
|
18
|
+
compactDetail(input.outcomeText),
|
|
19
|
+
compactDetail(input.bridgeId ? `bridge: ${input.bridgeId}` : null),
|
|
20
|
+
compactDetail(input.taskName ? `task: ${input.taskName}` : null),
|
|
21
|
+
].filter((line) => Boolean(line));
|
|
22
|
+
const story = {
|
|
23
|
+
statusLine: `${labelForScope(input.scope)}: ${input.phase}`,
|
|
24
|
+
detailLines,
|
|
25
|
+
};
|
|
26
|
+
(0, runtime_1.emitNervesEvent)({
|
|
27
|
+
component: "engine",
|
|
28
|
+
event: "engine.progress_story_build",
|
|
29
|
+
message: "built shared progress story",
|
|
30
|
+
meta: {
|
|
31
|
+
scope: input.scope,
|
|
32
|
+
phase: input.phase,
|
|
33
|
+
detailLines: detailLines.length,
|
|
34
|
+
hasBridge: Boolean(input.bridgeId),
|
|
35
|
+
hasTask: Boolean(input.taskName),
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
return story;
|
|
39
|
+
}
|
|
40
|
+
function renderProgressStory(story) {
|
|
41
|
+
return [story.statusLine, ...story.detailLines].join("\n");
|
|
42
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildFailoverContext = buildFailoverContext;
|
|
4
|
+
exports.handleFailoverReply = handleFailoverReply;
|
|
5
|
+
const runtime_1 = require("../nerves/runtime");
|
|
6
|
+
const FAILING_PROVIDER_LABELS = {
|
|
7
|
+
"auth-failure": "its credentials need to be refreshed",
|
|
8
|
+
"usage-limit": "has also hit its usage limit",
|
|
9
|
+
"rate-limit": "is also being rate limited",
|
|
10
|
+
"server-error": "is also experiencing an outage",
|
|
11
|
+
"network-error": "could not be reached",
|
|
12
|
+
"unknown": "could not be reached",
|
|
13
|
+
};
|
|
14
|
+
const CLASSIFICATION_LABELS = {
|
|
15
|
+
"auth-failure": "authentication failed",
|
|
16
|
+
"usage-limit": "hit its usage limit",
|
|
17
|
+
"rate-limit": "is being rate limited",
|
|
18
|
+
"server-error": "is experiencing an outage",
|
|
19
|
+
"network-error": "is unreachable (network error)",
|
|
20
|
+
"unknown": "encountered an error",
|
|
21
|
+
};
|
|
22
|
+
function buildFailoverContext(_errorMessage, classification, currentProvider, currentModel, agentName, inventory, providerModels) {
|
|
23
|
+
const label = CLASSIFICATION_LABELS[classification];
|
|
24
|
+
const providerWithModel = currentModel ? `${currentProvider} (${currentModel})` : currentProvider;
|
|
25
|
+
const errorSummary = `${providerWithModel} ${label}`;
|
|
26
|
+
const workingProviders = [];
|
|
27
|
+
const unconfiguredProviders = [];
|
|
28
|
+
const failingProviders = [];
|
|
29
|
+
for (const [provider, result] of Object.entries(inventory)) {
|
|
30
|
+
if (result.ok) {
|
|
31
|
+
workingProviders.push(provider);
|
|
32
|
+
}
|
|
33
|
+
else if (result.classification === "auth-failure" && result.message === "no credentials configured") {
|
|
34
|
+
unconfiguredProviders.push(provider);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
// Configured but ping failed (expired token, provider also down, etc.)
|
|
38
|
+
failingProviders.push({ provider, classification: result.classification });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const lines = [`${errorSummary}.`];
|
|
42
|
+
if (workingProviders.length > 0) {
|
|
43
|
+
const switchDescriptions = workingProviders.map((p) => {
|
|
44
|
+
const model = providerModels[p];
|
|
45
|
+
return model ? `${p} (${model})` : /* v8 ignore next -- defensive: model always present in secrets @preserve */ p;
|
|
46
|
+
});
|
|
47
|
+
const switchOptions = workingProviders.map((p) => `"switch to ${p}"`).join(" or ");
|
|
48
|
+
lines.push(`these providers are ready to go: ${switchDescriptions.join(", ")}.`);
|
|
49
|
+
lines.push(`reply ${switchOptions} to continue.`);
|
|
50
|
+
}
|
|
51
|
+
if (failingProviders.length > 0) {
|
|
52
|
+
for (const { provider, classification } of failingProviders) {
|
|
53
|
+
/* v8 ignore next -- defensive: all classifications have labels @preserve */
|
|
54
|
+
const detail = FAILING_PROVIDER_LABELS[classification] ?? "could not be reached";
|
|
55
|
+
lines.push(`${provider} is configured but ${detail}. run \`ouro auth --agent ${agentName} --provider ${provider}\` to refresh.`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (unconfiguredProviders.length > 0) {
|
|
59
|
+
lines.push(`to set up ${unconfiguredProviders.join(", ")}, run \`ouro auth --agent ${agentName}\` in terminal.`);
|
|
60
|
+
}
|
|
61
|
+
if (workingProviders.length === 0 && unconfiguredProviders.length === 0 && failingProviders.length === 0) {
|
|
62
|
+
lines.push(`no other providers are available. run \`ouro auth --agent ${agentName}\` in terminal to configure one.`);
|
|
63
|
+
}
|
|
64
|
+
(0, runtime_1.emitNervesEvent)({
|
|
65
|
+
component: "engine",
|
|
66
|
+
event: "engine.failover_context_built",
|
|
67
|
+
message: "built provider failover context",
|
|
68
|
+
meta: { currentProvider, classification, workingProviders, unconfiguredProviders },
|
|
69
|
+
});
|
|
70
|
+
return {
|
|
71
|
+
errorSummary,
|
|
72
|
+
classification,
|
|
73
|
+
currentProvider,
|
|
74
|
+
agentName,
|
|
75
|
+
workingProviders,
|
|
76
|
+
unconfiguredProviders,
|
|
77
|
+
userMessage: lines.join(" "),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function handleFailoverReply(reply, context) {
|
|
81
|
+
const lower = reply.toLowerCase().trim();
|
|
82
|
+
for (const provider of context.workingProviders) {
|
|
83
|
+
if (lower.includes(`switch to ${provider}`) || lower === provider) {
|
|
84
|
+
return { action: "switch", provider };
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return { action: "dismiss" };
|
|
88
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sanitizeErrorMessage = sanitizeErrorMessage;
|
|
4
|
+
exports.pingProvider = pingProvider;
|
|
5
|
+
exports.runHealthInventory = runHealthInventory;
|
|
6
|
+
const anthropic_1 = require("./providers/anthropic");
|
|
7
|
+
const azure_1 = require("./providers/azure");
|
|
8
|
+
const minimax_1 = require("./providers/minimax");
|
|
9
|
+
const openai_codex_1 = require("./providers/openai-codex");
|
|
10
|
+
const github_copilot_1 = require("./providers/github-copilot");
|
|
11
|
+
const auth_flow_1 = require("./daemon/auth-flow");
|
|
12
|
+
const runtime_1 = require("../nerves/runtime");
|
|
13
|
+
const PING_TIMEOUT_MS = 10_000;
|
|
14
|
+
/**
|
|
15
|
+
* Strip raw JSON/HTML API response bodies from error messages.
|
|
16
|
+
* SDK errors often include the full response: "400 {"type":"error",...}" or "403 <html>...".
|
|
17
|
+
* Extract just the HTTP status and a short human-readable summary.
|
|
18
|
+
*/
|
|
19
|
+
function sanitizeErrorMessage(message) {
|
|
20
|
+
const statusMatch = message.match(/^(\d{3})\s/);
|
|
21
|
+
if (!statusMatch)
|
|
22
|
+
return message;
|
|
23
|
+
const status = statusMatch[1];
|
|
24
|
+
const body = message.slice(status.length).trim();
|
|
25
|
+
// HTML response (Cloudflare challenge, error pages, etc.)
|
|
26
|
+
if (body.startsWith("<") || body.includes("<!DOCTYPE") || body.includes("<html")) {
|
|
27
|
+
return `HTTP ${status}`;
|
|
28
|
+
}
|
|
29
|
+
// JSON response
|
|
30
|
+
if (body.startsWith("{")) {
|
|
31
|
+
try {
|
|
32
|
+
const json = JSON.parse(body);
|
|
33
|
+
const inner = json?.error?.message;
|
|
34
|
+
if (typeof inner === "string" && inner && inner !== "Error") {
|
|
35
|
+
return `${status} ${inner}`;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
catch { /* not valid JSON */ }
|
|
39
|
+
return `HTTP ${status}`;
|
|
40
|
+
}
|
|
41
|
+
// Already clean (e.g., "401 Provided authentication token is expired.")
|
|
42
|
+
return message;
|
|
43
|
+
}
|
|
44
|
+
function hasEmptyCredentials(provider, config) {
|
|
45
|
+
switch (provider) {
|
|
46
|
+
case "anthropic":
|
|
47
|
+
return !config.setupToken;
|
|
48
|
+
case "openai-codex":
|
|
49
|
+
return !config.oauthAccessToken;
|
|
50
|
+
case "minimax":
|
|
51
|
+
return !config.apiKey;
|
|
52
|
+
case "azure": {
|
|
53
|
+
const azure = config;
|
|
54
|
+
return !(azure.apiKey && azure.endpoint && azure.deployment);
|
|
55
|
+
}
|
|
56
|
+
case "github-copilot": {
|
|
57
|
+
const copilot = config;
|
|
58
|
+
return !(copilot.githubToken && copilot.baseUrl);
|
|
59
|
+
}
|
|
60
|
+
/* v8 ignore next 2 -- exhaustive: all providers handled above @preserve */
|
|
61
|
+
default:
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function createRuntimeForPing(provider, _config) {
|
|
66
|
+
// The provider constructors read credentials from the active config via getXxxConfig().
|
|
67
|
+
// For ping, this is acceptable because verifyProviderCredentials patches the runtime
|
|
68
|
+
// config before calling ping. The model string is a default — for anthropic the ping
|
|
69
|
+
// overrides it with haiku anyway, and for others it's used in a minimal API call.
|
|
70
|
+
const DEFAULT_MODELS = {
|
|
71
|
+
anthropic: "claude-haiku-4-5-20251001",
|
|
72
|
+
azure: "gpt-4o",
|
|
73
|
+
minimax: "MiniMax-M2.7",
|
|
74
|
+
"openai-codex": "codex-mini-latest",
|
|
75
|
+
"github-copilot": "gpt-4o",
|
|
76
|
+
};
|
|
77
|
+
/* v8 ignore next -- fallback: all known providers are in DEFAULT_MODELS @preserve */
|
|
78
|
+
const model = DEFAULT_MODELS[provider] ?? "unknown";
|
|
79
|
+
switch (provider) {
|
|
80
|
+
case "anthropic":
|
|
81
|
+
return (0, anthropic_1.createAnthropicProviderRuntime)(model);
|
|
82
|
+
case "azure":
|
|
83
|
+
return (0, azure_1.createAzureProviderRuntime)(model);
|
|
84
|
+
case "minimax":
|
|
85
|
+
return (0, minimax_1.createMinimaxProviderRuntime)(model);
|
|
86
|
+
case "openai-codex":
|
|
87
|
+
return (0, openai_codex_1.createOpenAICodexProviderRuntime)(model);
|
|
88
|
+
case "github-copilot":
|
|
89
|
+
return (0, github_copilot_1.createGithubCopilotProviderRuntime)(model);
|
|
90
|
+
/* v8 ignore next 2 -- exhaustive: all providers handled above @preserve */
|
|
91
|
+
default:
|
|
92
|
+
throw new Error(`unsupported provider for ping: ${provider}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
async function pingProvider(provider, config) {
|
|
96
|
+
if (hasEmptyCredentials(provider, config)) {
|
|
97
|
+
return { ok: false, classification: "auth-failure", message: "no credentials configured" };
|
|
98
|
+
}
|
|
99
|
+
let runtime;
|
|
100
|
+
try {
|
|
101
|
+
runtime = createRuntimeForPing(provider, config);
|
|
102
|
+
/* v8 ignore start -- factory creation failure: tested via individual provider init tests @preserve */
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
return {
|
|
106
|
+
ok: false,
|
|
107
|
+
classification: "auth-failure",
|
|
108
|
+
message: error instanceof Error ? error.message : String(error),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
/* v8 ignore stop */
|
|
112
|
+
try {
|
|
113
|
+
const controller = new AbortController();
|
|
114
|
+
/* v8 ignore next -- timeout callback: only fires after 10s, tests resolve faster @preserve */
|
|
115
|
+
const timeout = setTimeout(() => controller.abort(), PING_TIMEOUT_MS);
|
|
116
|
+
try {
|
|
117
|
+
// Minimal API call — no thinking, no reasoning, no tools.
|
|
118
|
+
if (provider === "anthropic") {
|
|
119
|
+
// Use haiku for the ping — setup tokens may not have access to newer
|
|
120
|
+
// models, but if haiku works, the credentials are valid.
|
|
121
|
+
// Override the beta header to exclude thinking (which requires a
|
|
122
|
+
// thinking param in the request body).
|
|
123
|
+
const client = runtime.client;
|
|
124
|
+
await client.messages.create({ model: "claude-haiku-4-5-20251001", max_tokens: 1, messages: [{ role: "user", content: "ping" }] }, { signal: controller.signal, headers: { "anthropic-beta": "claude-code-20250219,oauth-2025-04-20" } });
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
// OpenAI-compatible providers (azure, codex, minimax, github-copilot)
|
|
128
|
+
const client = runtime.client;
|
|
129
|
+
await client.chat.completions.create({ model: runtime.model, max_tokens: 1, messages: [{ role: "user", content: "ping" }] }, { signal: controller.signal });
|
|
130
|
+
}
|
|
131
|
+
return { ok: true };
|
|
132
|
+
}
|
|
133
|
+
finally {
|
|
134
|
+
clearTimeout(timeout);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
const err = error instanceof Error ? error : /* v8 ignore next -- defensive @preserve */ new Error(String(error));
|
|
139
|
+
let classification;
|
|
140
|
+
try {
|
|
141
|
+
classification = runtime.classifyError(err);
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
/* v8 ignore next -- defensive: classifyError should not throw @preserve */
|
|
145
|
+
classification = "unknown";
|
|
146
|
+
}
|
|
147
|
+
(0, runtime_1.emitNervesEvent)({
|
|
148
|
+
component: "engine",
|
|
149
|
+
event: "engine.provider_ping_fail",
|
|
150
|
+
message: `provider ping failed: ${provider}`,
|
|
151
|
+
meta: { provider, classification, error: err.message },
|
|
152
|
+
});
|
|
153
|
+
return { ok: false, classification, message: sanitizeErrorMessage(err.message) };
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
const PINGABLE_PROVIDERS = ["anthropic", "openai-codex", "azure", "minimax", "github-copilot"];
|
|
157
|
+
async function runHealthInventory(agentName, currentProvider, deps = {}) {
|
|
158
|
+
/* v8 ignore next -- default: tests inject ping dep @preserve */
|
|
159
|
+
const ping = deps.ping ?? pingProvider;
|
|
160
|
+
const { secrets } = (0, auth_flow_1.loadAgentSecrets)(agentName);
|
|
161
|
+
const providers = PINGABLE_PROVIDERS.filter((p) => p !== currentProvider);
|
|
162
|
+
const results = await Promise.all(providers.map(async (provider) => {
|
|
163
|
+
const config = secrets.providers[provider];
|
|
164
|
+
const result = await ping(provider, config);
|
|
165
|
+
return [provider, result];
|
|
166
|
+
}));
|
|
167
|
+
const inventory = {};
|
|
168
|
+
for (const [provider, result] of results) {
|
|
169
|
+
inventory[provider] = result;
|
|
170
|
+
}
|
|
171
|
+
return inventory;
|
|
172
|
+
}
|