@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 execBw(args, sessionToken, appDataDir) {
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(new Error(`bw CLI error: ${err.message}`));
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, encoded], session, this.appDataDir);
298
+ await execBw(["edit", "item", existing.id], session, this.appDataDir, encoded);
279
299
  }
280
300
  else {
281
- await execBw(["create", "item", encoded], session, this.appDataDir);
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",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.397",
3
+ "version": "0.1.0-alpha.399",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",