@shuyhere/bb-agent 0.0.9 → 0.0.10

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.md CHANGED
@@ -5,6 +5,18 @@ All notable changes to BB-Agent will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.0.10] - 2026-04-07
9
+
10
+ ### Fixed
11
+
12
+ - npm install now uses a longer timeout, retries release-binary downloads, and reports real download errors instead of incorrectly saying no matching prebuilt binary exists
13
+ - npm install now shows progress logs during native binary download and verification so first-time installs on macOS/Linux are less confusing
14
+ - fullscreen `/login` provider-family status now correctly shows OpenAI OAuth state after ChatGPT login instead of incorrectly showing the API key path as not authenticated
15
+
16
+ ### Changed
17
+
18
+ - README install docs now lead with `npm install -g @shuyhere/bb-agent`, move terminal/font guidance into Troubleshooting, and clearly separate npm install from building from source for development
19
+
8
20
  ## [0.0.9] - 2026-04-07
9
21
 
10
22
  ### Added
@@ -46,5 +58,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
46
58
 
47
59
  - latest published package includes the post-0.0.7 startup, auth, model-default, and update-notice improvements
48
60
 
61
+ [0.0.10]: https://github.com/shuyhere/bb-agent/releases/tag/v0.0.10
49
62
  [0.0.9]: https://github.com/shuyhere/bb-agent/releases/tag/v0.0.9
50
63
  [0.0.8]: https://github.com/shuyhere/bb-agent/releases/tag/v0.0.8
package/README.md CHANGED
@@ -2,44 +2,36 @@
2
2
 
3
3
  ![BB-Agent title figure](assets/title-figure.png)
4
4
 
5
- BB means Bridge Baby in Death Stranding. I named this project that way because while building it, I was also enjoying Death Stranding and loved the idea of connecting everyone together.
5
+ > BB means Bridge Baby in Death Stranding. I named this project that way because while building it, I was also enjoying Death Stranding and loved the idea of connecting everyone together.
6
6
 
7
7
  A Rust-native AI coding agent for the terminal — featuring a fullscreen TUI, multi-provider support, tool use, session persistence, branching, extensions, and skills.
8
8
 
9
9
  ## Install
10
10
 
11
- ### Terminal & Font Compatibility
12
-
13
- BB-Agent uses Unicode glyphs and ANSI color in the fullscreen TUI. For the best visual experience, use a modern terminal and a Unicode-capable monospace font such as:
14
-
15
- - JetBrains Mono
16
- - SF Mono / Menlo
17
- - Fira Code
18
- - Cascadia Mono
19
- - Nerd Font variants of the above
20
-
21
- If some symbols look broken, missing, or too narrow in your terminal:
11
+ ```bash
12
+ npm install -g @shuyhere/bb-agent
13
+ ```
22
14
 
23
- 1. switch to a Unicode-capable monospace font
24
- 2. make sure your terminal uses UTF-8
25
- 3. enable BB-Agent compatibility mode
15
+ ### 1. Install with npm
26
16
 
27
- Compatibility mode uses safer ASCII-style fallback glyphs for spinner/status/tool markers:
17
+ npm install downloads a small wrapper package first, then fetches the matching native BB-Agent binary from the GitHub release for your platform.
28
18
 
29
- ```bash
30
- BB_TUI_COMPAT=1 bb
31
- ```
19
+ What to expect:
20
+ - first install can take a bit because npm downloads and verifies the native binary
21
+ - the installer now prints progress and retry information while downloading
22
+ - after install, run `bb`
32
23
 
33
- Or set this in `~/.bb-agent/settings.json`:
24
+ Current GitHub release binaries are published for:
25
+ - Linux x86_64
26
+ - macOS x86_64
27
+ - macOS arm64 (Apple Silicon)
28
+ - Windows x86_64
34
29
 
35
- ```json
36
- {
37
- "compatibility_mode": true
38
- }
39
- ```
30
+ If no matching prebuilt binary is available, or the download fails, npm install will print source-build instructions instead.
40
31
 
32
+ ### 2. Build from source for development
41
33
 
42
- ### From source (all platforms macOS, Linux, Windows)
34
+ Use this if you want to develop BB-Agent itself, work on the Rust codebase, or install directly without the npm downloader.
43
35
 
