@ouro.bot/cli 0.1.0-alpha.404 → 0.1.0-alpha.406
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,23 @@
|
|
|
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.406",
|
|
6
|
+
"changes": [
|
|
7
|
+
"`ouro auth --agent <agent> --provider <provider>` now keeps narrating the post-login vault save path with `opening ... vault session`, `storing ... credentials`, and `refreshing local provider snapshot`, so a successful browser login no longer drops into a silent cursor while secrets are being persisted.",
|
|
8
|
+
"Bitwarden-backed provider saves now classify timeouts and empty command failures by operation and redact raw `bw create item ...` command text, encoded payloads, and prompt echoes from auth output.",
|
|
9
|
+
"Auth, repair CLI, and Bitwarden regression coverage now encodes the reported post-login save failure shapes so the same leak-prone path stays guarded.",
|
|
10
|
+
"`@ouro.bot/cli` and the `ouro.bot` wrapper are version-synced for the post-login vault save hardening release."
|
|
11
|
+
]
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"version": "0.1.0-alpha.405",
|
|
15
|
+
"changes": [
|
|
16
|
+
"`ouro up` now keeps walking the guided repair path when one successful fix reveals the next runnable issue for the same agent, instead of stopping after the first pass and making you rerun the command.",
|
|
17
|
+
"The happy repair path still prints `provider checks recovered after repair`, and the chained-flow coverage now exercises vault-unlock-then-auth continuation directly.",
|
|
18
|
+
"`@ouro.bot/cli` and the `ouro.bot` wrapper are version-synced for the chained preflight repair release."
|
|
19
|
+
]
|
|
20
|
+
},
|
|
4
21
|
{
|
|
5
22
|
"version": "0.1.0-alpha.404",
|
|
6
23
|
"changes": [
|
|
@@ -148,6 +148,7 @@ async function storeProviderCredentials(agentName, provider, credentials, deps =
|
|
|
148
148
|
config: split.config,
|
|
149
149
|
provenance: { source: "auth-flow" },
|
|
150
150
|
now: deps.now,
|
|
151
|
+
onProgress: deps.onProgress,
|
|
151
152
|
});
|
|
152
153
|
return { credentialPath: (0, provider_credentials_1.providerCredentialItemName)(provider) };
|
|
153
154
|
}
|
|
@@ -391,11 +392,14 @@ async function runRuntimeAuthFlow(input, deps = {}) {
|
|
|
391
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
393
|
}
|
|
393
394
|
const credentials = await collectRuntimeAuthCredentials(input, deps);
|
|
394
|
-
writeAuthProgress(input, `${input.provider} credentials collected; storing in ${input.agentName}'s vault...`);
|
|
395
395
|
let credentialPath;
|
|
396
396
|
try {
|
|
397
397
|
;
|
|
398
|
-
({
|
|
398
|
+
({
|
|
399
|
+
credentialPath,
|
|
400
|
+
} = await storeProviderCredentials(input.agentName, input.provider, credentials, {
|
|
401
|
+
onProgress: (message) => writeAuthProgress(input, message),
|
|
402
|
+
}));
|
|
399
403
|
}
|
|
400
404
|
catch (error) {
|
|
401
405
|
throw formatVaultStoreError(input.agentName, input.provider, error);
|
|
@@ -1459,13 +1459,65 @@ function readinessReportsFromDegraded(degraded) {
|
|
|
1459
1459
|
issues: [readinessIssueFromDegraded(entry)],
|
|
1460
1460
|
}));
|
|
1461
1461
|
}
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1462
|
+
function degradedEntrySignature(entry) {
|
|
1463
|
+
const issue = readinessIssueFromDegraded(entry);
|
|
1464
|
+
return JSON.stringify({
|
|
1465
|
+
agent: entry.agent,
|
|
1466
|
+
kind: issue.kind,
|
|
1467
|
+
summary: issue.summary,
|
|
1468
|
+
detail: issue.detail ?? "",
|
|
1469
|
+
actions: issue.actions.map((action) => `${action.kind}:${action.command}:${action.executable === false ? "manual" : "run"}`),
|
|
1467
1470
|
});
|
|
1468
1471
|
}
|
|
1472
|
+
function mergeRemainingDegraded(untouched, rechecked) {
|
|
1473
|
+
const byAgent = new Map();
|
|
1474
|
+
for (const entry of untouched)
|
|
1475
|
+
byAgent.set(entry.agent, entry);
|
|
1476
|
+
for (const entry of rechecked)
|
|
1477
|
+
byAgent.set(entry.agent, entry);
|
|
1478
|
+
return [...byAgent.values()];
|
|
1479
|
+
}
|
|
1480
|
+
async function runReadinessRepairForDegraded(degraded, deps) {
|
|
1481
|
+
let current = degraded;
|
|
1482
|
+
let repairsAttempted = false;
|
|
1483
|
+
for (let pass = 0; pass < 3; pass += 1) {
|
|
1484
|
+
const attemptedAgents = new Set();
|
|
1485
|
+
const result = await (0, readiness_repair_1.runGuidedReadinessRepair)(readinessReportsFromDegraded(current), {
|
|
1486
|
+
promptInput: deps.promptInput,
|
|
1487
|
+
writeStdout: deps.writeStdout,
|
|
1488
|
+
onActionAttempted: (agentName) => {
|
|
1489
|
+
attemptedAgents.add(agentName);
|
|
1490
|
+
},
|
|
1491
|
+
runRepairAction: async (agentName, action) => executeReadinessRepairAction(agentName, action, deps),
|
|
1492
|
+
});
|
|
1493
|
+
if (!result.repairsAttempted) {
|
|
1494
|
+
return { repairsAttempted, remainingDegraded: current };
|
|
1495
|
+
}
|
|
1496
|
+
repairsAttempted = true;
|
|
1497
|
+
/* v8 ignore next -- onActionAttempted runs before any runnable repair action, so repairsAttempted implies at least one attempted agent @preserve */
|
|
1498
|
+
if (attemptedAgents.size === 0) {
|
|
1499
|
+
return { repairsAttempted, remainingDegraded: current };
|
|
1500
|
+
}
|
|
1501
|
+
const previousByAgent = new Map();
|
|
1502
|
+
for (const entry of current) {
|
|
1503
|
+
if (attemptedAgents.has(entry.agent)) {
|
|
1504
|
+
previousByAgent.set(entry.agent, degradedEntrySignature(entry));
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
const untouched = current.filter((entry) => !attemptedAgents.has(entry.agent));
|
|
1508
|
+
const rechecked = await checkAgentProviders(deps, [...attemptedAgents]);
|
|
1509
|
+
const remaining = mergeRemainingDegraded(untouched, rechecked);
|
|
1510
|
+
if (remaining.length === 0) {
|
|
1511
|
+
return { repairsAttempted, remainingDegraded: remaining };
|
|
1512
|
+
}
|
|
1513
|
+
const nextPrompt = rechecked.filter((entry) => previousByAgent.get(entry.agent) !== degradedEntrySignature(entry));
|
|
1514
|
+
if (nextPrompt.length === 0) {
|
|
1515
|
+
return { repairsAttempted, remainingDegraded: remaining };
|
|
1516
|
+
}
|
|
1517
|
+
current = nextPrompt;
|
|
1518
|
+
}
|
|
1519
|
+
return { repairsAttempted, remainingDegraded: current };
|
|
1520
|
+
}
|
|
1469
1521
|
async function executeLegacyAuthSwitch(command, deps) {
|
|
1470
1522
|
const { state } = readOrBootstrapProviderState(command.agent, deps);
|
|
1471
1523
|
const lanes = command.facing
|
|
@@ -2246,17 +2298,19 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
2246
2298
|
}
|
|
2247
2299
|
const repairResult = await runReadinessRepairForDegraded(preflightProviderDegraded, deps);
|
|
2248
2300
|
if (!repairResult.repairsAttempted) {
|
|
2249
|
-
writeProviderRepairSummary(deps, "Provider checks still need repair:",
|
|
2301
|
+
writeProviderRepairSummary(deps, "Provider checks still need repair:", repairResult.remainingDegraded);
|
|
2250
2302
|
const message = "daemon not started: provider checks need repair. Run `ouro repair` or rerun `ouro up` to choose a repair path.";
|
|
2251
2303
|
deps.writeStdout(message);
|
|
2252
2304
|
return message;
|
|
2253
2305
|
}
|
|
2254
|
-
const remainingDegraded =
|
|
2306
|
+
const remainingDegraded = repairResult.remainingDegraded;
|
|
2255
2307
|
if (remainingDegraded.length > 0) {
|
|
2308
|
+
writeProviderRepairSummary(deps, "Still blocked:", remainingDegraded);
|
|
2256
2309
|
const message = "daemon not started: provider checks still need repair.";
|
|
2257
2310
|
deps.writeStdout(message);
|
|
2258
2311
|
return message;
|
|
2259
2312
|
}
|
|
2313
|
+
deps.writeStdout("provider checks recovered after repair");
|
|
2260
2314
|
}
|
|
2261
2315
|
}
|
|
2262
2316
|
progress.startPhase("starting daemon");
|
|
@@ -187,6 +187,7 @@ async function runGuidedReadinessRepair(reports, deps) {
|
|
|
187
187
|
continue;
|
|
188
188
|
}
|
|
189
189
|
try {
|
|
190
|
+
deps.onActionAttempted?.(report.agent, action, issue);
|
|
190
191
|
await deps.runRepairAction(report.agent, action, issue);
|
|
191
192
|
repairsAttempted = true;
|
|
192
193
|
deps.writeStdout(`repair step finished for ${report.agent}.`);
|
|
@@ -328,12 +328,15 @@ async function upsertProviderCredential(input) {
|
|
|
328
328
|
},
|
|
329
329
|
};
|
|
330
330
|
const record = recordFromPayload(payload);
|
|
331
|
+
input.onProgress?.(`opening ${input.agentName}'s vault session...`);
|
|
331
332
|
const store = (0, credential_access_1.getCredentialStore)(input.agentName);
|
|
333
|
+
input.onProgress?.(`storing ${input.provider} credentials in ${input.agentName}'s vault...`);
|
|
332
334
|
await store.store(providerCredentialItemName(input.provider), {
|
|
333
335
|
username: input.provider,
|
|
334
336
|
password: JSON.stringify(payload),
|
|
335
337
|
notes: "Ouro provider credentials. The vault item password is a versioned JSON payload.",
|
|
336
338
|
});
|
|
339
|
+
input.onProgress?.(`refreshing local provider snapshot from ${input.agentName}'s vault...`);
|
|
337
340
|
const refreshResult = await refreshProviderCredentialPool(input.agentName);
|
|
338
341
|
if (!refreshResult.ok) {
|
|
339
342
|
throw new Error(`credential stored in vault, but the local provider snapshot could not be refreshed: ${refreshResult.error}. ` +
|
|
@@ -59,6 +59,21 @@ function isBwSessionUnavailableMessage(message) {
|
|
|
59
59
|
function isBwInvalidUnlockSecretMessage(message) {
|
|
60
60
|
return /invalid master password/i.test(message) || /saved vault unlock secret/i.test(message);
|
|
61
61
|
}
|
|
62
|
+
function isBwTimeoutError(err) {
|
|
63
|
+
const timeoutErr = err;
|
|
64
|
+
const message = err.message.toLowerCase();
|
|
65
|
+
return (timeoutErr.code === "ETIMEDOUT" ||
|
|
66
|
+
timeoutErr.killed === true ||
|
|
67
|
+
timeoutErr.signal === "SIGTERM" ||
|
|
68
|
+
message.includes("timed out"));
|
|
69
|
+
}
|
|
70
|
+
function formatBwOperation(args) {
|
|
71
|
+
const [command, target] = args;
|
|
72
|
+
/* v8 ignore next -- defensive: all execBw call sites pass a concrete bw subcommand @preserve */
|
|
73
|
+
if (!command)
|
|
74
|
+
return "bw command";
|
|
75
|
+
return [command, target].filter(Boolean).join(" ");
|
|
76
|
+
}
|
|
62
77
|
function sanitizeBwErrorDetail(message) {
|
|
63
78
|
if (isBwInvalidUnlockSecretMessage(message)) {
|
|
64
79
|
return "bw CLI rejected the saved vault unlock secret for this machine";
|
|
@@ -75,8 +90,15 @@ function sanitizeBwErrorDetail(message) {
|
|
|
75
90
|
.replace(/[A-Za-z0-9+/=]{80,}/g, "[redacted]")
|
|
76
91
|
.slice(0, 500);
|
|
77
92
|
}
|
|
78
|
-
function formatBwCliError(err, stderr = "") {
|
|
93
|
+
function formatBwCliError(err, stderr = "", args = []) {
|
|
94
|
+
const operation = formatBwOperation(args);
|
|
95
|
+
if (isBwTimeoutError(err)) {
|
|
96
|
+
return new Error(`bw CLI error: ${operation} timed out while waiting for a vault response`);
|
|
97
|
+
}
|
|
79
98
|
const detail = sanitizeBwErrorDetail(stderr.trim() || err.message);
|
|
99
|
+
if (detail === "command failed") {
|
|
100
|
+
return new Error(`bw CLI error: ${operation} failed without error detail`);
|
|
101
|
+
}
|
|
80
102
|
return new Error(`bw CLI error: ${detail}`);
|
|
81
103
|
}
|
|
82
104
|
function isBwSessionAuthError(err) {
|
|
@@ -99,7 +121,7 @@ function execBw(args, sessionToken, appDataDir, stdin) {
|
|
|
99
121
|
reject(new Error("bw CLI not found. Install from https://bitwarden.com/help/cli/"));
|
|
100
122
|
return;
|
|
101
123
|
}
|
|
102
|
-
reject(formatBwCliError(err, stderr));
|
|
124
|
+
reject(formatBwCliError(err, stderr, args));
|
|
103
125
|
return;
|
|
104
126
|
}
|
|
105
127
|
resolve(stdout);
|