@ouro.bot/cli 0.1.0-alpha.907 → 0.1.0-alpha.908

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,15 @@
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.908",
6
+ "changes": [
7
+ "Vault commands use the Bitwarden CLI that ships with ouro (pinned 2026.6.0) instead of whichever bw is first on PATH. bw 2026.9 runs a user key id migration on sign-in that our Vaultwarden server answers with 404, so ouro vault unlock and create failed on machines with a newer global bw.",
8
+ "The bundled bw runs under ouro's own Node, so it works with no node on PATH and on Windows. A missing bundled bw is logged as repertoire.bw_cli_bundled_missing before ouro falls back to PATH, and the fallback npm install uses the same pinned version.",
9
+ "OURO_BW_BINARY names a specific bw to use ahead of the bundled one, for tests and unusual installs; ouro refuses it with a clear error when it is not an executable file.",
10
+ "ouro vault unlock now shows each step while it checks vault access: which bw it uses, pointing bw at the server, signing in or unlocking, syncing, and any retry with its reason."
11
+ ]
12
+ },
4
13
  {
5
14
  "version": "0.1.0-alpha.907",
6
15
  "changes": [
@@ -1,5 +1,5 @@
1
1
  {
2
- "runtimeVersion": "0.1.0-alpha.907",
2
+ "runtimeVersion": "0.1.0-alpha.908",
3
3
  "bundleSchemaVersion": 3,
4
4
  "lastUpdated": "2026-09-03T00:00:00.000Z"
5
5
  }
@@ -1,7 +1,7 @@
1
1
  <?xml version="1.0"?>
2
2
  <Container version="2">
3
3
  <Name>ouro-butler</Name>
4
- <Repository>ghcr.io/ourostack/ouroboros-butler:0.1.0-alpha.907</Repository>
4
+ <Repository>ghcr.io/ourostack/ouroboros-butler:0.1.0-alpha.908</Repository>
5
5
  <Registry>https://github.com/ourostack/ouroboros/pkgs/container/ouroboros-butler</Registry>
6
6
  <Network>host</Network>
7
7
  <Shell>sh</Shell>
@@ -773,7 +773,7 @@ function createHumanCommandProgress(deps, commandName) {
773
773
  async function runCommandProgressPhase(progress, label, run, detail) {
774
774
  progress.startPhase(label);
775
775
  try {
776
- const result = await Promise.resolve(run());
776
+ const result = await Promise.resolve(run((step) => progress.updateDetail(step)));
777
777
  progress.completePhase(label, detail(result));
778
778
  return result;
779
779
  }
@@ -1809,7 +1809,7 @@ async function executeVaultUnlock(command, deps) {
1809
1809
  const progress = createHumanCommandProgress(deps, "vault unlock");
1810
1810
  let store;
1811
1811
  try {
1812
- await runCommandProgressPhase(progress, "checking vault access", () => (0, credential_access_1.probeCredentialVaultAccess)(command.agent, unlockSecret, { homeDir: deps.homeDir }), () => "ok");
1812
+ await runCommandProgressPhase(progress, "checking vault access", (report) => (0, credential_access_1.probeCredentialVaultAccess)(command.agent, unlockSecret, { homeDir: deps.homeDir, onProgress: report }), () => "ok");
1813
1813
  store = await runCommandProgressPhase(progress, "saving local unlock", () => (0, vault_unlock_1.storeVaultUnlockSecret)({
1814
1814
  agentName: command.agent,
1815
1815
  email: vault.email,
@@ -341,7 +341,8 @@ function execBw(args, sessionToken, appDataDir, stdin, bwBinaryPath = "bw", extr
341
341
  ...extraEnv,
342
342
  };
343
343
  const runCommand = (lock) => new Promise((resolve, reject) => {
344
- const child = (0, node_child_process_1.execFile)(bwBinaryPath, args, { timeout: BW_COMMAND_TIMEOUT_MS, env }, (err, stdout, stderr) => {
344
+ const command = (0, bw_installer_1.bwCommand)(bwBinaryPath, args);
345
+ const child = (0, node_child_process_1.execFile)(command.file, command.args, { timeout: BW_COMMAND_TIMEOUT_MS, env }, (err, stdout, stderr) => {
345
346
  if (err) {
346
347
  if (isBwNotInstalled(err)) {
347
348
  reject(new Error("bw CLI not found. Install from https://bitwarden.com/help/cli/"));
@@ -562,6 +563,7 @@ class BitwardenCredentialStore {
562
563
  appDataDir;
563
564
  onInvalidUnlockSecret;
564
565
  onLoginSuccess;
566
+ onProgress;
565
567
  sessionToken = null;
566
568
  terminalLoginError = null;
567
569
  terminalLoginErrorAt = 0;
@@ -575,6 +577,7 @@ class BitwardenCredentialStore {
575
577
  this.appDataDir = options.appDataDir;
576
578
  this.onInvalidUnlockSecret = options.onInvalidUnlockSecret;
577
579
  this.onLoginSuccess = options.onLoginSuccess;
580
+ this.onProgress = options.onProgress;
578
581
  }
579
582
  isReady() {
580
583
  return true;
@@ -621,12 +624,14 @@ class BitwardenCredentialStore {
621
624
  this.terminalLoginError = null;
622
625
  }
623
626
  // Ensure bw CLI is installed before any bw commands
627
+ this.onProgress?.("finding the Bitwarden CLI");
624
628
  this.bwBinaryPath = await (0, bw_installer_1.ensureBwCli)();
629
+ const bwVersion = readBwCliVersion(this.bwBinaryPath);
630
+ this.onProgress?.(`using bw ${bwVersion ?? "(unknown version)"} at ${this.bwBinaryPath}`);
625
631
  if (this.agentDataDir) {
626
632
  // Each bw version gets its own profile directory, so machines with
627
633
  // several bw installs (one per Node version) never migrate or fight
628
634
  // over one another's state.
629
- const bwVersion = readBwCliVersion(this.bwBinaryPath);
630
635
  this.appDataDir = bwVersion ? path.join(this.agentDataDir, bwProfileDirName(bwVersion)) : this.agentDataDir;
631
636
  fs.mkdirSync(this.appDataDir, { recursive: true, mode: 0o700 });
632
637
  }
@@ -658,6 +663,7 @@ class BitwardenCredentialStore {
658
663
  message: `bw login attempt ${attempt + 1} failed, retrying in ${backoffMs}ms`,
659
664
  meta: { attempt: attempt + 1, backoffMs, reason: lastError.message },
660
665
  });
666
+ this.onProgress?.(`sign-in attempt ${attempt + 1} failed (${lastError.message.split("\n")[0].slice(0, 120)}); retrying in ${Math.round(backoffMs / 1000)}s`);
661
667
  await delay(backoffMs);
662
668
  }
663
669
  }
@@ -731,6 +737,7 @@ class BitwardenCredentialStore {
731
737
  });
732
738
  }
733
739
  async loginWithLocalProfile() {
740
+ this.onProgress?.("reading the local bw profile");
734
741
  let status = await this.readStatus();
735
742
  if (this.shouldRebuildLocalProfile(status)) {
736
743
  await this.rebuildLocalProfile("local bw profile did not match requested vault", status);
@@ -739,6 +746,7 @@ class BitwardenCredentialStore {
739
746
  // Configure server URL if needed (only works when logged out)
740
747
  if (status.status === "unauthenticated" || !status.serverUrl) {
741
748
  try {
749
+ this.onProgress?.(`pointing bw at ${this.serverUrl}`);
742
750
  await this.execBw(["config", "server", this.serverUrl]);
743
751
  }
744
752
  catch (error) {
@@ -750,6 +758,7 @@ class BitwardenCredentialStore {
750
758
  }
751
759
  }
752
760
  if (!this.sessionToken) {
761
+ this.onProgress?.(status.status === "unauthenticated" || !status.status ? `signing in to ${this.serverUrl} as ${this.email}` : `unlocking ${this.email}`);
753
762
  if (status.status === "locked") {
754
763
  // Already logged in, just needs unlock.
755
764
  const unlockOutput = await this.execWithLocalProfileRebuild("unlock rejected saved local unlock material", status, () => this.execBwWithPasswordEnv(["unlock", "--raw"]));
@@ -766,6 +775,7 @@ class BitwardenCredentialStore {
766
775
  }
767
776
  }
768
777
  if (this.shouldSyncVaultAfterSession(status)) {
778
+ this.onProgress?.("syncing vault items");
769
779
  /* v8 ignore next -- defensive: loginAttempt always sets sessionToken before sync @preserve */
770
780
  await this.execBw(["sync"], this.sessionToken ?? undefined);
771
781
  this.writeSyncMarker();
@@ -2,8 +2,10 @@
2
2
  /**
3
3
  * Lazy bw CLI installer — auto-installs the Bitwarden CLI when not present.
4
4
  *
5
- * Mirrors the whisper-cpp pattern in senses/bluebubbles/media.ts:
6
- * check PATH first, install via npm if missing, emit nerves event.
5
+ * Prefers the Bitwarden CLI that ships with ouro (the `@bitwarden/cli` version pinned in our
6
+ * package.json), because a newer `bw` on PATH can run client migrations our Vaultwarden server
7
+ * does not support: bw 2026.9 posts a user key id that Vaultwarden 1.37 answers with 404, and its
8
+ * login then fails. PATH is the fallback, then an npm install of the same pinned version.
7
9
  */
8
10
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
11
  if (k2 === undefined) k2 = k;
@@ -39,8 +41,11 @@ var __importStar = (this && this.__importStar) || (function () {
39
41
  };
40
42
  })();
41
43
  Object.defineProperty(exports, "__esModule", { value: true });
44
+ exports.PINNED_BW_CLI_VERSION = void 0;
42
45
  exports.findExecutableOnPath = findExecutableOnPath;
43
46
  exports.findExecutableViaNpmPrefix = findExecutableViaNpmPrefix;
47
+ exports.findBundledBwCli = findBundledBwCli;
48
+ exports.bwCommand = bwCommand;
44
49
  exports.ensureBwCli = ensureBwCli;
45
50
  const node_child_process_1 = require("node:child_process");
46
51
  const fs = __importStar(require("node:fs"));
@@ -49,6 +54,8 @@ const runtime_1 = require("../nerves/runtime");
49
54
  const INSTALL_TIMEOUT_MS = 120_000;
50
55
  const WHICH_TIMEOUT_MS = 5_000;
51
56
  const DEFAULT_WINDOWS_PATHEXT = ".EXE;.CMD;.BAT;.COM";
57
+ /** Must match the `@bitwarden/cli` dependency in package.json (a test checks). */
58
+ exports.PINNED_BW_CLI_VERSION = "2026.6.0";
52
59
  function execFileAsync(cmd, args, timeout) {
53
60
  return new Promise((resolve, reject) => {
54
61
  (0, node_child_process_1.execFile)(cmd, args, { timeout }, (err, stdout) => {
@@ -134,16 +141,60 @@ async function findExecutableViaNpmPrefix(command, platform = process.platform,
134
141
  return null;
135
142
  }
136
143
  /**
137
- * Ensure the `bw` CLI is available, installing it via npm if needed.
138
- * Returns the path to the `bw` binary.
144
+ * The `bw` entry point (`build/bw.js`) of the `@bitwarden/cli` package installed with ouro, or
145
+ * null when it is missing. It is run with ouro's own Node (see `bwCommand`), so it needs neither
146
+ * an executable bit nor `node` on PATH, and works on Windows too.
139
147
  */
140
- async function ensureBwCli() {
141
- // 1. Check if bw is already in PATH
148
+ function findBundledBwCli(resolvePackageJson = (id) => require.resolve(id)) {
149
+ try {
150
+ const packageJsonPath = resolvePackageJson("@bitwarden/cli/package.json");
151
+ const pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
152
+ const bin = typeof pkg.bin === "string" ? pkg.bin : (pkg.bin && typeof pkg.bin === "object" ? pkg.bin.bw : undefined);
153
+ if (typeof bin !== "string")
154
+ throw new Error("@bitwarden/cli package.json names no bw entry point");
155
+ const entry = path.join(path.dirname(packageJsonPath), bin);
156
+ if (!fs.existsSync(entry))
157
+ throw new Error(`bundled bw entry point is missing: ${entry}`);
158
+ return entry;
159
+ }
160
+ catch (error) {
161
+ (0, runtime_1.emitNervesEvent)({
162
+ level: "warn",
163
+ event: "repertoire.bw_cli_bundled_missing",
164
+ component: "repertoire",
165
+ message: "the bw that ships with ouro was not found; falling back to bw on PATH",
166
+ meta: { reason: error instanceof Error ? error.message : String(error) },
167
+ });
168
+ return null;
169
+ }
170
+ }
171
+ /** How to spawn a resolved `bw`: a `.js` entry point runs under this Node, anything else directly. */
172
+ function bwCommand(bwBinaryPath, args, nodePath = process.execPath) {
173
+ return bwBinaryPath.endsWith(".js") ? { file: nodePath, args: [bwBinaryPath, ...args] } : { file: bwBinaryPath, args };
174
+ }
175
+ /**
176
+ * Ensure the `bw` CLI is available: `OURO_BW_BINARY` when set, else the bundled pinned one, else
177
+ * one on PATH, else install the pinned version via npm. Returns the path to the `bw` binary.
178
+ */
179
+ async function ensureBwCli(findBundled = findBundledBwCli) {
180
+ // 0. An explicit override (tests and unusual installs): OURO_BW_BINARY
181
+ const override = process.env.OURO_BW_BINARY?.trim();
182
+ if (override) {
183
+ if (findExecutableOnPath(override))
184
+ return override;
185
+ throw new Error(`OURO_BW_BINARY is set to ${override}, which is not an executable file`);
186
+ }
187
+ // 1. The bw that ships with ouro
188
+ const bundled = findBundled();
189
+ if (bundled) {
190
+ return bundled;
191
+ }
192
+ // 2. A bw already on PATH
142
193
  const existing = findExecutableOnPath("bw");
143
194
  if (existing) {
144
195
  return existing;
145
196
  }
146
- // 2. Install via npm
197
+ // 3. Install the pinned version via npm
147
198
  (0, runtime_1.emitNervesEvent)({
148
199
  event: "repertoire.bw_cli_install_start",
149
200
  component: "repertoire",
@@ -151,7 +202,7 @@ async function ensureBwCli() {
151
202
  meta: {},
152
203
  });
153
204
  try {
154
- await execFileAsync("npm", ["install", "-g", "@bitwarden/cli"], INSTALL_TIMEOUT_MS);
205
+ await execFileAsync("npm", ["install", "-g", `@bitwarden/cli@${exports.PINNED_BW_CLI_VERSION}`], INSTALL_TIMEOUT_MS);
155
206
  }
156
207
  catch (err) {
157
208
  /* v8 ignore next -- execFileCb always throws Error instances @preserve */
@@ -165,7 +216,7 @@ async function ensureBwCli() {
165
216
  });
166
217
  throw new Error(`failed to install bw CLI via npm: ${reason}`);
167
218
  }
168
- // 3. Verify installation and return path
219
+ // 4. Verify installation and return path
169
220
  const installed = findExecutableOnPath("bw") ?? await findExecutableViaNpmPrefix("bw");
170
221
  if (installed) {
171
222
  (0, runtime_1.emitNervesEvent)({
@@ -174,6 +174,7 @@ async function probeCredentialVaultAccess(agentNameInput, unlockSecret, options
174
174
  const store = new bitwarden_store_1.BitwardenCredentialStore(vaultConfig.serverUrl, vaultConfig.email, unlockSecret, {
175
175
  appDataDir: agentDataDir,
176
176
  onLoginSuccess: () => (0, vault_unlock_1.clearVaultUnlockRejection)(agentDataDir),
177
+ onProgress: options.onProgress,
177
178
  });
178
179
  await store.get("__ouro_vault_probe__");
179
180
  }
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.907",
3
+ "version": "0.1.0-alpha.908",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@ouro.bot/cli",
9
- "version": "0.1.0-alpha.907",
9
+ "version": "0.1.0-alpha.908",
10
10
  "dependencies": {
11
11
  "@anthropic-ai/sdk": "^0.78.0",
12
12
  "@azure/identity": "^4.13.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ouro.bot/cli",
3
- "version": "0.1.0-alpha.907",
3
+ "version": "0.1.0-alpha.908",
4
4
  "engines": {
5
5
  "node": ">=22"
6
6
  },