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

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.395",
6
+ "changes": [
7
+ "`ouro vault replace` and `ouro vault recover` now default to the stable agent vault email, `<agent>@ouro.bot`, instead of timestamped `+replaced` or `+recovered` addresses.",
8
+ "Vault repair now treats previously generated repair emails as stale defaults and repairs back to the stable agent email unless the human explicitly supplies `--email <email>`.",
9
+ "Existing-account repair guidance now tells operators to unlock the stable vault when possible and use `--email` only when intentionally moving an agent to a different vault account.",
10
+ "Auth/provider docs and CLI help now describe stable vault identity repair for old auth-style agents without implying that Ouro can recover a forgotten unlock secret.",
11
+ "`@ouro.bot/cli` and the `ouro.bot` wrapper are version-synced for the stable vault identity repair release."
12
+ ]
13
+ },
14
+ {
15
+ "version": "0.1.0-alpha.394",
16
+ "changes": [
17
+ "Added `ouro vault replace --agent <agent>` for existing pre-vault agents whose bundle has vault coordinates but no saved unlock secret and no local JSON credential export.",
18
+ "`ouro vault status --agent <agent>` now clearly reports when an existing agent has not configured a vault locator yet and points to `ouro vault create --agent <agent>` instead of implying an unlockable vault exists.",
19
+ "Locked-vault repair guidance now distinguishes saved-secret unlock, no-export replacement, and JSON-export recovery across provider checks, auth, provider refresh, vault config status, and vault unlock errors.",
20
+ "Interactive repair prompts now warn humans to run vault unlock only when they have the saved unlock secret, and docs explain how old auth-style agents continue by replacing the vault and re-entering credentials.",
21
+ "`@ouro.bot/cli` and the `ouro.bot` wrapper are version-synced for the pre-vault replacement repair release."
22
+ ]
23
+ },
4
24
  {
5
25
  "version": "0.1.0-alpha.393",
6
26
  "changes": [
@@ -49,6 +49,7 @@ const identity_1 = require("../identity");
49
49
  const migrate_config_1 = require("../migrate-config");
50
50
  const provider_models_1 = require("../provider-models");
51
51
  const provider_credentials_1 = require("../provider-credentials");
52
+ const vault_unlock_1 = require("../../repertoire/vault-unlock");
52
53
  const ANTHROPIC_SETUP_TOKEN_PREFIX = "sk-ant-oat01-";
53
54
  const ANTHROPIC_SETUP_TOKEN_MIN_LENGTH = 80;
54
55
  function assertPersistentProviderCredentialsAllowed(agentName) {
@@ -358,7 +359,7 @@ async function runRuntimeAuthFlow(input, deps = {}) {
358
359
  });
359
360
  const vault = await (0, provider_credentials_1.refreshProviderCredentialPool)(input.agentName);
360
361
  if (!vault.ok && vault.reason === "unavailable") {
361
- throw new Error(`${vault.error}\nRun \`ouro vault unlock --agent ${input.agentName}\`, then retry auth.`);
362
+ throw new Error(`${vault.error}\n${(0, vault_unlock_1.vaultUnlockReplaceRecoverFix)(input.agentName, `Then retry 'ouro auth --agent ${input.agentName} --provider ${input.provider}'.`)}`);
362
363
  }
363
364
  const credentials = await collectRuntimeAuthCredentials(input, deps);
364
365
  const { credentialPath } = await storeProviderCredentials(input.agentName, input.provider, credentials);
@@ -44,6 +44,7 @@ const provider_models_1 = require("../provider-models");
44
44
  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
+ const vault_unlock_1 = require("../../repertoire/vault-unlock");
47
48
  function isAgentProvider(value) {
48
49
  return Object.prototype.hasOwnProperty.call(identity_1.PROVIDER_CREDENTIALS, value);
49
50
  }
@@ -260,11 +261,7 @@ function isVaultLockedError(error) {
260
261
  return /(?:ouro )?credential vault is locked|vault(?: is)? locked/.test(normalized);
261
262
  }
262
263
  function vaultUnlockOrRecoverFix(agentName, nextStep = "Then run 'ouro up' again.") {
263
- return [
264
- `Run 'ouro vault unlock --agent ${agentName}' if you have the saved vault unlock secret.`,
265
- `If nobody saved it, run 'ouro vault recover --agent ${agentName} --from <json>' with a local credential export.`,
266
- nextStep,
267
- ].join(" ");
264
+ return (0, vault_unlock_1.vaultUnlockReplaceRecoverFix)(agentName, nextStep);
268
265
  }
269
266
  function failedPingResult(agentName, lane, provider, model, result) {
270
267
  return {
@@ -686,13 +686,22 @@ function readVaultRecoverSource(sourcePath) {
686
686
  runtimeConfig: recoverRuntimeConfig(parsed),
687
687
  };
688
688
  }
689
- function defaultRecoveredVaultEmail(agentName, now) {
689
+ function defaultStableVaultEmail(agentName) {
690
690
  const local = agentName
691
691
  .toLowerCase()
692
692
  .replace(/[^a-z0-9._-]+/g, "-")
693
693
  .replace(/^-+|-+$/g, "") || "agent";
694
- const stamp = now.toISOString().replace(/[-:.TZ]/g, "").slice(0, 14);
695
- return `${local}+recovered-${stamp}@ouro.bot`;
694
+ return `${local}@ouro.bot`;
695
+ }
696
+ function isGeneratedRepairVaultEmail(email) {
697
+ const [local, domain] = email.trim().split("@");
698
+ return domain?.toLowerCase() === "ouro.bot" && /\+(?:replaced|recovered)-\d{14}(?:$|\+)/i.test(local);
699
+ }
700
+ function defaultRepairVaultEmail(agentName, config) {
701
+ const configuredEmail = config.vault?.email?.trim();
702
+ if (configuredEmail && !isGeneratedRepairVaultEmail(configuredEmail))
703
+ return configuredEmail;
704
+ return defaultStableVaultEmail(agentName);
696
705
  }
697
706
  function ensureVaultSecretPrompt(promptSecret, action) {
698
707
  if (promptSecret)
@@ -702,6 +711,31 @@ function ensureVaultSecretPrompt(promptSecret, action) {
702
711
  function rejectGeneratedVaultUnlockSecret(action) {
703
712
  throw new Error(`vault ${action} no longer supports --generate-unlock-secret. Re-run without that flag and enter a human-chosen unlock secret; Ouro will not print vault unlock secrets.`);
704
713
  }
714
+ async function createRepairVaultForAgent(input) {
715
+ const result = await (0, vault_setup_1.createVaultAccount)("Ouro credential vault", input.serverUrl, input.email, input.unlockSecret);
716
+ if (!result.success) {
717
+ const message = [
718
+ `vault ${input.action} failed for ${input.agentName}: ${result.error}`,
719
+ "",
720
+ "Could not create the selected vault account.",
721
+ "If this is the existing vault, run:",
722
+ ` ouro vault unlock --agent ${input.agentName}`,
723
+ "If the unlock secret is lost and you intentionally need a different vault account, rerun with --email <email>.",
724
+ "If this looks like a server or network issue, check --server and retry.",
725
+ ].join("\n");
726
+ input.deps.writeStdout(message);
727
+ return { ok: false, message };
728
+ }
729
+ writeAgentVaultConfig(input.agentName, input.configPath, input.config, { email: input.email, serverUrl: input.serverUrl });
730
+ const store = (0, vault_unlock_1.storeVaultUnlockSecret)({
731
+ agentName: input.agentName,
732
+ email: input.email,
733
+ serverUrl: input.serverUrl,
734
+ }, input.unlockSecret, { homeDir: input.deps.homeDir, store: input.store });
735
+ (0, credential_access_1.resetCredentialStore)();
736
+ await (0, credential_access_1.getCredentialStore)(input.agentName).get("__ouro_vault_probe__");
737
+ return { ok: true, store };
738
+ }
705
739
  async function executeVaultUnlock(command, deps) {
706
740
  if (command.agent === "SerpentGuide") {
707
741
  throw new Error("SerpentGuide does not have a persistent credential vault. Hatch bootstrap uses selected provider credentials in memory only.");
@@ -773,6 +807,50 @@ async function executeVaultCreate(command, deps) {
773
807
  deps.writeStdout(message);
774
808
  return message;
775
809
  }
810
+ async function executeVaultReplace(command, deps) {
811
+ if (command.agent === "SerpentGuide") {
812
+ throw new Error("SerpentGuide does not have a persistent credential vault. Replace the hatchling agent vault, not SerpentGuide.");
813
+ }
814
+ if (command.generateUnlockSecret)
815
+ rejectGeneratedVaultUnlockSecret("replace");
816
+ const promptSecret = ensureVaultSecretPrompt(deps.promptSecret, "replace");
817
+ const { configPath, config } = (0, auth_flow_1.readAgentConfigForAgent)(command.agent, deps.bundlesRoot);
818
+ const configuredVault = (0, identity_1.resolveVaultConfig)(command.agent, config.vault);
819
+ const email = command.email ?? defaultRepairVaultEmail(command.agent, config);
820
+ const serverUrl = command.serverUrl ?? config.vault?.serverUrl ?? configuredVault.serverUrl;
821
+ const unlockSecret = (await promptSecret(`Choose Ouro vault unlock secret for ${email}: `)).trim();
822
+ if (!unlockSecret) {
823
+ throw new Error("vault replace requires an unlock secret. Re-run in an interactive terminal and enter a human-chosen unlock secret.");
824
+ }
825
+ const repair = await createRepairVaultForAgent({
826
+ action: "replace",
827
+ agentName: command.agent,
828
+ email,
829
+ serverUrl,
830
+ unlockSecret,
831
+ store: command.store,
832
+ deps,
833
+ configPath,
834
+ config,
835
+ });
836
+ if (!repair.ok)
837
+ return repair.message;
838
+ const message = [
839
+ `vault replaced for ${command.agent}`,
840
+ `vault: ${email} at ${serverUrl}`,
841
+ `local unlock store: ${repair.store.kind}${repair.store.secure ? "" : " (explicit plaintext fallback)"}`,
842
+ "credentials imported: none",
843
+ "This is the no-export path for agents that predate vault auth or lost an unsaved unlock secret.",
844
+ "Re-auth/re-enter the credentials this agent should use:",
845
+ ` ouro auth --agent ${command.agent} --provider <provider>`,
846
+ ` ouro vault config set --agent ${command.agent} --key <field>`,
847
+ ` ouro provider refresh --agent ${command.agent}`,
848
+ ` ouro auth verify --agent ${command.agent}`,
849
+ "Keep the vault unlock secret saved outside Ouro. Another machine will need it once.",
850
+ ].join("\n");
851
+ deps.writeStdout(message);
852
+ return message;
853
+ }
776
854
  async function executeVaultRecover(command, deps) {
777
855
  if (command.agent === "SerpentGuide") {
778
856
  throw new Error("SerpentGuide does not have a persistent credential vault. Recover the hatchling agent vault, not SerpentGuide.");
@@ -784,30 +862,25 @@ async function executeVaultRecover(command, deps) {
784
862
  const now = providerCliNow(deps);
785
863
  const { configPath, config } = (0, auth_flow_1.readAgentConfigForAgent)(command.agent, deps.bundlesRoot);
786
864
  const configuredVault = (0, identity_1.resolveVaultConfig)(command.agent, config.vault);
787
- const email = command.email ?? defaultRecoveredVaultEmail(command.agent, now);
865
+ const email = command.email ?? defaultRepairVaultEmail(command.agent, config);
788
866
  const serverUrl = command.serverUrl ?? config.vault?.serverUrl ?? configuredVault.serverUrl;
789
- const unlockSecret = (await promptSecret(`Choose replacement Ouro vault unlock secret for ${email}: `)).trim();
867
+ const unlockSecret = (await promptSecret(`Choose Ouro vault unlock secret for ${email}: `)).trim();
790
868
  if (!unlockSecret) {
791
- throw new Error("vault recover requires a replacement unlock secret. Re-run in an interactive terminal and enter a human-chosen unlock secret.");
792
- }
793
- const result = await (0, vault_setup_1.createVaultAccount)("Ouro credential vault", serverUrl, email, unlockSecret);
794
- if (!result.success) {
795
- const message = [
796
- `vault recover failed for ${command.agent}: ${result.error}`,
797
- "",
798
- "Recovery creates a replacement vault. If that vault account already exists, retry with a fresh --email value.",
799
- ].join("\n");
800
- deps.writeStdout(message);
801
- return message;
869
+ throw new Error("vault recover requires an unlock secret. Re-run in an interactive terminal and enter a human-chosen unlock secret.");
802
870
  }
803
- writeAgentVaultConfig(command.agent, configPath, config, { email, serverUrl });
804
- const store = (0, vault_unlock_1.storeVaultUnlockSecret)({
871
+ const repair = await createRepairVaultForAgent({
872
+ action: "recover",
805
873
  agentName: command.agent,
806
874
  email,
807
875
  serverUrl,
808
- }, unlockSecret, { homeDir: deps.homeDir, store: command.store });
809
- (0, credential_access_1.resetCredentialStore)();
810
- await (0, credential_access_1.getCredentialStore)(command.agent).get("__ouro_vault_probe__");
876
+ unlockSecret,
877
+ store: command.store,
878
+ deps,
879
+ configPath,
880
+ config,
881
+ });
882
+ if (!repair.ok)
883
+ return repair.message;
811
884
  const importedProviders = new Set();
812
885
  let mergedRuntimeConfig = {};
813
886
  for (const source of sourceImports) {
@@ -832,12 +905,12 @@ async function executeVaultRecover(command, deps) {
832
905
  const message = [
833
906
  `vault recovered for ${command.agent}`,
834
907
  `vault: ${email} at ${serverUrl}`,
835
- `local unlock store: ${store.kind}${store.secure ? "" : " (explicit plaintext fallback)"}`,
908
+ `local unlock store: ${repair.store.kind}${repair.store.secure ? "" : " (explicit plaintext fallback)"}`,
836
909
  `sources imported: ${sourceImports.length}`,
837
910
  `provider credentials imported: ${providerList.length === 0 ? "none" : providerList.join(", ")}`,
838
911
  `runtime credentials imported: ${runtimeFields.length === 0 ? "none" : runtimeFields.join(", ")}`,
839
912
  "credential values were not printed",
840
- "Keep the replacement vault unlock secret saved outside Ouro. Another machine will need it once.",
913
+ "Keep the vault unlock secret saved outside Ouro. Another machine will need it once.",
841
914
  ].join("\n");
842
915
  deps.writeStdout(message);
843
916
  return message;
@@ -850,6 +923,19 @@ async function executeVaultStatus(command, deps) {
850
923
  }
851
924
  const { config } = (0, auth_flow_1.readAgentConfigForAgent)(command.agent, deps.bundlesRoot);
852
925
  const vault = (0, identity_1.resolveVaultConfig)(command.agent, config.vault);
926
+ if (!config.vault) {
927
+ const lines = [
928
+ `agent: ${command.agent}`,
929
+ `vault: ${vault.email} at ${vault.serverUrl}`,
930
+ "vault locator: not configured in agent.json",
931
+ "local unlock: not checked",
932
+ "",
933
+ `fix: Run 'ouro vault create --agent ${command.agent}' to create this agent's vault, then run 'ouro auth --agent ${command.agent} --provider <provider>' and 'ouro provider refresh --agent ${command.agent}'.`,
934
+ ];
935
+ const message = lines.join("\n");
936
+ deps.writeStdout(message);
937
+ return message;
938
+ }
853
939
  const status = (0, vault_unlock_1.getVaultUnlockStatus)({
854
940
  agentName: command.agent,
855
941
  email: vault.email,
@@ -858,7 +944,7 @@ async function executeVaultStatus(command, deps) {
858
944
  const lines = [
859
945
  `agent: ${command.agent}`,
860
946
  `vault: ${vault.email} at ${vault.serverUrl}`,
861
- `vault locator: ${config.vault ? "agent.json" : "default"}`,
947
+ "vault locator: agent.json",
862
948
  `local unlock store: ${status.store ? `${status.store.kind}${status.store.secure ? "" : " (explicit plaintext fallback)"}` : "unavailable"}`,
863
949
  `local unlock: ${status.stored ? "available" : "missing"}`,
864
950
  ];
@@ -976,7 +1062,7 @@ async function executeVaultConfigStatus(command, deps) {
976
1062
  lines.push(`error: ${runtime.error}`);
977
1063
  lines.push(runtime.reason === "missing"
978
1064
  ? `fix: Run 'ouro vault config set --agent ${command.agent} --key <field>' to store runtime credentials.`
979
- : `fix: Run 'ouro vault unlock --agent ${command.agent}', then retry.`);
1065
+ : `fix: ${(0, vault_unlock_1.vaultUnlockReplaceRecoverFix)(command.agent, "Then retry 'ouro vault config status'.")}`);
980
1066
  }
981
1067
  const message = lines.join("\n");
982
1068
  deps.writeStdout(message);
@@ -1243,7 +1329,7 @@ async function executeProviderRefresh(command, deps) {
1243
1329
  }
1244
1330
  else {
1245
1331
  lines.push(`provider credential refresh failed for ${command.agent}: ${pool.error}`);
1246
- lines.push(`Run \`ouro vault unlock --agent ${command.agent}\`, then retry.`);
1332
+ lines.push((0, vault_unlock_1.vaultUnlockReplaceRecoverFix)(command.agent, "Then retry 'ouro provider refresh'."));
1247
1333
  const message = lines.join("\n");
1248
1334
  deps.writeStdout(message);
1249
1335
  return message;
@@ -2714,6 +2800,9 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
2714
2800
  if (command.kind === "vault.create") {
2715
2801
  return executeVaultCreate(command, deps);
2716
2802
  }
2803
+ if (command.kind === "vault.replace") {
2804
+ return executeVaultReplace(command, deps);
2805
+ }
2717
2806
  if (command.kind === "vault.recover") {
2718
2807
  return executeVaultRecover(command, deps);
2719
2808
  }
@@ -2761,7 +2850,7 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
2761
2850
  if (command.kind === "auth.verify") {
2762
2851
  const poolResult = await (0, provider_credentials_1.refreshProviderCredentialPool)(command.agent);
2763
2852
  if (!poolResult.ok) {
2764
- const message = `vault unavailable: ${poolResult.error}\nRun \`ouro vault unlock --agent ${command.agent}\`, then retry.`;
2853
+ const message = `vault unavailable: ${poolResult.error}\n${(0, vault_unlock_1.vaultUnlockReplaceRecoverFix)(command.agent, "Then retry 'ouro auth verify'.")}`;
2765
2854
  deps.writeStdout(message);
2766
2855
  return message;
2767
2856
  }
@@ -184,10 +184,10 @@ exports.COMMAND_REGISTRY = {
184
184
  },
185
185
  vault: {
186
186
  category: "Auth",
187
- description: "Create, recover, unlock, inspect, and populate the agent credential vault",
188
- usage: "ouro vault <create|recover|unlock|status|config> --agent <name>",
187
+ description: "Create, replace, recover, unlock, inspect, and populate the agent credential vault",
188
+ usage: "ouro vault <create|replace|recover|unlock|status|config> --agent <name>",
189
189
  example: "ouro vault status --agent ouroboros",
190
- subcommands: ["create", "recover", "unlock", "status", "config set", "config status"],
190
+ subcommands: ["create", "replace", "recover", "unlock", "status", "config set", "config status"],
191
191
  },
192
192
  thoughts: {
193
193
  category: "Internal",
@@ -269,8 +269,13 @@ const SUBCOMMAND_HELP = {
269
269
  usage: "ouro vault create --agent <name> --email <email> [--server <url>] [--store <store>]",
270
270
  example: "ouro vault create --agent ouroboros --email ouroboros@ouro.bot",
271
271
  },
272
+ "vault replace": {
273
+ description: "Create an empty agent vault at the stable agent email when no unlock secret or JSON export exists",
274
+ usage: "ouro vault replace --agent <name> [--email <email>] [--server <url>] [--store <store>]",
275
+ example: "ouro vault replace --agent ouroboros",
276
+ },
272
277
  "vault recover": {
273
- description: "Create a replacement agent vault and import local JSON credential exports",
278
+ description: "Create an agent vault at the stable agent email and import local JSON credential exports",
274
279
  usage: "ouro vault recover --agent <name> --from <json> [--from <json>] [--email <email>] [--server <url>] [--store <store>]",
275
280
  example: "ouro vault recover --agent ouroboros --from ./credentials.json",
276
281
  },
@@ -85,6 +85,7 @@ function usage() {
85
85
  " ouro auth verify --agent <name> [--provider <provider>]",
86
86
  " ouro auth switch --agent <name> --provider <provider>",
87
87
  " ouro vault create --agent <name> --email <email> [--server <url>] [--store <store>]",
88
+ " ouro vault replace --agent <name> [--email <email>] [--server <url>] [--store <store>]",
88
89
  " ouro vault recover --agent <name> --from <json> [--from <json>] [--email <email>] [--server <url>] [--store <store>]",
89
90
  " ouro vault unlock --agent <name> [--store auto|macos-keychain|windows-dpapi|linux-secret-service|plaintext-file]",
90
91
  " ouro vault status --agent <name> [--store auto|macos-keychain|windows-dpapi|linux-secret-service|plaintext-file]",
@@ -479,6 +480,9 @@ function parseVaultCommand(args) {
479
480
  continue;
480
481
  }
481
482
  if (token === "--from") {
483
+ if (sub !== "recover") {
484
+ throw new Error("--from is only valid with `ouro vault recover`; use `ouro vault replace` when there is no JSON export to import.");
485
+ }
482
486
  const value = rest[i + 1];
483
487
  if (!value)
484
488
  throw new Error("Usage: ouro vault recover --agent <name> --from <json> [--from <json>]");
@@ -490,10 +494,10 @@ function parseVaultCommand(args) {
490
494
  generateUnlockSecret = true;
491
495
  continue;
492
496
  }
493
- throw new Error("Usage: ouro vault create|recover|unlock|status --agent <name>");
497
+ throw new Error("Usage: ouro vault create|replace|recover|unlock|status --agent <name>");
494
498
  }
495
- if (!agent || (sub !== "create" && sub !== "recover" && sub !== "unlock" && sub !== "status")) {
496
- throw new Error("Usage: ouro vault create|recover|unlock|status --agent <name>");
499
+ if (!agent || (sub !== "create" && sub !== "replace" && sub !== "recover" && sub !== "unlock" && sub !== "status")) {
500
+ throw new Error("Usage: ouro vault create|replace|recover|unlock|status --agent <name>");
497
501
  }
498
502
  if (sub === "create") {
499
503
  return {
@@ -505,6 +509,16 @@ function parseVaultCommand(args) {
505
509
  ...(generateUnlockSecret ? { generateUnlockSecret: true } : {}),
506
510
  };
507
511
  }
512
+ if (sub === "replace") {
513
+ return {
514
+ kind: "vault.replace",
515
+ agent,
516
+ ...(email ? { email } : {}),
517
+ ...(serverUrl ? { serverUrl } : {}),
518
+ ...(store ? { store } : {}),
519
+ ...(generateUnlockSecret ? { generateUnlockSecret: true } : {}),
520
+ };
521
+ }
508
522
  if (sub === "recover") {
509
523
  if (sources.length === 0) {
510
524
  throw new Error("Usage: ouro vault recover --agent <name> --from <json> [--from <json>]");
@@ -63,6 +63,9 @@ function isAffirmativeAnswer(answer) {
63
63
  }
64
64
  function writeDeclinedRepair(degraded, command, deps) {
65
65
  deps.writeStdout(`repair skipped for ${degraded.agent}; run \`${command}\` later.`);
66
+ if (degraded.fixHint.includes("ouro vault replace") || degraded.fixHint.includes("ouro vault recover")) {
67
+ deps.writeStdout(`repair options for ${degraded.agent}: ${degraded.fixHint}`);
68
+ }
66
69
  }
67
70
  function runnableRepairActionFor(degraded) {
68
71
  if (isVaultUnlockIssue(degraded)) {
@@ -106,7 +109,7 @@ async function runInteractiveRepair(degraded, deps) {
106
109
  for (const entry of degraded) {
107
110
  const action = runnableRepairActionFor(entry);
108
111
  if (action?.kind === "vault-unlock") {
109
- const answer = await deps.promptInput(`run \`${action.command}\` now? [y/n] `);
112
+ const answer = await deps.promptInput(`run \`${action.command}\` now? Only say yes if you have the saved unlock secret. [y/n] `);
110
113
  if (isAffirmativeAnswer(answer)) {
111
114
  try {
112
115
  if (!deps.runVaultUnlock) {
@@ -99,7 +99,7 @@ function numberField(record, key, fallback) {
99
99
  function compactRuntimeConfigError(agent, error) {
100
100
  const compact = error.replace(/\s+/g, " ").trim();
101
101
  if (/credential vault is locked|vault locked|vault is locked/i.test(compact)) {
102
- return `vault locked; run 'ouro vault unlock --agent ${agent}'`;
102
+ return `vault locked; run 'ouro vault unlock --agent ${agent}' if you have the saved secret, or 'ouro vault replace --agent ${agent}' if none was saved`;
103
103
  }
104
104
  return compact || "unavailable";
105
105
  }
@@ -33,6 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.vaultUnlockReplaceRecoverFix = vaultUnlockReplaceRecoverFix;
36
37
  exports.resolveVaultUnlockStore = resolveVaultUnlockStore;
37
38
  exports.readVaultUnlockSecret = readVaultUnlockSecret;
38
39
  exports.storeVaultUnlockSecret = storeVaultUnlockSecret;
@@ -95,6 +96,23 @@ function missingSecureStoreMessage(config) {
95
96
  : "Run `ouro vault unlock --store plaintext-file` to store the vault unlock secret in a chmod 0600 local file.",
96
97
  ].join("\n");
97
98
  }
99
+ function vaultUnlockReplaceRecoverFix(agentName, nextStep = "Then run 'ouro up' again.") {
100
+ return [
101
+ `Run 'ouro vault unlock --agent ${agentName}' if you have the saved vault unlock secret.`,
102
+ `If this agent predates vault auth or nobody saved the unlock secret, run 'ouro vault replace --agent ${agentName}' to create a new empty vault, then re-auth/re-enter credentials.`,
103
+ `If you do have a local JSON credential export, run 'ouro vault recover --agent ${agentName} --from <json>' to create the agent vault and import it.`,
104
+ nextStep,
105
+ ].join(" ");
106
+ }
107
+ function lostUnlockSecretGuidance(config) {
108
+ if (!config.agentName) {
109
+ 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.";
110
+ }
111
+ return [
112
+ `If nobody saved that unlock secret, run \`ouro vault replace --agent ${config.agentName}\` to create a new empty vault and re-enter credentials.`,
113
+ `If you do have a local JSON credential export, run \`ouro vault recover --agent ${config.agentName} --from <json>\` to import it.`,
114
+ ].join(" ");
115
+ }
98
116
  function lockedMessage(config, store) {
99
117
  const agentPart = config.agentName ? ` for ${config.agentName}` : "";
100
118
  const command = config.agentName
@@ -111,9 +129,7 @@ function lockedMessage(config, store) {
111
129
  "This can happen on a new computer, after a local profile or hostname migration, or if the local unlock entry was removed.",
112
130
  "",
113
131
  `Run \`${command}\` and enter the saved agent vault unlock secret from the human/operator who controls that vault.`,
114
- config.agentName
115
- ? `If nobody saved that unlock secret, run \`ouro vault recover --agent ${config.agentName} --from <json>\` with a local credential export, or create a replacement vault and re-enter credentials.`
116
- : "If nobody saved that unlock secret, run `ouro vault recover --agent <agent> --from <json>` with a local credential export, or create a replacement vault and re-enter credentials.",
132
+ lostUnlockSecretGuidance(config),
117
133
  ].join("\n");
118
134
  }
119
135
  function validateStoreKind(store) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.393",
3
+ "version": "0.1.0-alpha.395",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",