44
36
  Requires [Rust](https://rustup.rs). Install Rust first if you don't have it:
45
37
 
@@ -48,7 +40,7 @@ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
48
40
  source ~/.cargo/env
49
41
  ```
50
42
 
51
- Then build and install BB-Agent:
43
+ Then build and install BB-Agent from source:
52
44
 
53
45
  ```bash
54
46
  git clone https://github.com/shuyhere/bb-agent.git
@@ -58,16 +50,6 @@ cargo install --path crates/cli
58
50
 
59
51
  This compiles the `bb` binary and installs it to `~/.cargo/bin/bb` (which Rust adds to your PATH).
60
52
 
61
- ### npm (Linux/macOS/Windows — downloads matching prebuilt binary when available)
62
-
63
- ```bash
64
- npm install -g @shuyhere/bb-agent
65
- ```
66
-
67
- > If no matching prebuilt binary is available for your platform, npm install will print source-build instructions instead. After install, run `bb` to start.
68
- >
69
- > Current GitHub release binaries are published for Linux x86_64, macOS x86_64/arm64, and Windows x86_64.
70
-
71
53
  ## Getting Started
72
54
 
73
55
  ### 1. Start the TUI
@@ -205,6 +187,38 @@ BB-Agent uses layered configuration:
205
187
  | `bb-tui` | Terminal UI components and fullscreen experience |
206
188
  | `bb-cli` | The `bb` command-line application |
207
189
 
190
+ ## Troubleshooting
191
+
192
+ ### Terminal & Font Compatibility
193
+
194
+ BB-Agent uses Unicode glyphs and ANSI color in the fullscreen TUI. For the best visual experience, use a modern terminal and a Unicode-capable monospace font such as:
195
+
196
+ - JetBrains Mono
197
+ - SF Mono / Menlo
198
+ - Fira Code
199
+ - Cascadia Mono
200
+ - Nerd Font variants of the above
201
+
202
+ If some symbols look broken, missing, or too narrow in your terminal:
203
+
204
+ 1. switch to a Unicode-capable monospace font
205
+ 2. make sure your terminal uses UTF-8
206
+ 3. enable BB-Agent compatibility mode
207
+
208
+ Compatibility mode uses safer ASCII-style fallback glyphs for spinner/status/tool markers:
209
+
210
+ ```bash
211
+ BB_TUI_COMPAT=1 bb
212
+ ```
213
+
214
+ Or set this in `~/.bb-agent/settings.json`:
215
+
216
+ ```json
217
+ {
218
+ "compatibility_mode": true
219
+ }
220
+ ```
221
+
208
222
  ## Documentation
209
223
 
210
224
  - [Configuration Reference](docs/configuration.md) — settings.json, AGENTS.md, templates
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shuyhere/bb-agent",
3
- "version": "0.0.9",
3
+ "version": "0.0.10",
4
4
  "description": "BB-Agent — a Rust-native AI coding agent for the terminal",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -7,13 +7,15 @@ const fs = require("fs");
7
7
  const path = require("path");
8
8
  const os = require("os");
9
9
  const https = require("https");
10
+ const http = require("http");
10
11
 
11
12
  const packageJson = require("../package.json");
12
13
  const BINARY_RELEASE_TAG = `v${packageJson.version}`;
13
14
  const REPO = "shuyhere/bb-agent";
14
- const PACKAGE_ROOT = path.resolve(__dirname, "..");
15
15
  const NATIVE_DIR = path.join(__dirname, "..", "native");
16
- const DOWNLOAD_TIMEOUT_MS = 15_000;
16
+ const DOWNLOAD_TIMEOUT_MS = 120_000;
17
+ const MAX_REDIRECTS = 8;
18
+ const MAX_DOWNLOAD_ATTEMPTS = 3;
17
19
 
18
20
  function isWindows() {
19
21
  return os.platform() === "win32";
@@ -44,34 +46,6 @@ function getTarget() {
44
46
  return `${a}-${p}`;
45
47
  }
46
48
 
47
- function downloadBinary(url, dest, timeoutMs) {
48
- return new Promise((resolve, reject) => {
49
- const timer = setTimeout(() => reject(new Error("Download timed out")), timeoutMs);
50
-
51
- const follow = (url, redirects = 0) => {
52
- if (redirects > 5) { clearTimeout(timer); return reject(new Error("Too many redirects")); }
53
-
54
- const mod = url.startsWith("https") ? https : require("http");
55
- const req = mod.get(url, (res) => {
56
- if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
57
- return follow(res.headers.location, redirects + 1);
58
- }
59
- if (res.statusCode !== 200) {
60
- clearTimeout(timer);
61
- return reject(new Error(`HTTP ${res.statusCode}`));
62
- }
63
- const file = fs.createWriteStream(dest);
64
- res.pipe(file);
65
- file.on("finish", () => { clearTimeout(timer); file.close(); resolve(); });
66
- file.on("error", (e) => { clearTimeout(timer); reject(e); });
67
- });
68
- req.on("error", (e) => { clearTimeout(timer); reject(e); });
69
- req.on("timeout", () => { req.destroy(); clearTimeout(timer); reject(new Error("Request timed out")); });
70
- };
71
- follow(url);
72
- });
73
- }
74
-
75
49
  function assetNameForTarget(target) {
76
50
  return isWindows() ? `bb-${target}.exe` : `bb-${target}`;
77
51
  }
@@ -87,60 +61,232 @@ function hasBundledNativeBinary() {
87
61
  }
88
62
  }
