@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 +9 -0
- package/deploy/unraid/sanctuary.ouro/bundle-meta.json +1 -1
- package/deploy/unraid/sanctuary.xml +1 -1
- package/dist/heart/daemon/cli-exec.js +2 -2
- package/dist/repertoire/bitwarden-store.js +12 -2
- package/dist/repertoire/bw-installer.js +60 -9
- package/dist/repertoire/credential-access.js +1 -0
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
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,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.
|
|
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
|
|
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
|
-
*
|
|
6
|
-
*
|
|
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
|
-
*
|
|
138
|
-
*
|
|
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
|
-
|
|
141
|
-
|
|
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
|
-
//
|
|
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",
|
|
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
|
-
//
|
|
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
|
}
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ouro.bot/cli",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
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.
|
|
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",
|