@ouro.bot/cli 0.1.0-alpha.416 → 0.1.0-alpha.417

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,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.417",
6
+ "changes": [
7
+ "`isTransientError` now recognizes timeout errors after `formatBwCliError` transforms them, so retries fire correctly even when the raw `ETIMEDOUT` code is lost during formatting.",
8
+ "New `withTransientRetry` wrapper on read operations (`get`, `getRawSecret`, `list`) retries up to 3 times with exponential backoff (500ms, 1s, 2s) and emits `repertoire.bw_transient_retry` nerves events, so transient vault hiccups resolve silently.",
9
+ "`bw sync` now runs after every login/unlock in `loginAttempt()`, ensuring vault data is synchronized with the server before any read operations.",
10
+ "Timeout error messages changed from a generic 'timed out while waiting for a vault response' to actionable guidance: 'timed out -- usually resolves on retry. If it persists, check network connectivity to the vault server.'",
11
+ "`@ouro.bot/cli` and the `ouro.bot` wrapper are version-synced for the Bitwarden store resilience release."
12
+ ]
13
+ },
4
14
  {
5
15
  "version": "0.1.0-alpha.416",
6
16
  "changes": [
@@ -116,7 +116,7 @@ function sanitizeBwErrorDetail(message) {
116
116
  function formatBwCliError(err, stderr = "", args = []) {
117
117
  const operation = formatBwOperation(args);
118
118
  if (isBwTimeoutError(err)) {
119
- return new Error(`bw CLI error: ${operation} timed out while waiting for a vault response`);
119
+ return new Error(`bw CLI error: ${operation} timed out -- usually resolves on retry. If it persists, check network connectivity to the vault server.`);
120
120
  }
121
121
  const detail = sanitizeBwErrorDetail(stderr.trim() || err.message);
122
122
  if (detail === "command failed") {
@@ -274,10 +274,13 @@ function isTransientError(err) {
274
274
  msg.includes("enotfound") ||
275
275
  msg.includes("socket hang up") ||
276
276
  msg.includes("503") ||
277
- msg.includes("server unavailable"));
277
+ msg.includes("server unavailable") ||
278
+ msg.includes("timed out"));
278
279
  }
279
280
  const MAX_RETRIES = 3;
280
281
  const BASE_BACKOFF_MS = 1000;
282
+ const TRANSIENT_MAX_RETRIES = 3;
283
+ const TRANSIENT_RETRY_BASE_MS = 500;
281
284
  function delay(ms) {
282
285
  return new Promise((resolve) => setTimeout(resolve, ms));
283
286
  }
@@ -469,6 +472,9 @@ class BitwardenCredentialStore {
469
472
  const unlockOutput = await execBw(["unlock", this.masterPassword, "--raw"], undefined, this.appDataDir);
470
473
  this.sessionToken = unlockOutput.trim();
471
474
  }
475
+ // Sync vault data after obtaining a fresh session token
476
+ /* v8 ignore next -- defensive: loginAttempt always sets sessionToken before sync @preserve */
477
+ await execBw(["sync"], this.sessionToken ?? undefined, this.appDataDir);
472
478
  }
473
479
  async ensureSession() {
474
480
  if (!this.sessionToken) {
@@ -494,6 +500,32 @@ class BitwardenCredentialStore {
494
500
  }
495
501
  }
496
502
  }
503
+ async withTransientRetry(operation) {
504
+ let lastError;
505
+ for (let attempt = 0; attempt < TRANSIENT_MAX_RETRIES; attempt++) {
506
+ try {
507
+ return await operation();
508
+ }
509
+ catch (err) {
510
+ /* v8 ignore next -- defensive: operation always throws Error instances @preserve */
511
+ lastError = err instanceof Error ? err : new Error(String(err));
512
+ if (!isTransientError(lastError)) {
513
+ throw lastError;
514
+ }
515
+ if (attempt === TRANSIENT_MAX_RETRIES - 1)
516
+ break;
517
+ const backoffMs = TRANSIENT_RETRY_BASE_MS * Math.pow(2, attempt);
518
+ (0, runtime_1.emitNervesEvent)({
519
+ event: "repertoire.bw_transient_retry",
520
+ component: "repertoire",
521
+ message: `transient bw error, retrying in ${backoffMs}ms`,
522
+ meta: { attempt: attempt + 1, backoffMs, reason: lastError.message },
523
+ });
524
+ await delay(backoffMs);
525
+ }
526
+ }
527
+ throw lastError;
528
+ }
497
529
  async get(domain) {
498
530
  (0, runtime_1.emitNervesEvent)({
499
531
  event: "repertoire.bw_credential_get_start",
@@ -501,7 +533,7 @@ class BitwardenCredentialStore {
501
533
  message: `getting credential via bw for ${domain}`,
502
534
  meta: { domain, backend: "bitwarden" },
503
535
  });
504
- const item = await this.withSessionRetry((session) => this.findItemByDomain(domain, session));
536
+ const item = await this.withTransientRetry(() => this.withSessionRetry((session) => this.findItemByDomain(domain, session)));
505
537
  if (!item) {
506
538
  (0, runtime_1.emitNervesEvent)({
507
539
  event: "repertoire.bw_credential_get_end",
@@ -525,7 +557,7 @@ class BitwardenCredentialStore {
525
557
  };
526
558
  }
527
559
  async getRawSecret(domain, field) {
528
- const item = await this.withSessionRetry((session) => this.findItemByDomain(domain, session));
560
+ const item = await this.withTransientRetry(() => this.withSessionRetry((session) => this.findItemByDomain(domain, session)));
529
561
  if (!item) {
530
562
  throw new Error(`no credential found for domain "${domain}"`);
531
563
  }
@@ -595,7 +627,7 @@ class BitwardenCredentialStore {
595
627
  message: "listing bw credentials",
596
628
  meta: { backend: "bitwarden" },
597
629
  });
598
- const stdout = await this.withSessionRetry((session) => execBw(["list", "items"], session, this.appDataDir));
630
+ const stdout = await this.withTransientRetry(() => this.withSessionRetry((session) => execBw(["list", "items"], session, this.appDataDir)));
599
631
  const items = parseBwItems(stdout, "bw list items");
600
632
  const results = items.map((item) => ({
601
633
  domain: item.name,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.416",
3
+ "version": "0.1.0-alpha.417",
4
4
  "main": "dist/heart/daemon/ouro-entry.js",
5
5
  "bin": {
6
6
  "cli": "dist/heart/daemon/ouro-bot-entry.js",