89
63
 
90
- async function tryDownloadPrebuilt(target) {
91
- const assetName = assetNameForTarget(target);
92
- const url = `https://github.com/${REPO}/releases/download/${BINARY_RELEASE_TAG}/${assetName}`;
93
-
94
- fs.mkdirSync(NATIVE_DIR, { recursive: true });
95
- const dest = nativeBinaryPath();
64
+ function makeDownloadError(kind, message, statusCode) {
65
+ const err = new Error(message);
66
+ err.kind = kind;
67
+ if (statusCode) err.statusCode = statusCode;
68
+ return err;
69
+ }
96
70
 
97
- try {
98
- console.log(`Downloading BB-Agent ${BINARY_RELEASE_TAG} for ${target}...`);
99
- await downloadBinary(url, dest, DOWNLOAD_TIMEOUT_MS);
100
- fs.chmodSync(dest, 0o755);
71
+ function formatBytes(bytes) {
72
+ if (!Number.isFinite(bytes) || bytes <= 0) return "0 B";
73
+ const units = ["B", "KB", "MB", "GB"];
74
+ let value = bytes;
75
+ let unit = 0;
76
+ while (value >= 1024 && unit < units.length - 1) {
77
+ value /= 1024;
78
+ unit += 1;
79
+ }
80
+ return `${value.toFixed(value >= 10 || unit === 0 ? 0 : 1)} ${units[unit]}`;
81
+ }
101
82
 
102
- // Verify the binary is executable
103
- try {
104
- execSync(`"${dest}" --version`, { stdio: "pipe", timeout: 5000 });
105
- } catch {
106
- // Binary may not run on this platform (e.g. wrong arch) — remove it
107
- fs.unlinkSync(dest);
108
- return false;
83
+ function requestBinary(url, dest, redirects = 0) {
84
+ return new Promise((resolve, reject) => {
85
+ if (redirects > MAX_REDIRECTS) {
86
+ reject(makeDownloadError("redirect", "Too many redirects"));
87
+ return;
109
88
  }
110
89
 
111
- console.log(" BB-Agent binary installed successfully.");
112
- return true;
90
+ const client = url.startsWith("https:") ? https : http;
91
+ const req = client.get(
92
+ url,
93
+ {
94
+ headers: {
95
+ "User-Agent": `${packageJson.name}/${packageJson.version} (postinstall)`,
96
+ Accept: "application/octet-stream,application/octet-stream; q=0.9,*/*;q=0.1",
97
+ },
98
+ },
99
+ (res) => {
100
+ const status = res.statusCode || 0;
101
+
102
+ if (status >= 300 && status < 400 && res.headers.location) {
103
+ res.resume();
104
+ requestBinary(res.headers.location, dest, redirects + 1)
105
+ .then(resolve)
106
+ .catch(reject);
107
+ return;
108
+ }
109
+
110
+ if (status === 404) {
111
+ res.resume();
112
+ reject(makeDownloadError("not-found", `HTTP 404 for ${url}`, 404));
113
+ return;
114
+ }
115
+
116
+ if (status !== 200) {
117
+ res.resume();
118
+ reject(makeDownloadError("http", `HTTP ${status} for ${url}`, status));
119
+ return;
120
+ }
121
+
122
+ const totalBytes = Number(res.headers["content-length"] || 0);
123
+ let downloadedBytes = 0;
124
+ let lastLoggedAt = Date.now();
125
+ if (totalBytes > 0) {
126
+ console.log(`Release asset size: ${formatBytes(totalBytes)}.`);
127
+ } else {
128
+ console.log("Release asset size: unknown (streaming download).");
129
+ }
130
+
131
+ const file = fs.createWriteStream(dest);
132
+ let settled = false;
133
+
134
+ const finish = (fn, value) => {
135
+ if (settled) return;
136
+ settled = true;
137
+ clearTimeout(timeout);
138
+ fn(value);
139
+ };
140
+
141
+ res.on("data", (chunk) => {
142
+ downloadedBytes += chunk.length;
143
+ const now = Date.now();
144
+ if (now - lastLoggedAt >= 5000) {
145
+ lastLoggedAt = now;
146
+ if (totalBytes > 0) {
147
+ const percent = Math.min(100, Math.round((downloadedBytes / totalBytes) * 100));
148
+ console.log(
149
+ `Download progress: ${percent}% (${formatBytes(downloadedBytes)} / ${formatBytes(totalBytes)})`
150
+ );
151
+ } else {
152
+ console.log(`Downloaded ${formatBytes(downloadedBytes)} so far...`);
153
+ }
154
+ }
155
+ });
156
+
157
+ file.on("finish", () => {
158
+ file.close((closeErr) => {
159
+ if (closeErr) {
160
+ finish(reject, makeDownloadError("write", closeErr.message));
161
+ } else {
162
+ if (totalBytes > 0) {
163
+ console.log(
164
+ `Download complete: ${formatBytes(downloadedBytes)} / ${formatBytes(totalBytes)}.`
165
+ );
166
+ } else {
167
+ console.log(`Download complete: ${formatBytes(downloadedBytes)}.`);
168
+ }
169
+ finish(resolve);
170
+ }
171
+ });
172
+ });
173
+
174
+ file.on("error", (err) => {
175
+ try { file.close(() => {}); } catch {}
176
+ try { fs.unlinkSync(dest); } catch {}
177
+ finish(reject, makeDownloadError("write", err.message));
178
+ });
179
+
180
+ res.on("error", (err) => {
181
+ try { file.close(() => {}); } catch {}
182
+ try { fs.unlinkSync(dest); } catch {}
183
+ finish(reject, makeDownloadError("network", err.message));
184
+ });
185
+
186
+ res.pipe(file);
187
+ }
188
+ );
189
+
190
+ const timeout = setTimeout(() => {
191
+ req.destroy(makeDownloadError("timeout", `Download timed out after ${DOWNLOAD_TIMEOUT_MS}ms`));
192
+ }, DOWNLOAD_TIMEOUT_MS);
193
+
194
+ req.on("error", (err) => {
195
+ clearTimeout(timeout);
196
+ reject(makeDownloadError(err.kind || "network", err.message));
197
+ });
198
+ });
199
+ }
200
+
201
+ function verifyBinary(dest) {
202
+ try {
203
+ execSync(`"${dest}" --version`, { stdio: "pipe", timeout: 5000 });
204
+ return { ok: true };
113
205
  } catch (err) {
114
- // Clean up partial download
115
- try { fs.unlinkSync(dest); } catch {}
116
- return false;
206
+ return {
207
+ ok: false,
208
+ message: err && err.message ? err.message : "binary verification failed",
209
+ };
117
210
  }
118
211
  }
119
212
 
213
+ async function tryDownloadPrebuilt(target) {
214
+ const assetName = assetNameForTarget(target);
215
+ const url = `https://github.com/${REPO}/releases/download/${BINARY_RELEASE_TAG}/${assetName}`;
120
216
 
121
- async function main() {
122
- if (process.env.BB_SKIP_POSTINSTALL) {
123
- return;
124
- }
217
+ fs.mkdirSync(NATIVE_DIR, { recursive: true });
218
+ const dest = nativeBinaryPath();
125
219
 
126
- if (hasBundledNativeBinary()) return;
220
+ let lastError = null;
221
+ for (let attempt = 1; attempt <= MAX_DOWNLOAD_ATTEMPTS; attempt += 1) {
222
+ try {
223
+ console.log(
224
+ `Downloading BB-Agent ${BINARY_RELEASE_TAG} for ${target} (attempt ${attempt}/${MAX_DOWNLOAD_ATTEMPTS})...`
225
+ );
226
+ console.log("This may take a little while on first install because npm downloads and verifies the native binary from the GitHub release.");
227
+ try { fs.unlinkSync(dest); } catch {}
228
+ await requestBinary(url, dest, 0);
229
+ fs.chmodSync(dest, 0o755);
127
230
 
128
- const target = getTarget();
231
+ const verified = verifyBinary(dest);
232
+ if (!verified.ok) {
233
+ try { fs.unlinkSync(dest); } catch {}
234
+ return {
235
+ ok: false,
236
+ kind: "verify",
237
+ message: `Downloaded binary could not run: ${verified.message}`,
238
+ };
239
+ }
129
240
 
130
- // Try prebuilt binary
131
- if (target) {
132
- const ok = await tryDownloadPrebuilt(target);
133
- if (ok) return;
241
+ console.log("Verifying downloaded binary...");
242
+ console.log("✓ BB-Agent binary installed successfully.");
243
+ return { ok: true };
244
+ } catch (err) {
245
+ lastError = err;
246
+ try { fs.unlinkSync(dest); } catch {}
247
+ if (err.kind === "not-found") {
248
+ return {
249
+ ok: false,
250
+ kind: "not-found",
251
+ message: `No release asset named ${assetName} was found for ${BINARY_RELEASE_TAG}.`,
252
+ };
253
+ }
254
+ if (attempt < MAX_DOWNLOAD_ATTEMPTS) {
255
+ await new Promise((resolve) => setTimeout(resolve, 1000 * attempt));
256
+ }
257
+ }
134
258
  }
135
259
 
136
- // No prebuilt available — print instructions instead of trying cargo build
137
- // (cargo build takes 5+ minutes and would appear to hang)
138
- const platform = `${os.platform()}-${os.arch()}`;
260
+ return {
261
+ ok: false,
262
+ kind: (lastError && lastError.kind) || "download",
263
+ message: (lastError && lastError.message) || "unknown download failure",
264
+ };
265
+ }
266
+
267
+ function printFallbackHelp(platform, reason) {
139
268
  console.log("");
140
- console.log(`BB-Agent ${packageJson.version}: matching prebuilt binary not available yet for ${platform}.`);
269
+ if (reason && reason.kind === "not-found") {
270
+ console.log(
271
+ `BB-Agent ${packageJson.version}: matching prebuilt binary is not published for ${platform}.`
272
+ );
273
+ } else if (reason) {
274
+ console.log(
275
+ `BB-Agent ${packageJson.version}: failed to download the prebuilt binary for ${platform}.`
276
+ );
277
+ console.log(`Reason: ${reason.message}`);
278
+ } else {
279
+ console.log(
280
+ `BB-Agent ${packageJson.version}: matching prebuilt binary not available yet for ${platform}.`
281
+ );
282
+ }
141
283
  console.log("");
142
284
  console.log("╔══════════════════════════════════════════════════════════════╗");
143
- console.log("║ BB-Agent: no prebuilt binary for " + platform.padEnd(19) + " ║");
285
+ console.log(
286
+ "║ BB-Agent: npm could not install native binary for " +
287
+ platform.padEnd(16) +
288
+ " ║"
289
+ );
144
290
  console.log("║ ║");
145
291
  console.log("║ Install Rust (if needed): ║");
146
292
  console.log("║ https://rustup.rs ║");
@@ -155,8 +301,40 @@ async function main() {
155
301
  console.log("");
156
302
  }
157
303
 
158
- main().catch((err) => {
159
- // Never fail npm install — just print instructions
160
- console.error("BB-Agent postinstall notice:", err.message);
161
- console.log("Install manually: git clone https://github.com/shuyhere/bb-agent.git && cd bb-agent && cargo install --path crates/cli");
162
- });
304
+ async function main() {
305
+ if (process.env.BB_SKIP_POSTINSTALL) {
306
+ return;
307
+ }
308
+
309
+ if (hasBundledNativeBinary()) {
310
+ return;
311
+ }
312
+
313
+ const target = getTarget();
314
+ const platform = `${os.platform()}-${os.arch()}`;
315
+
316
+ if (target) {
317
+ const result = await tryDownloadPrebuilt(target);
318
+ if (result.ok) {
319
+ return;
320
+ }
321
+ printFallbackHelp(platform, result);
322
+ return;
323
+ }
324
+
325
+ printFallbackHelp(platform, {
326
+ kind: "unsupported-platform",
327
+ message: `Unsupported target mapping for ${platform}`,
328
+ });
329
+ }
330
+
331
+ main()
332
+ .catch((err) => {
333
+ console.error("BB-Agent postinstall notice:", err && err.message ? err.message : String(err));
334
+ console.log(
335
+ "Install manually: git clone https://github.com/shuyhere/bb-agent.git && cd bb-agent && cargo install --path crates/cli"
336
+ );
337
+ })
338
+ .finally(() => {
339
+ process.exitCode = 0;
340
+ });