@bivy/bivy 0.1.0-staging.2 → 0.1.0-staging.3

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.
Files changed (2) hide show
  1. package/bin/bivy.mjs +53 -6
  2. package/package.json +1 -1
package/bin/bivy.mjs CHANGED
@@ -84,6 +84,47 @@ const relayConfigPath = path.join(appDir, "relay.json");
84
84
  // Read once and deleted immediately — never a credential left at rest.
85
85
  const setupSessionPath = path.join(appDir, ".setup-session.json");
86
86
  const updateLogPath = path.join(appDir, "update.log");
87
+ // The release channel (npm dist-tag) this install tracks, recorded at install
88
+ // time by install.sh and here by `bivy update --channel`. Absent = `latest`, so
89
+ // existing installs keep behaving exactly as before.
90
+ const channelPath = path.join(appDir, "channel");
91
+ function readChannel() {
92
+ try {
93
+ const ch = fs.readFileSync(channelPath, "utf8").trim();
94
+ return /^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(ch) ? ch : "latest";
95
+ } catch {
96
+ return "latest";
97
+ }
98
+ }
99
+ function writeChannel(ch) {
100
+ try {
101
+ fs.mkdirSync(appDir, { recursive: true });
102
+ fs.writeFileSync(channelPath, `${ch}\n`);
103
+ } catch {
104
+ /* best-effort: an unwritable data dir just means update uses the default */
105
+ }
106
+ }
107
+ // Pick the channel for an update: an explicit --stable/--staging/--channel flag
108
+ // wins and is PERSISTED (so later plain `bivy update`s stay on it); otherwise use
109
+ // the recorded channel (default `latest`). Returns a validated tag.
110
+ function resolveUpdateChannel(args) {
111
+ let ch = null;
112
+ if (args.includes("--stable")) ch = "latest";
113
+ else if (args.includes("--staging")) ch = "staging";
114
+ else {
115
+ const i = args.indexOf("--channel");
116
+ if (i !== -1 && args[i + 1]) ch = args[i + 1];
117
+ }
118
+ if (ch) {
119
+ if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(ch)) {
120
+ console.log(c.yellow(`Ignoring invalid channel '${ch}'; keeping ${readChannel()}.`));
121
+ ch = null;
122
+ } else {
123
+ writeChannel(ch);
124
+ }
125
+ }
126
+ return ch || readChannel();
127
+ }
87
128
  const packaged = fs.existsSync(path.join(repoRoot, "dist", "server.js"));
88
129
  const serverEntry = path.join(repoRoot, packaged ? "dist/server.js" : "src/server.ts");
89
130
  const nativePiEntry = path.join(repoRoot, packaged ? "dist/native-pi.js" : "src/native-pi.ts");
@@ -3393,7 +3434,7 @@ async function showDetachedUpdateProgress(child, start) {
3393
3434
 
3394
3435
  async function cmdUpdate(args = []) {
3395
3436
  if (args.includes("-h") || args.includes("--help")) {
3396
- console.log("Usage: bivy update [--force|--no-wait]\n\nUpdate Bivy + install deps + restart service. Waits for active sessions to finish a turn first; --force/--no-wait skips the wait. See 'bivy update:log' for the last run's output.");
3437
+ console.log("Usage: bivy update [--force|--no-wait] [--staging|--stable|--channel <name>]\n\nUpdate Bivy + install deps + restart service. Stays on the release channel recorded at install time (default 'latest'); --staging/--stable/--channel switch channels and are remembered for future updates. Waits for active sessions to finish a turn first; --force/--no-wait skips the wait. See 'bivy update:log' for the last run's output.");
3397
3438
  return;
3398
3439
  }
3399
3440
  // Inside a Bivy web/PWA terminal the shell is a child of the node's own
@@ -3437,11 +3478,15 @@ async function runUpdate(args = []) {
3437
3478
  return;
3438
3479
  }
3439
3480
 
3481
+ // Update along the recorded channel (default `latest`), not a hardcoded tag,
3482
+ // so a staging box stays on staging instead of silently jumping to production.
3483
+ const channel = resolveUpdateChannel(args);
3484
+
3440
3485
  if (kind === "npm-global") {
3441
- console.log(c.dim("Updating the globally-installed bivy package…"));
3442
- const code = await run("npm", ["install", "-g", "@bivy/bivy@latest", "--no-audit", "--no-fund"]);
3486
+ console.log(c.dim(`Updating the globally-installed bivy package (channel: ${channel})…`));
3487
+ const code = await run("npm", ["install", "-g", `@bivy/bivy@${channel}`, "--no-audit", "--no-fund"]);
3443
3488
  if (code !== 0) {
3444
- console.log(c.yellow(`npm reported an issue (exit ${code}). Try: sudo npm i -g @bivy/bivy@latest`));
3489
+ console.log(c.yellow(`npm reported an issue (exit ${code}). Try: sudo npm i -g @bivy/bivy@${channel}`));
3445
3490
  process.exit(code);
3446
3491
  }
3447
3492
  await ensureBundledAgents();
@@ -3457,7 +3502,7 @@ async function runUpdate(args = []) {
3457
3502
  }
3458
3503
 
3459
3504
  if (kind === "packaged") {
3460
- console.log(c.dim("Updating packaged Bivy install…"));
3505
+ console.log(c.dim(`Updating packaged Bivy install (channel: ${channel})…`));
3461
3506
  // The actual restart happens inside install.sh, which shells out to
3462
3507
  // `bivy restart` (using the freshly-swapped binary) once the new code is in
3463
3508
  // place — that invocation waits for busy sessions on its own. This earlier
@@ -3466,9 +3511,11 @@ async function runUpdate(args = []) {
3466
3511
  // surprise anyone mid-turn.
3467
3512
  const config = loadConfig();
3468
3513
  await waitForIdleSessions(config, { skip: skipWait });
3514
+ // install.sh reads BIVY_CHANNEL from the env; pass the recorded channel so a
3515
+ // packaged re-install stays on it (and re-records it) instead of latest.
3469
3516
  const code = await run("bash", ["-c", "curl -fsSL https://bivy.sh/install.sh | bash"], {
3470
3517
  cwd: repoRoot,
3471
- env: { ...process.env, BIVY_HOME: repoRoot },
3518
+ env: { ...process.env, BIVY_HOME: repoRoot, BIVY_CHANNEL: channel },
3472
3519
  });
3473
3520
  process.exit(code);
3474
3521
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bivy/bivy",
3
- "version": "0.1.0-staging.2",
3
+ "version": "0.1.0-staging.3",
4
4
  "type": "module",
5
5
  "license": "FSL-1.1-ALv2",
6
6
  "description": "Run coding agents on machines you own. Source-available, self-hostable agent workspace.",