@ouro.bot/cli 0.1.0-alpha.395 → 0.1.0-alpha.396

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/changelog.json CHANGED
@@ -1,6 +1,17 @@
1
1
  {
2
2
  "_note": "This changelog is maintained as part of the PR/version-bump workflow. Agent-curated, not auto-generated. Agents read this file directly via read_file to understand what changed between versions.",
3
3
  "versions": [
4
+ {
5
+ "version": "0.1.0-alpha.396",
6
+ "changes": [
7
+ "Added `ouro repair [--agent <agent>]`, a deterministic vault/provider readiness guide that uses typed repair issues and clear human choices instead of asking for AI diagnosis on known failures.",
8
+ "Provider health checks now attach shared typed repair metadata for locked vaults, missing provider credentials, and failed live provider/model pings, keeping CLI repair, `ouro up`, and agent-facing guidance aligned.",
9
+ "`ouro up` now always runs the live provider-check phase after startup, not only when the daemon was already running, so broken selected providers are surfaced during startup instead of later through confusing agent crashes.",
10
+ "`ouro vault create --agent <agent>` now defaults to the stable agent vault email when no vault locator exists, while still requiring a non-echoing human-provided unlock secret.",
11
+ "Cross-machine and auth/provider docs now include `ouro repair --agent <agent>` in the continuation path for existing bundles and old auth-style agents.",
12
+ "`@ouro.bot/cli` and the `ouro.bot` wrapper are version-synced for the typed readiness repair release."
13
+ ]
14
+ },
4
15
  {
5
16
  "version": "0.1.0-alpha.395",
6
17
  "changes": [
@@ -45,6 +45,7 @@ const machine_identity_1 = require("../machine-identity");
45
45
  const provider_state_1 = require("../provider-state");
46
46
  const provider_credentials_1 = require("../provider-credentials");
47
47
  const vault_unlock_1 = require("../../repertoire/vault-unlock");
48
+ const readiness_repair_1 = require("./readiness-repair");
48
49
  function isAgentProvider(value) {
49
50
  return Object.prototype.hasOwnProperty.call(identity_1.PROVIDER_CREDENTIALS, value);
50
51
  }
@@ -233,6 +234,13 @@ function missingCredentialResult(agentName, lane, provider, model, credentialPat
233
234
  ok: false,
234
235
  error: `${lane} provider ${provider} model ${model} has no credentials in ${agentName}'s vault at ${credentialPath}`,
235
236
  fix: `Run 'ouro auth --agent ${agentName} --provider ${provider}' to authenticate this machine, or run 'ouro use --agent ${agentName} --lane ${lane} --provider <provider> --model <model>' to choose a working provider/model.`,
237
+ issue: (0, readiness_repair_1.providerCredentialMissingIssue)({
238
+ agentName,
239
+ lane,
240
+ provider,
241
+ model,
242
+ credentialPath,
243
+ }),
236
244
  };
237
245
  }
238
246
  function invalidPoolResult(agentName, lane, provider, model, pool) {
@@ -241,6 +249,7 @@ function invalidPoolResult(agentName, lane, provider, model, pool) {
241
249
  ok: false,
242
250
  error: `${lane} provider ${provider} model ${model} cannot read provider credentials because ${agentName}'s credential vault is locked on this machine.`,
243
251
  fix: vaultUnlockOrRecoverFix(agentName),
252
+ issue: (0, readiness_repair_1.vaultLockedIssue)(agentName),
244
253
  };
245
254
  }
246
255
  if (pool.reason === "invalid") {
@@ -268,6 +277,13 @@ function failedPingResult(agentName, lane, provider, model, result) {
268
277
  ok: false,
269
278
  error: `${lane} provider ${provider} model ${model} failed live check: ${result.message}`,
270
279
  fix: `Run 'ouro auth --agent ${agentName} --provider ${provider}' to refresh credentials, or run 'ouro use --agent ${agentName} --lane ${lane} --provider <provider> --model <model>' to switch this lane.`,
280
+ issue: (0, readiness_repair_1.providerLiveCheckFailedIssue)({
281
+ agentName,
282
+ lane,
283
+ provider,
284
+ model,
285
+ message: result.message,
286
+ }),
271
287
  };
272
288
  }
273
289
  function credentialRecordForLane(pool, provider) {
@@ -12,6 +12,7 @@ exports.runAgenticRepair = runAgenticRepair;
12
12
  const runtime_1 = require("../../nerves/runtime");
13
13
  const interactive_repair_1 = require("./interactive-repair");
14
14
  const provider_ping_1 = require("../provider-ping");
15
+ const readiness_repair_1 = require("./readiness-repair");
15
16
  function buildSystemPrompt(degraded) {
16
17
  const agentList = degraded
17
18
  .map((d) => `- ${d.agent}: error="${d.errorReason}", hint="${d.fixHint}"`)
@@ -114,11 +115,18 @@ async function runAgenticRepair(degraded, deps) {
114
115
  return { repairsAttempted: false, usedAgentic: false };
115
116
  }
116
117
  const hasLocalRepair = degraded.some(interactive_repair_1.hasRunnableInteractiveRepair);
118
+ const hasKnownTypedRepair = degraded.some((entry) => (0, readiness_repair_1.isKnownReadinessIssue)(entry.issue));
117
119
  if (hasLocalRepair) {
118
120
  const interactiveResult = await runDeterministicRepair(degraded, deps);
119
121
  if (interactiveResult.repairsAttempted) {
120
122
  return { repairsAttempted: true, usedAgentic: false };
121
123
  }
124
+ if (hasKnownTypedRepair) {
125
+ return { repairsAttempted: false, usedAgentic: false };
126
+ }
127
+ }
128
+ else if (hasKnownTypedRepair) {
129
+ return { repairsAttempted: false, usedAgentic: false };
122
130
  }
123
131
  // Try to discover a working provider for agentic diagnosis
124
132
  let discoveredProvider = null;
@@ -85,10 +85,12 @@ const doctor_1 = require("./doctor");
85
85
  const cli_render_doctor_1 = require("./cli-render-doctor");
86
86
  const interactive_repair_1 = require("./interactive-repair");
87
87
  const agentic_repair_1 = require("./agentic-repair");
88
+ const readiness_repair_1 = require("./readiness-repair");
88
89
  const startup_tui_1 = require("./startup-tui");
89
90
  const stale_bundle_prune_1 = require("./stale-bundle-prune");
90
91
  const up_progress_1 = require("./up-progress");
91
92
  const provider_ping_1 = require("../provider-ping");
93
+ const agent_discovery_1 = require("./agent-discovery");
92
94
  // ── ensureDaemonRunning ──
93
95
  const DEFAULT_DAEMON_STARTUP_TIMEOUT_MS = 10_000;
94
96
  const DEFAULT_DAEMON_STARTUP_POLL_INTERVAL_MS = 500;
@@ -96,23 +98,23 @@ const DEFAULT_DAEMON_STARTUP_STABILITY_WINDOW_MS = 1_500;
96
98
  const DEFAULT_DAEMON_STARTUP_RETRY_LIMIT = 1;
97
99
  const DEFAULT_DAEMON_STARTUP_LOG_LINES = 10;
98
100
  async function checkAgentProviders(deps, agentsOverride) {
99
- const agents = agentsOverride ?? await Promise.resolve(deps.listDiscoveredAgents ? deps.listDiscoveredAgents() : (0, cli_defaults_1.defaultListDiscoveredAgents)());
101
+ const agents = agentsOverride ?? await listCliAgents(deps);
100
102
  const bundlesRoot = deps.bundlesRoot ?? (0, identity_1.getAgentBundlesRoot)();
101
103
  const degraded = [];
102
104
  for (const agent of [...new Set(agents)]) {
103
105
  try {
104
- const result = await (0, agent_config_check_1.checkAgentConfigWithProviderHealth)(agent, bundlesRoot);
106
+ const result = await checkAgentProviderHealth(agent, bundlesRoot, deps);
105
107
  if (result.ok)
106
108
  continue;
107
109
  const errorReason = result.error ?? "agent provider health check failed";
108
110
  const fixHint = result.fix ?? "";
109
- degraded.push({ agent, errorReason, fixHint });
111
+ degraded.push({ agent, errorReason, fixHint, ...(result.issue ? { issue: result.issue } : {}) });
110
112
  (0, runtime_1.emitNervesEvent)({
111
113
  level: "error",
112
114
  component: "daemon",
113
115
  event: "daemon.agent_config_invalid",
114
116
  message: errorReason,
115
- meta: { agent, fix: fixHint, source: "already-running-provider-check" },
117
+ meta: { agent, fixProvided: fixHint.length > 0, source: "already-running-provider-check" },
116
118
  });
117
119
  }
118
120
  catch (error) {
@@ -127,12 +129,27 @@ async function checkAgentProviders(deps, agentsOverride) {
127
129
  component: "daemon",
128
130
  event: "daemon.agent_config_invalid",
129
131
  message: errorReason,
130
- meta: { agent, fix: "ouro doctor", source: "already-running-provider-check" },
132
+ meta: { agent, fixProvided: true, source: "already-running-provider-check" },
131
133
  });
132
134
  }
133
135
  }
134
136
  return degraded;
135
137
  }
138
+ async function checkAgentProviderHealth(agentName, bundlesRoot, deps) {
139
+ if (deps.homeDir) {
140
+ return (0, agent_config_check_1.checkAgentConfigWithProviderHealth)(agentName, bundlesRoot, { homeDir: deps.homeDir });
141
+ }
142
+ return (0, agent_config_check_1.checkAgentConfigWithProviderHealth)(agentName, bundlesRoot);
143
+ }
144
+ async function listCliAgents(deps) {
145
+ if (deps.listDiscoveredAgents) {
146
+ return Promise.resolve(deps.listDiscoveredAgents());
147
+ }
148
+ if (deps.bundlesRoot) {
149
+ return (0, agent_discovery_1.listEnabledBundleAgents)({ bundlesRoot: deps.bundlesRoot });
150
+ }
151
+ return [];
152
+ }
136
153
  async function checkAlreadyRunningAgentProviders(deps) {
137
154
  return checkAgentProviders(deps);
138
155
  }
@@ -149,7 +166,7 @@ async function reportPostRepairProviderHealth(deps, repairedAgents) {
149
166
  });
150
167
  if (remainingDegraded.length === 0) {
151
168
  deps.writeStdout("provider checks recovered after repair");
152
- return;
169
+ return remainingDegraded;
153
170
  }
154
171
  deps.writeStdout("provider checks still degraded after repair:");
155
172
  for (const d of remainingDegraded) {
@@ -159,10 +176,11 @@ async function reportPostRepairProviderHealth(deps, repairedAgents) {
159
176
  }
160
177
  }
161
178
  deps.writeStdout("run `ouro up` again after applying the remaining fixes.");
179
+ return remainingDegraded;
162
180
  }
163
181
  async function checkProviderHealthBeforeChat(agentName, deps) {
164
182
  const bundlesRoot = deps.bundlesRoot ?? (0, identity_1.getAgentBundlesRoot)();
165
- const result = await (0, agent_config_check_1.checkAgentConfigWithProviderHealth)(agentName, bundlesRoot);
183
+ const result = await checkAgentProviderHealth(agentName, bundlesRoot, deps);
166
184
  if (!result.ok) {
167
185
  const output = `${result.error}\n${result.fix ? ` fix: ${result.fix}` : ""}`;
168
186
  deps.writeStdout(output);
@@ -186,7 +204,7 @@ function mergeStartupStability(stability, extraDegraded) {
186
204
  }
187
205
  return { stable, degraded };
188
206
  }
189
- async function ensureDaemonRunning(deps) {
207
+ async function ensureDaemonRunning(deps, options = {}) {
190
208
  const readLatestDaemonStartupEvent = () => {
191
209
  try {
192
210
  // The daemon writes structured events to daemon.ndjson in the first
@@ -230,7 +248,7 @@ async function ensureDaemonRunning(deps) {
230
248
  }
231
249
  return null;
232
250
  };
233
- const alive = await deps.checkSocketAlive(deps.socketPath);
251
+ const alive = options.initialAlive ?? await deps.checkSocketAlive(deps.socketPath);
234
252
  if (alive) {
235
253
  const localRuntime = (0, runtime_metadata_1.getRuntimeMetadata)();
236
254
  let runningRuntimePromise = null;
@@ -686,13 +704,6 @@ function readVaultRecoverSource(sourcePath) {
686
704
  runtimeConfig: recoverRuntimeConfig(parsed),
687
705
  };
688
706
  }
689
- function defaultStableVaultEmail(agentName) {
690
- const local = agentName
691
- .toLowerCase()
692
- .replace(/[^a-z0-9._-]+/g, "-")
693
- .replace(/^-+|-+$/g, "") || "agent";
694
- return `${local}@ouro.bot`;
695
- }
696
707
  function isGeneratedRepairVaultEmail(email) {
697
708
  const [local, domain] = email.trim().split("@");
698
709
  return domain?.toLowerCase() === "ouro.bot" && /\+(?:replaced|recovered)-\d{14}(?:$|\+)/i.test(local);
@@ -701,7 +712,7 @@ function defaultRepairVaultEmail(agentName, config) {
701
712
  const configuredEmail = config.vault?.email?.trim();
702
713
  if (configuredEmail && !isGeneratedRepairVaultEmail(configuredEmail))
703
714
  return configuredEmail;
704
- return defaultStableVaultEmail(agentName);
715
+ return (0, identity_1.defaultStableVaultEmail)(agentName);
705
716
  }
706
717
  function ensureVaultSecretPrompt(promptSecret, action) {
707
718
  if (promptSecret)
@@ -765,13 +776,9 @@ async function executeVaultCreate(command, deps) {
765
776
  }
766
777
  if (command.generateUnlockSecret)
767
778
  rejectGeneratedVaultUnlockSecret("create");
768
- const prompt = deps.promptInput;
769
779
  const { configPath, config } = (0, auth_flow_1.readAgentConfigForAgent)(command.agent, deps.bundlesRoot);
770
780
  const configuredVault = (0, identity_1.resolveVaultConfig)(command.agent, config.vault);
771
- const email = command.email ?? config.vault?.email ?? (prompt ? (await prompt("Ouro credential vault email: ")).trim() : "");
772
- if (!email) {
773
- throw new Error("vault create requires --email <email> when the agent bundle has no vault.email");
774
- }
781
+ const email = command.email ?? config.vault?.email ?? configuredVault.email;
775
782
  const promptSecret = ensureVaultSecretPrompt(deps.promptSecret, "create");
776
783
  const serverUrl = command.serverUrl ?? config.vault?.serverUrl ?? configuredVault.serverUrl;
777
784
  const unlockSecret = (await promptSecret(`Choose Ouro vault unlock secret for ${email}: `)).trim();
@@ -1356,6 +1363,108 @@ async function executeProviderRefresh(command, deps) {
1356
1363
  deps.writeStdout(message);
1357
1364
  return message;
1358
1365
  }
1366
+ async function readinessReportForAgent(agent, deps) {
1367
+ const bundlesRoot = deps.bundlesRoot ?? (0, identity_1.getAgentBundlesRoot)();
1368
+ try {
1369
+ const result = await checkAgentProviderHealth(agent, bundlesRoot, deps);
1370
+ if (result.ok) {
1371
+ return { agent, ok: true, issues: [] };
1372
+ }
1373
+ else {
1374
+ const issue = result.issue ?? (0, readiness_repair_1.genericReadinessIssue)({
1375
+ summary: result.error ?? `${agent} is not ready.`,
1376
+ ...(result.fix ? { fix: result.fix } : {}),
1377
+ });
1378
+ return { agent, ok: false, issues: [issue] };
1379
+ }
1380
+ }
1381
+ catch (error) {
1382
+ return {
1383
+ agent,
1384
+ ok: false,
1385
+ issues: [(0, readiness_repair_1.genericReadinessIssue)({
1386
+ summary: `${agent} readiness check failed.`,
1387
+ detail: error instanceof Error ? error.message : String(error),
1388
+ fix: "Run 'ouro doctor' for diagnostics, then retry 'ouro repair'.",
1389
+ })],
1390
+ };
1391
+ }
1392
+ }
1393
+ async function executeReadinessRepairAction(agent, action, deps) {
1394
+ if (action.kind === "vault-unlock") {
1395
+ await executeVaultUnlock({ kind: "vault.unlock", agent }, deps);
1396
+ return;
1397
+ }
1398
+ if (action.kind === "vault-replace") {
1399
+ await executeVaultReplace({ kind: "vault.replace", agent }, deps);
1400
+ return;
1401
+ }
1402
+ if (action.kind === "provider-auth") {
1403
+ const provider = action.provider;
1404
+ const authRunner = deps.runAuthFlow ?? auth_flow_1.runRuntimeAuthFlow;
1405
+ const result = await authRunner({
1406
+ agentName: agent,
1407
+ provider,
1408
+ promptInput: deps.promptInput,
1409
+ });
1410
+ deps.writeStdout(result.message);
1411
+ await executeProviderRefresh({ kind: "provider.refresh", agent }, deps);
1412
+ return;
1413
+ }
1414
+ deps.writeStdout(`manual step for ${agent}: ${action.command}`);
1415
+ }
1416
+ async function executeRepair(command, deps) {
1417
+ const agents = command.agent
1418
+ ? [command.agent]
1419
+ : await listCliAgents(deps);
1420
+ const uniqueAgents = [...new Set(agents)];
1421
+ const lines = [];
1422
+ const repairDeps = {
1423
+ ...deps,
1424
+ writeStdout: (text) => {
1425
+ lines.push(text);
1426
+ deps.writeStdout(text);
1427
+ },
1428
+ };
1429
+ if (uniqueAgents.length === 0) {
1430
+ const message = "no agents found to repair.";
1431
+ repairDeps.writeStdout(message);
1432
+ return message;
1433
+ }
1434
+ else {
1435
+ const reports = await Promise.all(uniqueAgents.map((agent) => readinessReportForAgent(agent, repairDeps)));
1436
+ for (const report of reports) {
1437
+ if (report.ok) {
1438
+ repairDeps.writeStdout(`${report.agent}: ready`);
1439
+ }
1440
+ }
1441
+ await (0, readiness_repair_1.runGuidedReadinessRepair)(reports, {
1442
+ promptInput: deps.promptInput,
1443
+ writeStdout: repairDeps.writeStdout,
1444
+ runRepairAction: async (agentName, action) => executeReadinessRepairAction(agentName, action, repairDeps),
1445
+ });
1446
+ return lines.join("\n");
1447
+ }
1448
+ }
1449
+ function readinessReportsFromDegraded(degraded) {
1450
+ return degraded.map((entry) => ({
1451
+ agent: entry.agent,
1452
+ ok: false,
1453
+ issues: [
1454
+ entry.issue ?? (0, readiness_repair_1.genericReadinessIssue)({
1455
+ summary: entry.errorReason,
1456
+ ...(entry.fixHint ? { fix: entry.fixHint } : {}),
1457
+ }),
1458
+ ],
1459
+ }));
1460
+ }
1461
+ async function runReadinessRepairForDegraded(degraded, deps) {
1462
+ return (0, readiness_repair_1.runGuidedReadinessRepair)(readinessReportsFromDegraded(degraded), {
1463
+ promptInput: deps.promptInput,
1464
+ writeStdout: deps.writeStdout,
1465
+ runRepairAction: async (agentName, action) => executeReadinessRepairAction(agentName, action, deps),
1466
+ });
1467
+ }
1359
1468
  async function executeLegacyAuthSwitch(command, deps) {
1360
1469
  const { state } = readOrBootstrapProviderState(command.agent, deps);
1361
1470
  const lanes = command.facing
@@ -2119,6 +2228,41 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
2119
2228
  bundlesRoot: deps.bundlesRoot ?? bundlesRoot,
2120
2229
  promptInput: deps.promptInput,
2121
2230
  });
2231
+ const daemonAliveBeforeStart = await deps.checkSocketAlive(deps.socketPath);
2232
+ let providerChecksAlreadyRun = false;
2233
+ if (!daemonAliveBeforeStart) {
2234
+ progress.startPhase("provider checks");
2235
+ const preflightProviderDegraded = await checkAgentProviders(deps);
2236
+ providerChecksAlreadyRun = true;
2237
+ progress.completePhase("provider checks", preflightProviderDegraded.length > 0 ? `${preflightProviderDegraded.length} degraded` : "ok");
2238
+ if (preflightProviderDegraded.length > 0) {
2239
+ progress.end();
2240
+ if (command.noRepair) {
2241
+ deps.writeStdout("degraded agents:");
2242
+ for (const d of preflightProviderDegraded) {
2243
+ deps.writeStdout(` ${d.agent}: ${d.errorReason}`);
2244
+ if (d.fixHint) {
2245
+ deps.writeStdout(` fix: ${d.fixHint}`);
2246
+ }
2247
+ }
2248
+ const message = "daemon not started because provider checks are degraded; run `ouro repair` after applying the fixes.";
2249
+ deps.writeStdout(message);
2250
+ return message;
2251
+ }
2252
+ const repairResult = await runReadinessRepairForDegraded(preflightProviderDegraded, deps);
2253
+ if (!repairResult.repairsAttempted) {
2254
+ const message = "daemon not started because provider checks are degraded; run `ouro repair` after applying the fixes.";
2255
+ deps.writeStdout(message);
2256
+ return message;
2257
+ }
2258
+ const remainingDegraded = await reportPostRepairProviderHealth(deps, preflightProviderDegraded.map((entry) => entry.agent));
2259
+ if (remainingDegraded.length > 0) {
2260
+ const message = "daemon not started because provider checks are still degraded.";
2261
+ deps.writeStdout(message);
2262
+ return message;
2263
+ }
2264
+ }
2265
+ }
2122
2266
  progress.startPhase("starting daemon");
2123
2267
  const daemonResult = await ensureDaemonRunning({
2124
2268
  ...deps,
@@ -2126,8 +2270,8 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
2126
2270
  ;
2127
2271
  progress.announceStep?.(label);
2128
2272
  },
2129
- });
2130
- if (daemonResult.alreadyRunning) {
2273
+ }, { initialAlive: daemonAliveBeforeStart });
2274
+ if (!providerChecksAlreadyRun || daemonResult.alreadyRunning) {
2131
2275
  progress.startPhase("provider checks");
2132
2276
  const providerDegraded = await checkAlreadyRunningAgentProviders(deps);
2133
2277
  daemonResult.stability = mergeStartupStability(daemonResult.stability, providerDegraded);
@@ -2794,6 +2938,9 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
2794
2938
  if (command.kind === "provider.refresh") {
2795
2939
  return executeProviderRefresh(command, deps);
2796
2940
  }
2941
+ if (command.kind === "repair") {
2942
+ return executeRepair(command, deps);
2943
+ }
2797
2944
  if (command.kind === "vault.unlock") {
2798
2945
  return executeVaultUnlock(command, deps);
2799
2946
  }
@@ -175,6 +175,12 @@ exports.COMMAND_REGISTRY = {
175
175
  usage: "ouro check --agent <name> --lane outward|inner",
176
176
  example: "ouro check --agent ouroboros --lane outward",
177
177
  },
178
+ repair: {
179
+ category: "Auth",
180
+ description: "Guide vault and provider readiness repair without invoking AI diagnosis for known issues",
181
+ usage: "ouro repair [--agent <name>]",
182
+ example: "ouro repair --agent ouroboros",
183
+ },
178
184
  provider: {
179
185
  category: "Auth",
180
186
  description: "Refresh daemon provider credentials from an agent vault",
@@ -76,6 +76,7 @@ function usage() {
76
76
  " ouro status --agent <name>",
77
77
  " ouro use --agent <name> --lane outward|inner --provider <provider> --model <model> [--force]",
78
78
  " ouro check --agent <name> --lane outward|inner",
79
+ " ouro repair [--agent <name>]",
79
80
  " ouro provider refresh --agent <name>",
80
81
  " ouro outlook [--json]",
81
82
  " ouro -v|--version",
@@ -1033,6 +1034,12 @@ function parseOuroCommand(args) {
1033
1034
  return parseProviderUseCommand(args.slice(1));
1034
1035
  if (head === "check")
1035
1036
  return parseProviderCheckCommand(args.slice(1));
1037
+ if (head === "repair") {
1038
+ const { agent, rest } = extractAgentFlag(args.slice(1));
1039
+ if (rest.length > 0)
1040
+ throw new Error("Usage: ouro repair [--agent <name>]");
1041
+ return agent ? { kind: "repair", agent } : { kind: "repair" };
1042
+ }
1036
1043
  if (head === "provider")
1037
1044
  return parseProviderCommand(args.slice(1));
1038
1045
  if (head === "logs") {
@@ -24,6 +24,9 @@ function isConfigError(degraded) {
24
24
  return degraded.fixHint.length > 0 && !isVaultUnlockIssue(degraded) && !isCredentialIssue(degraded);
25
25
  }
26
26
  function hasRunnableInteractiveRepair(degraded) {
27
+ if (degraded.issue?.actions.some((action) => typedActionToRunnable(degraded, action) !== undefined)) {
28
+ return true;
29
+ }
27
30
  return isVaultUnlockIssue(degraded) || isCredentialIssue(degraded);
28
31
  }
29
32
  function isAgentProvider(value) {
@@ -68,6 +71,11 @@ function writeDeclinedRepair(degraded, command, deps) {
68
71
  }
69
72
  }
70
73
  function runnableRepairActionFor(degraded) {
74
+ const typedAction = degraded.issue?.actions
75
+ .map((action) => typedActionToRunnable(degraded, action))
76
+ .find((action) => action !== undefined);
77
+ if (typedAction)
78
+ return typedAction;
71
79
  if (isVaultUnlockIssue(degraded)) {
72
80
  return { kind: "vault-unlock", label: "vault unlock", command: vaultUnlockCommandFor(degraded) };
73
81
  }
@@ -81,6 +89,22 @@ function runnableRepairActionFor(degraded) {
81
89
  }
82
90
  return undefined;
83
91
  }
92
+ function typedActionToRunnable(degraded, action) {
93
+ if (action.executable === false || action.command.includes("<"))
94
+ return undefined;
95
+ if (action.kind === "vault-unlock") {
96
+ return { kind: "vault-unlock", label: "vault unlock", command: action.command };
97
+ }
98
+ if (action.kind === "provider-auth") {
99
+ return {
100
+ kind: "provider-auth",
101
+ label: "provider auth",
102
+ command: action.command || `ouro auth --agent ${degraded.agent}`,
103
+ provider: action.provider,
104
+ };
105
+ }
106
+ return undefined;
107
+ }
84
108
  function writeRepairQueueSummary(degraded, deps) {
85
109
  const repairable = degraded
86
110
  .map((entry) => ({ entry, action: runnableRepairActionFor(entry) }))
@@ -0,0 +1,205 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.vaultLockedIssue = vaultLockedIssue;
4
+ exports.providerCredentialMissingIssue = providerCredentialMissingIssue;
5
+ exports.providerLiveCheckFailedIssue = providerLiveCheckFailedIssue;
6
+ exports.genericReadinessIssue = genericReadinessIssue;
7
+ exports.isKnownReadinessIssue = isKnownReadinessIssue;
8
+ exports.renderReadinessIssue = renderReadinessIssue;
9
+ exports.runGuidedReadinessRepair = runGuidedReadinessRepair;
10
+ const runtime_1 = require("../../nerves/runtime");
11
+ function vaultLockedIssue(agentName) {
12
+ return {
13
+ kind: "vault-locked",
14
+ severity: "blocked",
15
+ actor: "human-required",
16
+ summary: `${agentName} needs its vault unlocked on this machine.`,
17
+ detail: "Choose the path that matches what the human actually has. Ouro will not print or store a portable copy of the unlock secret.",
18
+ actions: [
19
+ {
20
+ kind: "vault-unlock",
21
+ label: "I have the saved vault unlock secret",
22
+ command: `ouro vault unlock --agent ${agentName}`,
23
+ actor: "human-required",
24
+ },
25
+ {
26
+ kind: "vault-replace",
27
+ label: "Nobody saved it; create an empty vault and re-enter credentials",
28
+ command: `ouro vault replace --agent ${agentName}`,
29
+ actor: "human-required",
30
+ },
31
+ {
32
+ kind: "vault-recover",
33
+ label: "I have an old JSON credential export",
34
+ command: `ouro vault recover --agent ${agentName} --from <json>`,
35
+ actor: "human-required",
36
+ executable: false,
37
+ },
38
+ ],
39
+ };
40
+ }
41
+ function providerCredentialMissingIssue(input) {
42
+ return {
43
+ kind: "provider-credentials-missing",
44
+ severity: "blocked",
45
+ actor: "human-required",
46
+ summary: `${input.agentName} is missing ${input.provider} credentials for the ${input.lane} lane.`,
47
+ detail: `Selected model: ${input.model}. Credential source: ${input.credentialPath}.`,
48
+ actions: [
49
+ {
50
+ kind: "provider-auth",
51
+ label: `Authenticate ${input.provider} for ${input.agentName}`,
52
+ command: `ouro auth --agent ${input.agentName} --provider ${input.provider}`,
53
+ actor: "human-required",
54
+ provider: input.provider,
55
+ },
56
+ {
57
+ kind: "provider-use",
58
+ label: "Choose a different working provider/model for this lane",
59
+ command: `ouro use --agent ${input.agentName} --lane ${input.lane} --provider <provider> --model <model>`,
60
+ actor: "human-choice",
61
+ executable: false,
62
+ lane: input.lane,
63
+ },
64
+ ],
65
+ };
66
+ }
67
+ function providerLiveCheckFailedIssue(input) {
68
+ return {
69
+ kind: "provider-live-check-failed",
70
+ severity: "blocked",
71
+ actor: "human-choice",
72
+ summary: `${input.agentName}'s ${input.lane} lane provider ${input.provider} / ${input.model} failed its live check.`,
73
+ detail: input.message,
74
+ actions: [
75
+ {
76
+ kind: "provider-auth",
77
+ label: `Refresh ${input.provider} credentials`,
78
+ command: `ouro auth --agent ${input.agentName} --provider ${input.provider}`,
79
+ actor: "human-required",
80
+ provider: input.provider,
81
+ },
82
+ {
83
+ kind: "provider-use",
84
+ label: "Choose a different working provider/model for this lane",
85
+ command: `ouro use --agent ${input.agentName} --lane ${input.lane} --provider <provider> --model <model>`,
86
+ actor: "human-choice",
87
+ executable: false,
88
+ lane: input.lane,
89
+ },
90
+ ],
91
+ };
92
+ }
93
+ function genericReadinessIssue(input) {
94
+ return {
95
+ kind: "generic",
96
+ severity: "degraded",
97
+ actor: "human-choice",
98
+ summary: input.summary,
99
+ ...(input.detail ? { detail: input.detail } : {}),
100
+ actions: input.fix
101
+ ? [{
102
+ kind: "provider-use",
103
+ label: "Follow the printed fix",
104
+ command: input.fix,
105
+ actor: "human-choice",
106
+ executable: false,
107
+ }]
108
+ : [],
109
+ };
110
+ }
111
+ function isKnownReadinessIssue(issue) {
112
+ return !!issue && issue.kind !== "generic";
113
+ }
114
+ function renderReadinessIssue(issue) {
115
+ const lines = [issue.summary];
116
+ if (issue.detail) {
117
+ lines.push(` ${issue.detail}`);
118
+ }
119
+ issue.actions.forEach((action, index) => {
120
+ lines.push(`${index + 1}. ${action.label}`);
121
+ lines.push(` runs: ${action.command}`);
122
+ });
123
+ lines.push(`${issue.actions.length + 1}. Skip for now`);
124
+ return lines.join("\n");
125
+ }
126
+ function selectedActionFor(answer, issue) {
127
+ const selected = Number.parseInt(answer.trim(), 10);
128
+ if (!Number.isFinite(selected))
129
+ return null;
130
+ if (selected === issue.actions.length + 1)
131
+ return "skip";
132
+ if (selected < 1 || selected > issue.actions.length)
133
+ return null;
134
+ /* v8 ignore next -- defensive: bounds were checked above, but keep sparse arrays harmless @preserve */
135
+ return issue.actions[selected - 1] ?? null;
136
+ }
137
+ function isExecutableAction(action) {
138
+ if (action.executable === false)
139
+ return false;
140
+ return !action.command.includes("<");
141
+ }
142
+ async function runGuidedReadinessRepair(reports, deps) {
143
+ (0, runtime_1.emitNervesEvent)({
144
+ level: "info",
145
+ component: "daemon",
146
+ event: "daemon.readiness_repair_start",
147
+ message: "guided readiness repair started",
148
+ meta: { reportCount: reports.length },
149
+ });
150
+ let repairsAttempted = false;
151
+ for (const report of reports) {
152
+ if (report.ok || report.issues.length === 0)
153
+ continue;
154
+ for (const issue of report.issues) {
155
+ deps.writeStdout(renderReadinessIssue(issue));
156
+ if (!deps.promptInput) {
157
+ deps.writeStdout(`manual repair required for ${report.agent}; run one of the commands above.`);
158
+ continue;
159
+ }
160
+ const answer = await deps.promptInput(`Choose [1-${issue.actions.length + 1}]: `);
161
+ const action = selectedActionFor(answer, issue);
162
+ if (action === "skip") {
163
+ deps.writeStdout(`repair skipped for ${report.agent}.`);
164
+ continue;
165
+ }
166
+ if (!action) {
167
+ deps.writeStdout(`invalid repair choice for ${report.agent}; no repair attempted.`);
168
+ continue;
169
+ }
170
+ if (!isExecutableAction(action)) {
171
+ deps.writeStdout(`manual step for ${report.agent}: ${action.command}`);
172
+ continue;
173
+ }
174
+ if (!deps.runRepairAction) {
175
+ deps.writeStdout(`repair runner unavailable for ${report.agent}; run \`${action.command}\` manually.`);
176
+ continue;
177
+ }
178
+ try {
179
+ await deps.runRepairAction(report.agent, action, issue);
180
+ repairsAttempted = true;
181
+ deps.writeStdout(`repair attempted for ${report.agent}`);
182
+ }
183
+ catch (error) {
184
+ const message = error instanceof Error ? error.message : String(error);
185
+ repairsAttempted = true;
186
+ deps.writeStdout(`repair error for ${report.agent}: ${message}`);
187
+ (0, runtime_1.emitNervesEvent)({
188
+ level: "error",
189
+ component: "daemon",
190
+ event: "daemon.readiness_repair_error",
191
+ message: "guided readiness repair action failed",
192
+ meta: { agent: report.agent, issue: issue.kind, action: action.kind, error: message },
193
+ });
194
+ }
195
+ }
196
+ }
197
+ (0, runtime_1.emitNervesEvent)({
198
+ level: "info",
199
+ component: "daemon",
200
+ event: "daemon.readiness_repair_end",
201
+ message: "guided readiness repair completed",
202
+ meta: { repairsAttempted },
203
+ });
204
+ return { repairsAttempted };
205
+ }
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.HARNESS_CANONICAL_REPO_URL = exports.DEFAULT_AGENT_SENSES = exports.DEFAULT_VAULT_SERVER_URL = exports.DEFAULT_AGENT_PHRASES = exports.DEFAULT_AGENT_CONTEXT = exports.PROVIDER_CREDENTIALS = void 0;
37
+ exports.defaultStableVaultEmail = defaultStableVaultEmail;
37
38
  exports.resolveVaultConfig = resolveVaultConfig;
38
39
  exports.normalizeSenses = normalizeSenses;
39
40
  exports.buildDefaultAgentTemplate = buildDefaultAgentTemplate;
@@ -76,13 +77,20 @@ exports.DEFAULT_AGENT_PHRASES = {
76
77
  followup: ["processing"],
77
78
  };
78
79
  exports.DEFAULT_VAULT_SERVER_URL = "https://vault.ouroboros.bot";
80
+ function defaultStableVaultEmail(agentName) {
81
+ const local = agentName
82
+ .toLowerCase()
83
+ .replace(/[^a-z0-9._-]+/g, "-")
84
+ .replace(/^-+|-+$/g, "") || "agent";
85
+ return `${local}@ouro.bot`;
86
+ }
79
87
  /**
80
88
  * Resolve the vault config for an agent, applying defaults.
81
89
  * If vault is not configured in agent.json, returns default values.
82
90
  */
83
91
  function resolveVaultConfig(agentName, config) {
84
92
  return {
85
- email: config?.email ?? `${agentName}@ouro.bot`,
93
+ email: config?.email ?? defaultStableVaultEmail(agentName),
86
94
  serverUrl: config?.serverUrl ?? exports.DEFAULT_VAULT_SERVER_URL,
87
95
  };
88
96
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.395",
3
+ "version": "0.1.0-alpha.396",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",