@ouro.bot/cli 0.1.0-alpha.393 → 0.1.0-alpha.394
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 +10 -0
- package/dist/heart/auth/auth-flow.js +2 -1
- package/dist/heart/daemon/agent-config-check.js +2 -5
- package/dist/heart/daemon/cli-exec.js +105 -22
- package/dist/heart/daemon/cli-help.js +8 -3
- package/dist/heart/daemon/cli-parse.js +17 -3
- package/dist/heart/daemon/interactive-repair.js +4 -1
- package/dist/heart/daemon/sense-manager.js +1 -1
- package/dist/repertoire/vault-unlock.js +19 -3
- package/package.json +1 -1
package/changelog.json
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
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.394",
|
|
6
|
+
"changes": [
|
|
7
|
+
"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.",
|
|
8
|
+
"`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.",
|
|
9
|
+
"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.",
|
|
10
|
+
"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.",
|
|
11
|
+
"`@ouro.bot/cli` and the `ouro.bot` wrapper are version-synced for the pre-vault replacement repair release."
|
|
12
|
+
]
|
|
13
|
+
},
|
|
4
14
|
{
|
|
5
15
|
"version": "0.1.0-alpha.393",
|
|
6
16
|
"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}\
|
|
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,19 @@ function readVaultRecoverSource(sourcePath) {
|
|
|
686
686
|
runtimeConfig: recoverRuntimeConfig(parsed),
|
|
687
687
|
};
|
|
688
688
|
}
|
|
689
|
-
function
|
|
689
|
+
function defaultReplacementVaultEmail(agentName, now, action) {
|
|
690
690
|
const local = agentName
|
|
691
691
|
.toLowerCase()
|
|
692
692
|
.replace(/[^a-z0-9._-]+/g, "-")
|
|
693
693
|
.replace(/^-+|-+$/g, "") || "agent";
|
|
694
694
|
const stamp = now.toISOString().replace(/[-:.TZ]/g, "").slice(0, 14);
|
|
695
|
-
return `${local}
|
|
695
|
+
return `${local}+${action}-${stamp}@ouro.bot`;
|
|
696
|
+
}
|
|
697
|
+
function defaultRecoveredVaultEmail(agentName, now) {
|
|
698
|
+
return defaultReplacementVaultEmail(agentName, now, "recovered");
|
|
699
|
+
}
|
|
700
|
+
function defaultReplacedVaultEmail(agentName, now) {
|
|
701
|
+
return defaultReplacementVaultEmail(agentName, now, "replaced");
|
|
696
702
|
}
|
|
697
703
|
function ensureVaultSecretPrompt(promptSecret, action) {
|
|
698
704
|
if (promptSecret)
|
|
@@ -702,6 +708,27 @@ function ensureVaultSecretPrompt(promptSecret, action) {
|
|
|
702
708
|
function rejectGeneratedVaultUnlockSecret(action) {
|
|
703
709
|
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
710
|
}
|
|
711
|
+
async function createReplacementVaultForAgent(input) {
|
|
712
|
+
const result = await (0, vault_setup_1.createVaultAccount)("Ouro credential vault", input.serverUrl, input.email, input.unlockSecret);
|
|
713
|
+
if (!result.success) {
|
|
714
|
+
const message = [
|
|
715
|
+
`vault ${input.action} failed for ${input.agentName}: ${result.error}`,
|
|
716
|
+
"",
|
|
717
|
+
"This creates a replacement vault. If that vault account already exists, retry with a fresh --email value.",
|
|
718
|
+
].join("\n");
|
|
719
|
+
input.deps.writeStdout(message);
|
|
720
|
+
return { ok: false, message };
|
|
721
|
+
}
|
|
722
|
+
writeAgentVaultConfig(input.agentName, input.configPath, input.config, { email: input.email, serverUrl: input.serverUrl });
|
|
723
|
+
const store = (0, vault_unlock_1.storeVaultUnlockSecret)({
|
|
724
|
+
agentName: input.agentName,
|
|
725
|
+
email: input.email,
|
|
726
|
+
serverUrl: input.serverUrl,
|
|
727
|
+
}, input.unlockSecret, { homeDir: input.deps.homeDir, store: input.store });
|
|
728
|
+
(0, credential_access_1.resetCredentialStore)();
|
|
729
|
+
await (0, credential_access_1.getCredentialStore)(input.agentName).get("__ouro_vault_probe__");
|
|
730
|
+
return { ok: true, store };
|
|
731
|
+
}
|
|
705
732
|
async function executeVaultUnlock(command, deps) {
|
|
706
733
|
if (command.agent === "SerpentGuide") {
|
|
707
734
|
throw new Error("SerpentGuide does not have a persistent credential vault. Hatch bootstrap uses selected provider credentials in memory only.");
|
|
@@ -773,6 +800,51 @@ async function executeVaultCreate(command, deps) {
|
|
|
773
800
|
deps.writeStdout(message);
|
|
774
801
|
return message;
|
|
775
802
|
}
|
|
803
|
+
async function executeVaultReplace(command, deps) {
|
|
804
|
+
if (command.agent === "SerpentGuide") {
|
|
805
|
+
throw new Error("SerpentGuide does not have a persistent credential vault. Replace the hatchling agent vault, not SerpentGuide.");
|
|
806
|
+
}
|
|
807
|
+
if (command.generateUnlockSecret)
|
|
808
|
+
rejectGeneratedVaultUnlockSecret("replace");
|
|
809
|
+
const promptSecret = ensureVaultSecretPrompt(deps.promptSecret, "replace");
|
|
810
|
+
const now = providerCliNow(deps);
|
|
811
|
+
const { configPath, config } = (0, auth_flow_1.readAgentConfigForAgent)(command.agent, deps.bundlesRoot);
|
|
812
|
+
const configuredVault = (0, identity_1.resolveVaultConfig)(command.agent, config.vault);
|
|
813
|
+
const email = command.email ?? defaultReplacedVaultEmail(command.agent, now);
|
|
814
|
+
const serverUrl = command.serverUrl ?? config.vault?.serverUrl ?? configuredVault.serverUrl;
|
|
815
|
+
const unlockSecret = (await promptSecret(`Choose replacement Ouro vault unlock secret for ${email}: `)).trim();
|
|
816
|
+
if (!unlockSecret) {
|
|
817
|
+
throw new Error("vault replace requires a replacement unlock secret. Re-run in an interactive terminal and enter a human-chosen unlock secret.");
|
|
818
|
+
}
|
|
819
|
+
const replacement = await createReplacementVaultForAgent({
|
|
820
|
+
action: "replace",
|
|
821
|
+
agentName: command.agent,
|
|
822
|
+
email,
|
|
823
|
+
serverUrl,
|
|
824
|
+
unlockSecret,
|
|
825
|
+
store: command.store,
|
|
826
|
+
deps,
|
|
827
|
+
configPath,
|
|
828
|
+
config,
|
|
829
|
+
});
|
|
830
|
+
if (!replacement.ok)
|
|
831
|
+
return replacement.message;
|
|
832
|
+
const message = [
|
|
833
|
+
`vault replaced for ${command.agent}`,
|
|
834
|
+
`vault: ${email} at ${serverUrl}`,
|
|
835
|
+
`local unlock store: ${replacement.store.kind}${replacement.store.secure ? "" : " (explicit plaintext fallback)"}`,
|
|
836
|
+
"credentials imported: none",
|
|
837
|
+
"This is the no-export path for agents that predate vault auth or lost an unsaved unlock secret.",
|
|
838
|
+
"Re-auth/re-enter the credentials this agent should use:",
|
|
839
|
+
` ouro auth --agent ${command.agent} --provider <provider>`,
|
|
840
|
+
` ouro vault config set --agent ${command.agent} --key <field>`,
|
|
841
|
+
` ouro provider refresh --agent ${command.agent}`,
|
|
842
|
+
` ouro auth verify --agent ${command.agent}`,
|
|
843
|
+
"Keep the replacement vault unlock secret saved outside Ouro. Another machine will need it once.",
|
|
844
|
+
].join("\n");
|
|
845
|
+
deps.writeStdout(message);
|
|
846
|
+
return message;
|
|
847
|
+
}
|
|
776
848
|
async function executeVaultRecover(command, deps) {
|
|
777
849
|
if (command.agent === "SerpentGuide") {
|
|
778
850
|
throw new Error("SerpentGuide does not have a persistent credential vault. Recover the hatchling agent vault, not SerpentGuide.");
|
|
@@ -790,24 +862,19 @@ async function executeVaultRecover(command, deps) {
|
|
|
790
862
|
if (!unlockSecret) {
|
|
791
863
|
throw new Error("vault recover requires a replacement unlock secret. Re-run in an interactive terminal and enter a human-chosen unlock secret.");
|
|
792
864
|
}
|
|
793
|
-
const
|
|
794
|
-
|
|
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;
|
|
802
|
-
}
|
|
803
|
-
writeAgentVaultConfig(command.agent, configPath, config, { email, serverUrl });
|
|
804
|
-
const store = (0, vault_unlock_1.storeVaultUnlockSecret)({
|
|
865
|
+
const replacement = await createReplacementVaultForAgent({
|
|
866
|
+
action: "recover",
|
|
805
867
|
agentName: command.agent,
|
|
806
868
|
email,
|
|
807
869
|
serverUrl,
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
870
|
+
unlockSecret,
|
|
871
|
+
store: command.store,
|
|
872
|
+
deps,
|
|
873
|
+
configPath,
|
|
874
|
+
config,
|
|
875
|
+
});
|
|
876
|
+
if (!replacement.ok)
|
|
877
|
+
return replacement.message;
|
|
811
878
|
const importedProviders = new Set();
|
|
812
879
|
let mergedRuntimeConfig = {};
|
|
813
880
|
for (const source of sourceImports) {
|
|
@@ -832,7 +899,7 @@ async function executeVaultRecover(command, deps) {
|
|
|
832
899
|
const message = [
|
|
833
900
|
`vault recovered for ${command.agent}`,
|
|
834
901
|
`vault: ${email} at ${serverUrl}`,
|
|
835
|
-
`local unlock store: ${store.kind}${store.secure ? "" : " (explicit plaintext fallback)"}`,
|
|
902
|
+
`local unlock store: ${replacement.store.kind}${replacement.store.secure ? "" : " (explicit plaintext fallback)"}`,
|
|
836
903
|
`sources imported: ${sourceImports.length}`,
|
|
837
904
|
`provider credentials imported: ${providerList.length === 0 ? "none" : providerList.join(", ")}`,
|
|
838
905
|
`runtime credentials imported: ${runtimeFields.length === 0 ? "none" : runtimeFields.join(", ")}`,
|
|
@@ -850,6 +917,19 @@ async function executeVaultStatus(command, deps) {
|
|
|
850
917
|
}
|
|
851
918
|
const { config } = (0, auth_flow_1.readAgentConfigForAgent)(command.agent, deps.bundlesRoot);
|
|
852
919
|
const vault = (0, identity_1.resolveVaultConfig)(command.agent, config.vault);
|
|
920
|
+
if (!config.vault) {
|
|
921
|
+
const lines = [
|
|
922
|
+
`agent: ${command.agent}`,
|
|
923
|
+
`vault: ${vault.email} at ${vault.serverUrl}`,
|
|
924
|
+
"vault locator: not configured in agent.json",
|
|
925
|
+
"local unlock: not checked",
|
|
926
|
+
"",
|
|
927
|
+
`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}'.`,
|
|
928
|
+
];
|
|
929
|
+
const message = lines.join("\n");
|
|
930
|
+
deps.writeStdout(message);
|
|
931
|
+
return message;
|
|
932
|
+
}
|
|
853
933
|
const status = (0, vault_unlock_1.getVaultUnlockStatus)({
|
|
854
934
|
agentName: command.agent,
|
|
855
935
|
email: vault.email,
|
|
@@ -858,7 +938,7 @@ async function executeVaultStatus(command, deps) {
|
|
|
858
938
|
const lines = [
|
|
859
939
|
`agent: ${command.agent}`,
|
|
860
940
|
`vault: ${vault.email} at ${vault.serverUrl}`,
|
|
861
|
-
|
|
941
|
+
"vault locator: agent.json",
|
|
862
942
|
`local unlock store: ${status.store ? `${status.store.kind}${status.store.secure ? "" : " (explicit plaintext fallback)"}` : "unavailable"}`,
|
|
863
943
|
`local unlock: ${status.stored ? "available" : "missing"}`,
|
|
864
944
|
];
|
|
@@ -976,7 +1056,7 @@ async function executeVaultConfigStatus(command, deps) {
|
|
|
976
1056
|
lines.push(`error: ${runtime.error}`);
|
|
977
1057
|
lines.push(runtime.reason === "missing"
|
|
978
1058
|
? `fix: Run 'ouro vault config set --agent ${command.agent} --key <field>' to store runtime credentials.`
|
|
979
|
-
: `fix:
|
|
1059
|
+
: `fix: ${(0, vault_unlock_1.vaultUnlockReplaceRecoverFix)(command.agent, "Then retry 'ouro vault config status'.")}`);
|
|
980
1060
|
}
|
|
981
1061
|
const message = lines.join("\n");
|
|
982
1062
|
deps.writeStdout(message);
|
|
@@ -1243,7 +1323,7 @@ async function executeProviderRefresh(command, deps) {
|
|
|
1243
1323
|
}
|
|
1244
1324
|
else {
|
|
1245
1325
|
lines.push(`provider credential refresh failed for ${command.agent}: ${pool.error}`);
|
|
1246
|
-
lines.push(
|
|
1326
|
+
lines.push((0, vault_unlock_1.vaultUnlockReplaceRecoverFix)(command.agent, "Then retry 'ouro provider refresh'."));
|
|
1247
1327
|
const message = lines.join("\n");
|
|
1248
1328
|
deps.writeStdout(message);
|
|
1249
1329
|
return message;
|
|
@@ -2714,6 +2794,9 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
2714
2794
|
if (command.kind === "vault.create") {
|
|
2715
2795
|
return executeVaultCreate(command, deps);
|
|
2716
2796
|
}
|
|
2797
|
+
if (command.kind === "vault.replace") {
|
|
2798
|
+
return executeVaultReplace(command, deps);
|
|
2799
|
+
}
|
|
2717
2800
|
if (command.kind === "vault.recover") {
|
|
2718
2801
|
return executeVaultRecover(command, deps);
|
|
2719
2802
|
}
|
|
@@ -2761,7 +2844,7 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
2761
2844
|
if (command.kind === "auth.verify") {
|
|
2762
2845
|
const poolResult = await (0, provider_credentials_1.refreshProviderCredentialPool)(command.agent);
|
|
2763
2846
|
if (!poolResult.ok) {
|
|
2764
|
-
const message = `vault unavailable: ${poolResult.error}\
|
|
2847
|
+
const message = `vault unavailable: ${poolResult.error}\n${(0, vault_unlock_1.vaultUnlockReplaceRecoverFix)(command.agent, "Then retry 'ouro auth verify'.")}`;
|
|
2765
2848
|
deps.writeStdout(message);
|
|
2766
2849
|
return message;
|
|
2767
2850
|
}
|
|
@@ -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,6 +269,11 @@ 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 replacement agent vault 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
278
|
description: "Create a replacement agent vault and import local JSON credential exports",
|
|
274
279
|
usage: "ouro vault recover --agent <name> --from <json> [--from <json>] [--email <email>] [--server <url>] [--store <store>]",
|
|
@@ -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 a replacement 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
|
|
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) {
|