@ouro.bot/cli 0.1.0-alpha.407 → 0.1.0-alpha.409

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,26 @@
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.409",
6
+ "changes": [
7
+ "`ouro up` interactive repair now presents each agent's next step as a compact panel with `needs`, `run`, and `note` lines, followed by a short human prompt like `Unlock it now?` or `Open the auth flow now?` instead of embedding commands and warnings into one long question.",
8
+ "Declined repair output now collapses into short `next` / `or` follow-up commands, so vault unlock, replace, recover, and provider-auth paths stay visible without reprinting a wall of explanatory prose.",
9
+ "Guided readiness repair and no-repair summaries now use blank-line grouping plus calmer `Provider checks need attention` and `Still needs attention` headings, and stale-daemon restart output now splits its repair handoff onto a second line instead of bolting it onto one overlong sentence.",
10
+ "Added regression coverage for the new interactive repair copy, grouped repair queue, readiness summary spacing, no-repair output, and the revised stale-daemon restart message.",
11
+ "`@ouro.bot/cli` and the `ouro.bot` wrapper are version-synced for the repair UX polish release."
12
+ ]
13
+ },
14
+ {
15
+ "version": "0.1.0-alpha.408",
16
+ "changes": [
17
+ "Existing agents without a `vault` block in `agent.json` now fail fast with explicit `ouro vault create --agent <agent>` guidance instead of silently deriving a stable vault account and misreporting missing provider credentials.",
18
+ "`ouro auth`, `ouro up`, and `ouro repair` now treat a missing agent vault locator as a first-class readiness state with create/recover choices, then continue through the normal provider-auth repair path after the vault exists.",
19
+ "Runtime credential access now requires an explicit agent vault locator before opening Bitwarden or Vaultwarden, which keeps provider, runtime, travel, and tool credential flows truthful for pre-vault agent migrations.",
20
+ "Added regression coverage for missing-vault-locator auth, provider readiness, guided repair, and the real Bitwarden-backed auth path, plus the default local `spawnSync` fallback used by Linux secure-store probing.",
21
+ "`@ouro.bot/cli` and the `ouro.bot` wrapper are version-synced for the pre-vault agent locator repair release."
22
+ ]
23
+ },
4
24
  {
5
25
  "version": "0.1.0-alpha.407",
6
26
  "changes": [
@@ -389,7 +389,10 @@ async function runRuntimeAuthFlow(input, deps = {}) {
389
389
  writeAuthProgress(input, `checking ${input.agentName}'s vault access...`);
390
390
  const vault = await (0, provider_credentials_1.refreshProviderCredentialPool)(input.agentName);
391
391
  if (!vault.ok && vault.reason === "unavailable") {
392
- throw new Error(`${vault.error}\n${(0, vault_unlock_1.vaultUnlockReplaceRecoverFix)(input.agentName, `Then retry 'ouro auth --agent ${input.agentName} --provider ${input.provider}'.`)}`);
392
+ const fix = (0, vault_unlock_1.isCredentialVaultNotConfiguredError)(vault.error)
393
+ ? (0, vault_unlock_1.vaultCreateRecoverFix)(input.agentName, `Then retry 'ouro auth --agent ${input.agentName} --provider ${input.provider}'.`)
394
+ : (0, vault_unlock_1.vaultUnlockReplaceRecoverFix)(input.agentName, `Then retry 'ouro auth --agent ${input.agentName} --provider ${input.provider}'.`);
395
+ throw new Error(`${vault.error}\n${fix}`);
393
396
  }
394
397
  const credentials = await collectRuntimeAuthCredentials(input, deps);
395
398
  let credentialPath;
@@ -252,6 +252,14 @@ function invalidPoolResult(agentName, lane, provider, model, pool) {
252
252
  issue: (0, readiness_repair_1.vaultLockedIssue)(agentName),
253
253
  };
254
254
  }
255
+ if (pool.reason === "unavailable" && (0, vault_unlock_1.isCredentialVaultNotConfiguredError)(pool.error)) {
256
+ return {
257
+ ok: false,
258
+ error: `${lane} provider ${provider} model ${model} cannot read provider credentials because ${agentName}'s credential vault is not configured in agent.json.`,
259
+ fix: (0, vault_unlock_1.vaultCreateRecoverFix)(agentName, `Then run 'ouro auth --agent ${agentName} --provider ${provider}' and rerun 'ouro up'.`),
260
+ issue: (0, readiness_repair_1.vaultUnconfiguredIssue)(agentName),
261
+ };
262
+ }
255
263
  if (pool.reason === "invalid") {
256
264
  return {
257
265
  ok: false,
@@ -166,7 +166,7 @@ function writeProviderRepairSummary(deps, title, degraded) {
166
166
  function providerRepairCountSummary(count) {
167
167
  if (count === 0)
168
168
  return "ok";
169
- return `${count} ${count === 1 ? "needs" : "need"} repair`;
169
+ return `${count} ${count === 1 ? "needs" : "need"} attention`;
170
170
  }
171
171
  async function reportPostRepairProviderHealth(deps, repairedAgents) {
172
172
  const remainingDegraded = await checkAgentProviders(deps, repairedAgents);
@@ -180,10 +180,10 @@ async function reportPostRepairProviderHealth(deps, repairedAgents) {
180
180
  meta: { degradedCount: remainingDegraded.length, repairedAgents },
181
181
  });
182
182
  if (remainingDegraded.length === 0) {
183
- deps.writeStdout("provider checks recovered after repair");
183
+ deps.writeStdout("All set. Provider checks recovered after repair.");
184
184
  return remainingDegraded;
185
185
  }
186
- writeProviderRepairSummary(deps, "Still blocked:", remainingDegraded);
186
+ writeProviderRepairSummary(deps, "Still needs attention", remainingDegraded);
187
187
  deps.writeStdout("Run `ouro up` again after these are fixed.");
188
188
  return remainingDegraded;
189
189
  }
@@ -1396,6 +1396,10 @@ async function readinessReportForAgent(agent, deps) {
1396
1396
  }
1397
1397
  }
1398
1398
  async function executeReadinessRepairAction(agent, action, deps) {
1399
+ if (action.kind === "vault-create") {
1400
+ await executeVaultCreate({ kind: "vault.create", agent }, deps);
1401
+ return;
1402
+ }
1399
1403
  if (action.kind === "vault-unlock") {
1400
1404
  await executeVaultUnlock({ kind: "vault.unlock", agent }, deps);
1401
1405
  return;
@@ -2291,26 +2295,26 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
2291
2295
  if (preflightProviderDegraded.length > 0) {
2292
2296
  progress.end();
2293
2297
  if (command.noRepair) {
2294
- writeProviderRepairSummary(deps, "Provider checks need repair:", preflightProviderDegraded);
2298
+ writeProviderRepairSummary(deps, "Provider checks need attention", preflightProviderDegraded);
2295
2299
  const message = "daemon not started: provider checks need repair. Run `ouro repair` or rerun `ouro up` to choose a repair path.";
2296
2300
  deps.writeStdout(message);
2297
2301
  return message;
2298
2302
  }
2299
2303
  const repairResult = await runReadinessRepairForDegraded(preflightProviderDegraded, deps);
2300
2304
  if (!repairResult.repairsAttempted) {
2301
- writeProviderRepairSummary(deps, "Provider checks still need repair:", repairResult.remainingDegraded);
2305
+ writeProviderRepairSummary(deps, "Provider checks still need attention", repairResult.remainingDegraded);
2302
2306
  const message = "daemon not started: provider checks need repair. Run `ouro repair` or rerun `ouro up` to choose a repair path.";
2303
2307
  deps.writeStdout(message);
2304
2308
  return message;
2305
2309
  }
2306
2310
  const remainingDegraded = repairResult.remainingDegraded;
2307
2311
  if (remainingDegraded.length > 0) {
2308
- writeProviderRepairSummary(deps, "Still blocked:", remainingDegraded);
2312
+ writeProviderRepairSummary(deps, "Still needs attention", remainingDegraded);
2309
2313
  const message = "daemon not started: provider checks still need repair.";
2310
2314
  deps.writeStdout(message);
2311
2315
  return message;
2312
2316
  }
2313
- deps.writeStdout("provider checks recovered after repair");
2317
+ deps.writeStdout("All set. Provider checks recovered after repair.");
2314
2318
  }
2315
2319
  }
2316
2320
  progress.startPhase("starting daemon");
@@ -2333,7 +2337,7 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
2333
2337
  if (daemonResult.stability?.degraded && daemonResult.stability.degraded.length > 0) {
2334
2338
  if (command.noRepair) {
2335
2339
  // --no-repair: write degraded summary and skip interactive repair
2336
- writeProviderRepairSummary(deps, "Provider checks need repair:", daemonResult.stability.degraded);
2340
+ writeProviderRepairSummary(deps, "Provider checks need attention", daemonResult.stability.degraded);
2337
2341
  (0, runtime_1.emitNervesEvent)({
2338
2342
  level: "warn",
2339
2343
  component: "daemon",
@@ -134,11 +134,11 @@ async function ensureCurrentDaemonRuntime(deps) {
134
134
  const pid = started.pid ?? "unknown";
135
135
  const verified = await verifyDaemonStarted(deps);
136
136
  /* v8 ignore next -- daemon liveness failure: requires real daemon crash timing @preserve */
137
- const suffix = verified ? "" : " but daemon failed to respond, check logs";
137
+ const suffix = verified ? "" : "\ndaemon did not answer yet, so Ouro is checking repair paths next.";
138
138
  result = {
139
139
  alreadyRunning: false,
140
140
  message: includesVersionDrift
141
- ? `restarted stale daemon from ${runningVersion} to ${deps.localVersion} (pid ${pid})${suffix}`
141
+ ? `restarted stale daemon ${runningVersion} -> ${deps.localVersion} (pid ${pid})${suffix}`
142
142
  : `restarted drifted daemon (${driftSummary}) (pid ${pid})${suffix}`,
143
143
  verifyStartupStatus: verified,
144
144
  startedPid: started.pid ?? null,
@@ -64,11 +64,83 @@ function vaultUnlockCommandFor(degraded) {
64
64
  function isAffirmativeAnswer(answer) {
65
65
  return /^(y|yes)$/i.test(answer.trim());
66
66
  }
67
- function writeDeclinedRepair(degraded, command, deps) {
68
- deps.writeStdout(`repair skipped for ${degraded.agent}; run \`${command}\` later.`);
69
- if (degraded.fixHint.includes("ouro vault replace") || degraded.fixHint.includes("ouro vault recover")) {
70
- deps.writeStdout(`repair options for ${degraded.agent}: ${degraded.fixHint}`);
67
+ function uniqueCommands(commands) {
68
+ const seen = new Set();
69
+ const unique = [];
70
+ commands.forEach((command) => {
71
+ if (!command)
72
+ return;
73
+ if (seen.has(command))
74
+ return;
75
+ seen.add(command);
76
+ unique.push(command);
77
+ });
78
+ return unique;
79
+ }
80
+ function fallbackCommandsFor(degraded, primaryCommand) {
81
+ const issueCommands = degraded.issue?.actions.map((action) => action.command) ?? [];
82
+ return uniqueCommands([
83
+ primaryCommand,
84
+ ...issueCommands,
85
+ extractRepairCommand(degraded.fixHint, "ouro vault replace"),
86
+ extractRepairCommand(degraded.fixHint, "ouro vault recover"),
87
+ extractRepairCommand(degraded.fixHint, "ouro use"),
88
+ ]);
89
+ }
90
+ function renderRepairChoices(prefix, commands) {
91
+ return commands.map((command, index) => ` ${index === 0 ? prefix : "or"}: ${command}`);
92
+ }
93
+ function renderRepairQueueSummaryLines(degraded) {
94
+ const repairable = degraded
95
+ .map((entry) => ({ entry, action: runnableRepairActionFor(entry) }))
96
+ .filter((item) => item.action !== undefined);
97
+ if (repairable.length < 2)
98
+ return [];
99
+ const lines = [
100
+ "Repair queue",
101
+ `${repairable.length} agents need attention before startup can finish.`,
102
+ "",
103
+ ];
104
+ repairable.forEach(({ entry, action }, index) => {
105
+ lines.push(`${entry.agent} - ${action.label}`);
106
+ lines.push(` ${action.command}`);
107
+ if (index < repairable.length - 1)
108
+ lines.push("");
109
+ });
110
+ return lines;
111
+ }
112
+ function renderActionPromptLines(agent, action) {
113
+ const lines = [
114
+ `${agent}`,
115
+ ` needs: ${action.label}`,
116
+ ` run: ${action.command}`,
117
+ ];
118
+ if (action.kind === "vault-unlock") {
119
+ lines.push(" note: use the saved vault unlock secret");
71
120
  }
121
+ return lines;
122
+ }
123
+ function renderDeferredRepair(agent, commands) {
124
+ return [
125
+ `Leaving ${agent} for later.`,
126
+ ...renderRepairChoices("next", commands),
127
+ ].join("\n");
128
+ }
129
+ function renderManualRepairHint(agent, fixHint) {
130
+ return [
131
+ `${agent}`,
132
+ " needs manual attention",
133
+ ` next: ${fixHint}`,
134
+ ].join("\n");
135
+ }
136
+ function renderUnknownRepair(agent, errorReason) {
137
+ return [
138
+ `${agent}`,
139
+ ` ${errorReason}`,
140
+ ].join("\n");
141
+ }
142
+ function writeDeclinedRepair(degraded, command, deps) {
143
+ deps.writeStdout(renderDeferredRepair(degraded.agent, fallbackCommandsFor(degraded, command)));
72
144
  }
73
145
  function runnableRepairActionFor(degraded) {
74
146
  const typedAction = degraded.issue?.actions
@@ -106,16 +178,9 @@ function typedActionToRunnable(degraded, action) {
106
178
  return undefined;
107
179
  }
108
180
  function writeRepairQueueSummary(degraded, deps) {
109
- const repairable = degraded
110
- .map((entry) => ({ entry, action: runnableRepairActionFor(entry) }))
111
- .filter((item) => item.action !== undefined);
112
- if (repairable.length < 2)
113
- return;
114
- const lines = [
115
- "repair queue:",
116
- ...repairable.map(({ entry, action }) => ` - ${entry.agent}: ${action.label}: \`${action.command}\``),
117
- ];
118
- deps.writeStdout(lines.join("\n"));
181
+ const lines = renderRepairQueueSummaryLines(degraded);
182
+ if (lines.length > 0)
183
+ deps.writeStdout(lines.join("\n"));
119
184
  }
120
185
  async function runInteractiveRepair(degraded, deps) {
121
186
  (0, runtime_1.emitNervesEvent)({
@@ -133,11 +198,12 @@ async function runInteractiveRepair(degraded, deps) {
133
198
  for (const entry of degraded) {
134
199
  const action = runnableRepairActionFor(entry);
135
200
  if (action?.kind === "vault-unlock") {
136
- const answer = await deps.promptInput(`run \`${action.command}\` now? Only say yes if you have the saved unlock secret. [y/n] `);
201
+ deps.writeStdout(renderActionPromptLines(entry.agent, action).join("\n"));
202
+ const answer = await deps.promptInput("Unlock it now? [y/N] ");
137
203
  if (isAffirmativeAnswer(answer)) {
138
204
  try {
139
205
  if (!deps.runVaultUnlock) {
140
- deps.writeStdout(`fix hint for ${entry.agent}: ${entry.fixHint}`);
206
+ deps.writeStdout(renderManualRepairHint(entry.agent, entry.fixHint));
141
207
  }
142
208
  else {
143
209
  await deps.runVaultUnlock(entry.agent);
@@ -146,7 +212,7 @@ async function runInteractiveRepair(degraded, deps) {
146
212
  }
147
213
  catch (error) {
148
214
  const msg = error instanceof Error ? error.message : String(error);
149
- deps.writeStdout(`vault unlock error for ${entry.agent}: ${msg}`);
215
+ deps.writeStdout(`Vault unlock did not finish for ${entry.agent}.\n ${msg}`);
150
216
  repairsAttempted = true;
151
217
  (0, runtime_1.emitNervesEvent)({
152
218
  level: "error",
@@ -162,7 +228,8 @@ async function runInteractiveRepair(degraded, deps) {
162
228
  }
163
229
  }
164
230
  else if (action?.kind === "provider-auth") {
165
- const answer = await deps.promptInput(`run \`${action.command}\` now? [y/n] `);
231
+ deps.writeStdout(renderActionPromptLines(entry.agent, action).join("\n"));
232
+ const answer = await deps.promptInput("Open the auth flow now? [y/N] ");
166
233
  if (isAffirmativeAnswer(answer)) {
167
234
  try {
168
235
  if (action.provider) {
@@ -175,7 +242,7 @@ async function runInteractiveRepair(degraded, deps) {
175
242
  }
176
243
  catch (error) {
177
244
  const msg = error instanceof Error ? error.message : String(error);
178
- deps.writeStdout(`auth flow error for ${entry.agent}: ${msg}`);
245
+ deps.writeStdout(`Auth did not finish for ${entry.agent}.\n ${msg}`);
179
246
  repairsAttempted = true;
180
247
  (0, runtime_1.emitNervesEvent)({
181
248
  level: "error",
@@ -191,11 +258,11 @@ async function runInteractiveRepair(degraded, deps) {
191
258
  }
192
259
  }
193
260
  else if (isConfigError(entry)) {
194
- deps.writeStdout(`fix hint for ${entry.agent}: ${entry.fixHint}`);
261
+ deps.writeStdout(renderManualRepairHint(entry.agent, entry.fixHint));
195
262
  }
196
263
  else {
197
264
  // Unknown error with no actionable fix hint
198
- deps.writeStdout(`${entry.agent}: ${entry.errorReason}`);
265
+ deps.writeStdout(renderUnknownRepair(entry.agent, entry.errorReason));
199
266
  }
200
267
  }
201
268
  (0, runtime_1.emitNervesEvent)({
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.vaultLockedIssue = vaultLockedIssue;
4
+ exports.vaultUnconfiguredIssue = vaultUnconfiguredIssue;
4
5
  exports.providerCredentialMissingIssue = providerCredentialMissingIssue;
5
6
  exports.providerLiveCheckFailedIssue = providerLiveCheckFailedIssue;
6
7
  exports.genericReadinessIssue = genericReadinessIssue;
@@ -39,6 +40,30 @@ function vaultLockedIssue(agentName) {
39
40
  ],
40
41
  };
41
42
  }
43
+ function vaultUnconfiguredIssue(agentName) {
44
+ return {
45
+ kind: "vault-unconfigured",
46
+ severity: "blocked",
47
+ actor: "human-required",
48
+ summary: `${agentName}: vault not configured`,
49
+ detail: "This bundle does not have a vault locator in agent.json yet. Create the agent vault before authenticating providers.",
50
+ actions: [
51
+ {
52
+ kind: "vault-create",
53
+ label: "Create this agent's vault",
54
+ command: `ouro vault create --agent ${agentName}`,
55
+ actor: "human-required",
56
+ },
57
+ {
58
+ kind: "vault-recover",
59
+ label: "Recover from JSON export",
60
+ command: `ouro vault recover --agent ${agentName} --from <json>`,
61
+ actor: "human-required",
62
+ executable: false,
63
+ },
64
+ ],
65
+ };
66
+ }
42
67
  function providerCredentialMissingIssue(input) {
43
68
  return {
44
69
  kind: "provider-credentials-missing",
@@ -117,20 +142,28 @@ function renderReadinessIssue(issue) {
117
142
  if (issue.detail) {
118
143
  lines.push(` ${issue.detail}`);
119
144
  }
145
+ if (issue.actions.length > 0) {
146
+ lines.push("");
147
+ }
120
148
  issue.actions.forEach((action, index) => {
149
+ if (index > 0)
150
+ lines.push("");
121
151
  lines.push(`${index + 1}. ${action.label}`);
122
152
  lines.push(` ${action.command}`);
123
153
  });
154
+ if (issue.actions.length > 0) {
155
+ lines.push("");
156
+ }
124
157
  lines.push(`${issue.actions.length + 1}. Skip for now`);
125
158
  return lines.join("\n");
126
159
  }
127
160
  function renderReadinessIssueNextSteps(issue) {
128
- const lines = [` ${issue.summary}`];
161
+ const lines = [issue.summary];
129
162
  if (issue.detail && issue.kind !== "vault-locked") {
130
- lines.push(` ${issue.detail}`);
163
+ lines.push(` ${issue.detail}`);
131
164
  }
132
165
  issue.actions.forEach((action, index) => {
133
- lines.push(` ${index === 0 ? "next" : "or"}: ${action.command}`);
166
+ lines.push(` ${index === 0 ? "next" : "or"}: ${action.command}`);
134
167
  });
135
168
  return lines;
136
169
  }
@@ -58,10 +58,10 @@ function loadVaultSectionForAgent(agentName) {
58
58
  const configPath = path.join(identity.getAgentRoot(agentName), "agent.json");
59
59
  try {
60
60
  const parsed = JSON.parse(fs.readFileSync(configPath, "utf-8"));
61
- return parsed.vault;
61
+ return { configPath, vault: parsed.vault };
62
62
  }
63
63
  catch {
64
- return undefined;
64
+ return { configPath };
65
65
  }
66
66
  }
67
67
  function bitwardenAppDataDir(agentName, vaultConfig) {
@@ -77,7 +77,11 @@ function getCredentialStore(agentNameInput) {
77
77
  if (agentName === "SerpentGuide") {
78
78
  throw new Error("SerpentGuide does not have a persistent credential vault; hatch bootstrap uses provider credentials in memory only.");
79
79
  }
80
- const vaultConfig = identity.resolveVaultConfig(agentName, loadVaultSectionForAgent(agentName));
80
+ const { configPath, vault } = loadVaultSectionForAgent(agentName);
81
+ if (!vault || typeof vault.email !== "string" || vault.email.trim().length === 0) {
82
+ throw new Error((0, vault_unlock_1.credentialVaultNotConfiguredError)(agentName, configPath));
83
+ }
84
+ const vaultConfig = identity.resolveVaultConfig(agentName, vault);
81
85
  const cacheKey = `${agentName}:${vaultConfig.serverUrl}:${vaultConfig.email}`;
82
86
  const cached = stores.get(cacheKey);
83
87
  if (cached)
@@ -34,6 +34,9 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.vaultUnlockReplaceRecoverFix = vaultUnlockReplaceRecoverFix;
37
+ exports.credentialVaultNotConfiguredError = credentialVaultNotConfiguredError;
38
+ exports.isCredentialVaultNotConfiguredError = isCredentialVaultNotConfiguredError;
39
+ exports.vaultCreateRecoverFix = vaultCreateRecoverFix;
37
40
  exports.resolveVaultUnlockStore = resolveVaultUnlockStore;
38
41
  exports.readVaultUnlockSecret = readVaultUnlockSecret;
39
42
  exports.storeVaultUnlockSecret = storeVaultUnlockSecret;
@@ -45,6 +48,7 @@ const os = __importStar(require("node:os"));
45
48
  const path = __importStar(require("node:path"));
46
49
  const runtime_1 = require("../nerves/runtime");
47
50
  const VAULT_UNLOCK_SERVICE = "ouro.vault";
51
+ const CREDENTIAL_VAULT_NOT_CONFIGURED_PREFIX = "credential vault is not configured in ";
48
52
  const PLAINTEXT_UNLOCK_DIR = path.join(".ouro-cli", "vault-unlock");
49
53
  const WINDOWS_DPAPI_UNLOCK_DIR = path.join(".ouro-cli", "vault-unlock-dpapi");
50
54
  const SUPPORTED_STORES = ["auto", "macos-keychain", "windows-dpapi", "linux-secret-service", "plaintext-file"];
@@ -104,6 +108,20 @@ function vaultUnlockReplaceRecoverFix(agentName, nextStep = "Then run 'ouro up'
104
108
  nextStep,
105
109
  ].join(" ");
106
110
  }
111
+ function credentialVaultNotConfiguredError(agentName, configPath) {
112
+ return (`${CREDENTIAL_VAULT_NOT_CONFIGURED_PREFIX}${configPath}. ` +
113
+ `Run 'ouro vault create --agent ${agentName}' to create this agent's vault before loading or storing credentials.`);
114
+ }
115
+ function isCredentialVaultNotConfiguredError(message) {
116
+ return message.includes(CREDENTIAL_VAULT_NOT_CONFIGURED_PREFIX);
117
+ }
118
+ function vaultCreateRecoverFix(agentName, nextStep = "Then run 'ouro up' again.") {
119
+ return [
120
+ `Run 'ouro vault create --agent ${agentName}' to create this agent's vault.`,
121
+ `If you still have a local JSON credential export from an earlier alpha, run 'ouro vault recover --agent ${agentName} --from <json>' instead.`,
122
+ nextStep,
123
+ ].join(" ");
124
+ }
107
125
  function lostUnlockSecretGuidance(config) {
108
126
  if (!config.agentName) {
109
127
  return "If nobody saved that unlock secret, run `ouro vault replace --agent <agent>` to create a new empty vault and re-enter credentials. If you do have a local JSON credential export, run `ouro vault recover --agent <agent> --from <json>` to import it.";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.407",
3
+ "version": "0.1.0-alpha.409",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",
@@ -32,6 +32,7 @@
32
32
  "test:coverage": "node scripts/run-coverage-gate.cjs",
33
33
  "build": "tsc && (cd packages/outlook-ui && npm install --ignore-scripts 2>/dev/null && npm run build && cp -r dist ../../dist/outlook-ui) || echo 'outlook-ui build skipped'",
34
34
  "lint": "eslint src/",
35
+ "release:preflight": "node scripts/release-preflight.cjs",
35
36
  "release:smoke": "node scripts/release-smoke.cjs",
36
37
  "audit:nerves": "npm run build && node dist/nerves/coverage/cli-main.js"
37
38
  },