@ouro.bot/cli 0.1.0-alpha.397 → 0.1.0-alpha.399
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,25 @@
|
|
|
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.399",
|
|
6
|
+
"changes": [
|
|
7
|
+
"Bitwarden-backed credential writes now pipe provider vault item payloads to `bw create item` and `bw edit item` over stdin instead of putting encoded credential JSON in process arguments.",
|
|
8
|
+
"Bitwarden CLI failures are now sanitized before they reach CLI output, so `Command failed: bw ...` argv, encoded payloads, raw provider tokens, and vault item passwords are not printed.",
|
|
9
|
+
"Bitwarden master-password prompts during credential writes now surface as a short locked/expired local session message instead of dumping the failed command invocation.",
|
|
10
|
+
"The nerves redaction audit now looks for credential-shaped text instead of failing on ordinary file paths or branch names that contain words like `secret`, `password`, or `api-key`.",
|
|
11
|
+
"`@ouro.bot/cli` and the `ouro.bot` wrapper are version-synced for the Bitwarden secret redaction release."
|
|
12
|
+
]
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"version": "0.1.0-alpha.398",
|
|
16
|
+
"changes": [
|
|
17
|
+
"`ouro auth --agent <agent> --provider <provider>` now prints safe progress breadcrumbs while it checks vault access, runs provider login, stores credentials in the agent vault, refreshes the local provider snapshot, and verifies the provider.",
|
|
18
|
+
"Provider auth launched from `ouro repair` and hatch bootstrap now uses the same auth progress hook, so browser/login flows no longer leave humans staring at a silent cursor after the provider says login succeeded.",
|
|
19
|
+
"Auth progress messages are phase labels only; they never include OAuth tokens, API keys, vault unlock secrets, or credential payload values.",
|
|
20
|
+
"`@ouro.bot/cli` and the `ouro.bot` wrapper are version-synced for the auth progress breadcrumb release."
|
|
21
|
+
]
|
|
22
|
+
},
|
|
4
23
|
{
|
|
5
24
|
"version": "0.1.0-alpha.397",
|
|
6
25
|
"changes": [
|
|
@@ -187,6 +187,9 @@ function ensurePromptInput(promptInput, provider) {
|
|
|
187
187
|
return promptInput;
|
|
188
188
|
throw new Error(`No prompt input is available for ${provider} authentication.`);
|
|
189
189
|
}
|
|
190
|
+
function writeAuthProgress(input, message) {
|
|
191
|
+
input.onProgress?.(message);
|
|
192
|
+
}
|
|
190
193
|
function validateAnthropicToken(token) {
|
|
191
194
|
const trimmed = token.trim();
|
|
192
195
|
if (!trimmed) {
|
|
@@ -206,10 +209,12 @@ async function collectRuntimeAuthCredentials(input, deps) {
|
|
|
206
209
|
if (input.provider === "github-copilot") {
|
|
207
210
|
let token = process.env.GH_TOKEN?.trim() || process.env.GITHUB_TOKEN?.trim() || "";
|
|
208
211
|
if (!token) {
|
|
212
|
+
writeAuthProgress(input, "checking GitHub CLI credentials...");
|
|
209
213
|
const result = spawnSync("gh", ["auth", "token"], { encoding: "utf8" });
|
|
210
214
|
token = (result.status === 0 && result.stdout ? result.stdout.trim() : "");
|
|
211
215
|
}
|
|
212
216
|
if (!token) {
|
|
217
|
+
writeAuthProgress(input, "starting GitHub login...");
|
|
213
218
|
(0, runtime_1.emitNervesEvent)({
|
|
214
219
|
component: "daemon",
|
|
215
220
|
event: "daemon.auth_gh_login_start",
|
|
@@ -228,6 +233,7 @@ async function collectRuntimeAuthCredentials(input, deps) {
|
|
|
228
233
|
throw new Error("gh auth login completed but no token was found. Run `gh auth login` and try again.");
|
|
229
234
|
}
|
|
230
235
|
}
|
|
236
|
+
writeAuthProgress(input, "checking GitHub Copilot access...");
|
|
231
237
|
const response = await fetch("https://api.github.com/copilot_internal/user", {
|
|
232
238
|
headers: { Authorization: `Bearer ${token}` },
|
|
233
239
|
});
|
|
@@ -252,6 +258,7 @@ async function collectRuntimeAuthCredentials(input, deps) {
|
|
|
252
258
|
message: "starting codex login for runtime auth",
|
|
253
259
|
meta: { agentName: input.agentName },
|
|
254
260
|
});
|
|
261
|
+
writeAuthProgress(input, "starting openai-codex browser login...");
|
|
255
262
|
const result = spawnSync("codex", ["login"], { stdio: "inherit" });
|
|
256
263
|
if (result.error) {
|
|
257
264
|
throw new Error(`Failed to run 'codex login': ${result.error.message}`);
|
|
@@ -259,6 +266,7 @@ async function collectRuntimeAuthCredentials(input, deps) {
|
|
|
259
266
|
if (result.status !== 0) {
|
|
260
267
|
throw new Error(`'codex login' exited with status ${result.status}.`);
|
|
261
268
|
}
|
|
269
|
+
writeAuthProgress(input, "openai-codex login complete; reading local Codex token...");
|
|
262
270
|
const token = readCodexAccessToken(homeDir);
|
|
263
271
|
if (!token) {
|
|
264
272
|
throw new Error("Codex login completed but no token was found in ~/.codex/auth.json. Re-run `codex login` and try again.");
|
|
@@ -272,6 +280,7 @@ async function collectRuntimeAuthCredentials(input, deps) {
|
|
|
272
280
|
message: "starting claude setup-token for runtime auth",
|
|
273
281
|
meta: { agentName: input.agentName },
|
|
274
282
|
});
|
|
283
|
+
writeAuthProgress(input, "starting anthropic setup-token flow...");
|
|
275
284
|
const result = spawnSync("claude", ["setup-token"], { stdio: "inherit" });
|
|
276
285
|
if (result.error) {
|
|
277
286
|
throw new Error(`Failed to run 'claude setup-token': ${result.error.message}`);
|
|
@@ -287,6 +296,7 @@ async function collectRuntimeAuthCredentials(input, deps) {
|
|
|
287
296
|
/* v8 ignore start -- token exchange: requires live Anthropic OAuth endpoint @preserve */
|
|
288
297
|
try {
|
|
289
298
|
const { refreshAnthropicToken } = await Promise.resolve().then(() => __importStar(require("../providers/anthropic-token")));
|
|
299
|
+
writeAuthProgress(input, "exchanging anthropic setup token...");
|
|
290
300
|
const tokenState = await refreshAnthropicToken(setupToken);
|
|
291
301
|
if (tokenState) {
|
|
292
302
|
return {
|
|
@@ -329,6 +339,7 @@ async function resolveHatchCredentials(input) {
|
|
|
329
339
|
agentName: input.agentName,
|
|
330
340
|
provider: input.provider,
|
|
331
341
|
promptInput: input.promptInput,
|
|
342
|
+
onProgress: input.onProgress,
|
|
332
343
|
});
|
|
333
344
|
Object.assign(credentials, result.credentials);
|
|
334
345
|
/* v8 ignore next 3 -- branch: auth flow always fills all required fields in production @preserve */
|
|
@@ -357,12 +368,15 @@ async function runRuntimeAuthFlow(input, deps = {}) {
|
|
|
357
368
|
message: "starting runtime auth flow",
|
|
358
369
|
meta: { agentName: input.agentName, provider: input.provider },
|
|
359
370
|
});
|
|
371
|
+
writeAuthProgress(input, `checking ${input.agentName}'s vault access...`);
|
|
360
372
|
const vault = await (0, provider_credentials_1.refreshProviderCredentialPool)(input.agentName);
|
|
361
373
|
if (!vault.ok && vault.reason === "unavailable") {
|
|
362
374
|
throw new Error(`${vault.error}\n${(0, vault_unlock_1.vaultUnlockReplaceRecoverFix)(input.agentName, `Then retry 'ouro auth --agent ${input.agentName} --provider ${input.provider}'.`)}`);
|
|
363
375
|
}
|
|
364
376
|
const credentials = await collectRuntimeAuthCredentials(input, deps);
|
|
377
|
+
writeAuthProgress(input, `${input.provider} credentials collected; storing in ${input.agentName}'s vault...`);
|
|
365
378
|
const { credentialPath } = await storeProviderCredentials(input.agentName, input.provider, credentials);
|
|
379
|
+
writeAuthProgress(input, `credentials stored at ${credentialPath}; local provider snapshot refreshed.`);
|
|
366
380
|
(0, runtime_1.emitNervesEvent)({
|
|
367
381
|
component: "daemon",
|
|
368
382
|
event: "daemon.auth_flow_end",
|
|
@@ -595,6 +595,7 @@ async function resolveHatchInput(command, deps) {
|
|
|
595
595
|
credentials: command.credentials,
|
|
596
596
|
promptInput: prompt,
|
|
597
597
|
runAuthFlow: deps.runAuthFlow,
|
|
598
|
+
onProgress: deps.writeStdout,
|
|
598
599
|
});
|
|
599
600
|
return {
|
|
600
601
|
agentName,
|
|
@@ -1414,6 +1415,7 @@ async function executeReadinessRepairAction(agent, action, deps) {
|
|
|
1414
1415
|
agentName: agent,
|
|
1415
1416
|
provider,
|
|
1416
1417
|
promptInput: deps.promptInput,
|
|
1418
|
+
onProgress: deps.writeStdout,
|
|
1417
1419
|
});
|
|
1418
1420
|
deps.writeStdout(result.message);
|
|
1419
1421
|
await executeProviderRefresh({ kind: "provider.refresh", agent }, deps);
|
|
@@ -2327,7 +2329,7 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
2327
2329
|
const provider = providerOverride ?? config.humanFacing.provider;
|
|
2328
2330
|
/* v8 ignore next -- tests always inject runAuthFlow; default is for production @preserve */
|
|
2329
2331
|
const authRunner = deps.runAuthFlow ?? (await Promise.resolve().then(() => __importStar(require("../auth/auth-flow")))).runRuntimeAuthFlow;
|
|
2330
|
-
await authRunner({ agentName: agent, provider, promptInput: deps.promptInput });
|
|
2332
|
+
await authRunner({ agentName: agent, provider, promptInput: deps.promptInput, onProgress: deps.writeStdout });
|
|
2331
2333
|
},
|
|
2332
2334
|
runVaultUnlock: async (agent) => {
|
|
2333
2335
|
await executeVaultUnlock({ kind: "vault.unlock", agent }, deps);
|
|
@@ -2963,6 +2965,7 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
2963
2965
|
agentName: command.agent,
|
|
2964
2966
|
provider,
|
|
2965
2967
|
promptInput: deps.promptInput,
|
|
2968
|
+
onProgress: deps.writeStdout,
|
|
2966
2969
|
});
|
|
2967
2970
|
// Behavior: ouro auth stores credentials only — does NOT switch provider.
|
|
2968
2971
|
// Use `ouro auth switch` to change the active provider.
|
|
@@ -2970,6 +2973,7 @@ async function runOuroCli(args, deps = (0, cli_defaults_1.createDefaultOuroCliDe
|
|
|
2970
2973
|
// Verify the credentials actually work by pinging the provider
|
|
2971
2974
|
/* v8 ignore start -- integration: real API ping after auth @preserve */
|
|
2972
2975
|
try {
|
|
2976
|
+
deps.writeStdout(`verifying ${provider} credentials...`);
|
|
2973
2977
|
const credential = await readProviderCredentialRecord(command.agent, provider, deps);
|
|
2974
2978
|
const status = credential.ok
|
|
2975
2979
|
? await verifyProviderCredentials(provider, {
|
|
@@ -12,11 +12,11 @@ exports.REQUIRED_ENVELOPE_FIELDS = [
|
|
|
12
12
|
"meta",
|
|
13
13
|
];
|
|
14
14
|
exports.SENSITIVE_PATTERNS = [
|
|
15
|
-
/\btoken\s*[:=]/i,
|
|
16
|
-
/\bapi[_-]?key\b/i,
|
|
17
|
-
/\bpassword\b/i,
|
|
18
|
-
/\bsecret\b/i,
|
|
19
|
-
/\bauthorization\b/i,
|
|
15
|
+
/\btoken\b["']?\s*[:=]/i,
|
|
16
|
+
/\bapi[_-]?key\b["']?\s*[:=]/i,
|
|
17
|
+
/\bpassword\b["']?\s*[:=]/i,
|
|
18
|
+
/\bsecret\b["']?\s*[:=]/i,
|
|
19
|
+
/\bauthorization\b["']?\s*[:=]/i,
|
|
20
20
|
];
|
|
21
21
|
function eventKey(component, event) {
|
|
22
22
|
return `${component}:${event}`;
|
|
@@ -49,24 +49,44 @@ const bw_installer_1 = require("./bw-installer");
|
|
|
49
49
|
// ---------------------------------------------------------------------------
|
|
50
50
|
// bw CLI wrapper
|
|
51
51
|
// ---------------------------------------------------------------------------
|
|
52
|
-
function
|
|
52
|
+
function sanitizeBwErrorDetail(message) {
|
|
53
|
+
if (/master password/i.test(message)) {
|
|
54
|
+
return "bw CLI requested a master password; the local Bitwarden session is locked or expired";
|
|
55
|
+
}
|
|
56
|
+
const withoutCommandLine = message
|
|
57
|
+
.split(/\r?\n/)
|
|
58
|
+
.filter((line) => !line.trim().startsWith("Command failed:"))
|
|
59
|
+
.join("\n")
|
|
60
|
+
.trim();
|
|
61
|
+
return (withoutCommandLine || "command failed")
|
|
62
|
+
.replace(/[A-Za-z0-9+/=]{80,}/g, "[redacted]")
|
|
63
|
+
.slice(0, 500);
|
|
64
|
+
}
|
|
65
|
+
function formatBwCliError(err, stderr = "") {
|
|
66
|
+
const detail = sanitizeBwErrorDetail(stderr.trim() || err.message);
|
|
67
|
+
return new Error(`bw CLI error: ${detail}`);
|
|
68
|
+
}
|
|
69
|
+
function execBw(args, sessionToken, appDataDir, stdin) {
|
|
53
70
|
const env = {
|
|
54
71
|
...process.env,
|
|
55
72
|
...(sessionToken ? { BW_SESSION: sessionToken } : {}),
|
|
56
73
|
...(appDataDir ? { BITWARDENCLI_APPDATA_DIR: appDataDir } : {}),
|
|
57
74
|
};
|
|
58
75
|
return new Promise((resolve, reject) => {
|
|
59
|
-
(0, node_child_process_1.execFile)("bw", args, { timeout: 30_000, env }, (err, stdout) => {
|
|
76
|
+
const child = (0, node_child_process_1.execFile)("bw", args, { timeout: 30_000, env }, (err, stdout, stderr) => {
|
|
60
77
|
if (err) {
|
|
61
78
|
if (isBwNotInstalled(err)) {
|
|
62
79
|
reject(new Error("bw CLI not found. Install from https://bitwarden.com/help/cli/"));
|
|
63
80
|
return;
|
|
64
81
|
}
|
|
65
|
-
reject(
|
|
82
|
+
reject(formatBwCliError(err, stderr));
|
|
66
83
|
return;
|
|
67
84
|
}
|
|
68
85
|
resolve(stdout);
|
|
69
86
|
});
|
|
87
|
+
if (stdin !== undefined) {
|
|
88
|
+
child?.stdin?.end(stdin);
|
|
89
|
+
}
|
|
70
90
|
});
|
|
71
91
|
}
|
|
72
92
|
/** Check if the error indicates the bw CLI binary is not installed. */
|
|
@@ -275,10 +295,10 @@ class BitwardenCredentialStore {
|
|
|
275
295
|
};
|
|
276
296
|
const encoded = Buffer.from(JSON.stringify(item)).toString("base64");
|
|
277
297
|
if (existing) {
|
|
278
|
-
await execBw(["edit", "item", existing.id
|
|
298
|
+
await execBw(["edit", "item", existing.id], session, this.appDataDir, encoded);
|
|
279
299
|
}
|
|
280
300
|
else {
|
|
281
|
-
await execBw(["create", "item"
|
|
301
|
+
await execBw(["create", "item"], session, this.appDataDir, encoded);
|
|
282
302
|
}
|
|
283
303
|
(0, runtime_1.emitNervesEvent)({
|
|
284
304
|
event: "repertoire.bw_credential_store_end",
|