@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 +20 -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 +116 -27
- package/dist/heart/daemon/cli-help.js +9 -4
- 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,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}\
|
|
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
|
|
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
|
-
|
|
695
|
-
|
|
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 ??
|
|
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
|
|
867
|
+
const unlockSecret = (await promptSecret(`Choose Ouro vault unlock secret for ${email}: `)).trim();
|
|
790
868
|
if (!unlockSecret) {
|
|
791
|
-
throw new Error("vault recover requires
|
|
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
|
-
|
|
804
|
-
|
|
871
|
+
const repair = await createRepairVaultForAgent({
|
|
872
|
+
action: "recover",
|
|
805
873
|
agentName: command.agent,
|
|
806
874
|
email,
|
|
807
875
|
serverUrl,
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
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
|
|
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
|
-
|
|
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:
|
|
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(
|
|
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}\
|
|
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
|
|
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
|
|
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) {
|