@bivy/bivy 0.12.0-staging.406 → 0.12.0-staging.407

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/bin/bivy.mjs CHANGED
@@ -3621,8 +3621,9 @@ async function cmdSetup(args = []) {
3621
3621
  // If self-host endpoints are already provided via the environment, default to
3622
3622
  // self-hosted so a scripted or self-hosted install doesn't have to re-pick it
3623
3623
  // (BIVY_CONTROL_PLANE_URL / BIVY_RELAY_URL then pre-fill the URL prompts below).
3624
- const selfHostEnv = Boolean((process.env.BIVY_CONTROL_PLANE_URL || "").trim() || (process.env.BIVY_RELAY_URL || "").trim());
3625
- const syncChoice = await askChoice(
3624
+ const nodeClaimCode = process.env.BIVY_NODE_CLAIM_CODE?.trim();
3625
+ const selfHostEnv = !nodeClaimCode && Boolean((process.env.BIVY_CONTROL_PLANE_URL || "").trim() || (process.env.BIVY_RELAY_URL || "").trim());
3626
+ const syncChoice = nodeClaimCode ? "h" : await askChoice(
3626
3627
  "Remote access",
3627
3628
  [
3628
3629
  { key: "h", label: "hosted (recommended — sign in with GitHub or email; nothing caps your local usage)" },
@@ -3644,29 +3645,33 @@ async function cmdSetup(args = []) {
3644
3645
  }
3645
3646
 
3646
3647
  if (syncChoice !== "l") {
3647
- const loginChoice = await askChoice(
3648
- "Remote login",
3649
- [
3650
- { key: "g", label: "GitHub" },
3651
- { key: "e", label: "email sign-in link (open or scan on any device)" },
3652
- ],
3653
- "g",
3654
- );
3655
- if (loginChoice === "e") {
3656
- const email = await ask(" Your account email:", config.env.BIVY_EMAIL || "");
3657
- if (email.trim()) relayArgs.push("--email", email.trim());
3658
- else relayArgs.push("--github");
3659
- } else {
3660
- relayArgs.push("--github");
3648
+ if (!nodeClaimCode) {
3649
+ const loginChoice = await askChoice(
3650
+ "Remote login",
3651
+ [
3652
+ { key: "g", label: "GitHub" },
3653
+ { key: "e", label: "email sign-in link (open or scan on any device)" },
3654
+ ],
3655
+ "g",
3656
+ );
3657
+ if (loginChoice === "e") {
3658
+ const email = await ask(" Your account email:", config.env.BIVY_EMAIL || "");
3659
+ if (email.trim()) relayArgs.push("--email", email.trim());
3660
+ else relayArgs.push("--github");
3661
+ } else {
3662
+ relayArgs.push("--github");
3663
+ }
3661
3664
  }
3662
3665
 
3663
3666
  const useGithub = relayArgs.includes("--github");
3664
3667
  try { fs.rmSync(setupSessionPath, { force: true }); } catch { /* best effort */ }
3665
3668
  let relayOk;
3666
3669
  for (;;) {
3667
- console.log(c.dim(useGithub
3668
- ? " We'll open GitHub in your browser (or print the URL on a headless server). Authorize, and setup continues automatically."
3669
- : " We'll email you a sign-in link. Open it in any browser and setup continues automatically."));
3670
+ console.log(c.dim(nodeClaimCode
3671
+ ? " Using the one-time machine claim from your Bivy account; no additional sign-in is required."
3672
+ : useGithub
3673
+ ? " We'll open GitHub in your browser (or print the URL on a headless server). Authorize, and setup continues automatically."
3674
+ : " We'll email you a sign-in link. Open it in any browser and setup continues automatically."));
3670
3675
  rl.pause();
3671
3676
  const code = await run(nodeBin, [...nodeScriptArgs(relaySetupEntry), ...relayArgs, "--emit-session", setupSessionPath], {
3672
3677
  cwd: repoRoot,
@@ -197,19 +197,29 @@ async function main() {
197
197
  const clientBaseUrl = (arg("client", process.env.BIVY_CLIENT_BASE_URL) ?? controlPlaneUrl).replace(/\/$/, "") || controlPlaneUrl;
198
198
  const email = arg("email", process.env.BIVY_EMAIL);
199
199
  const sessionToken = arg("session-token", process.env.BIVY_SESSION_TOKEN);
200
+ const nodeClaimCode = process.env.BIVY_NODE_CLAIM_CODE?.trim();
200
201
  // GitHub is the primary sign-in: used when --github is passed, or by default
201
- // when neither an email nor an existing session token is supplied.
202
- const useGithub = process.argv.includes("--github") || process.env.BIVY_AUTH === "github" || (!email && !sessionToken);
202
+ // when neither an email, an existing session token, nor a one-time machine
203
+ // claim is supplied. Claims authorize enrollment only and never mint a user
204
+ // session token.
205
+ const useGithub = process.argv.includes("--github") || process.env.BIVY_AUTH === "github" || (!email && !sessionToken && !nodeClaimCode);
203
206
  const identity = NodeIdentity.load(appDir);
204
207
  console.log(`Node: ${identity.name} (${identity.nodeId})`);
205
208
  console.log(`Control plane: ${controlPlaneUrl}`);
206
209
  console.log(`Relay: ${relayUrl}`);
207
210
  await checkControlPlane(controlPlaneUrl);
208
- const token = sessionToken ?? (useGithub ? await githubDeviceLogin(controlPlaneUrl) : await deviceLogin(controlPlaneUrl, email));
211
+ const token = nodeClaimCode ? undefined
212
+ : sessionToken ?? (useGithub ? await githubDeviceLogin(controlPlaneUrl) : await deviceLogin(controlPlaneUrl, email));
209
213
  async function enrollNode() {
210
- return fetchJson(`${controlPlaneUrl}/nodes/enroll`, {
214
+ const url = nodeClaimCode
215
+ ? `${controlPlaneUrl}/claim/${encodeURIComponent(nodeClaimCode)}/enroll`
216
+ : `${controlPlaneUrl}/nodes/enroll`;
217
+ return fetchJson(url, {
211
218
  method: "POST",
212
- headers: { "content-type": "application/json", authorization: `Bearer ${token}` },
219
+ headers: {
220
+ "content-type": "application/json",
221
+ ...(token ? { authorization: `Bearer ${token}` } : {}),
222
+ },
213
223
  body: JSON.stringify({ nodeId: identity.nodeId, name: identity.name }),
214
224
  });
215
225
  }
@@ -247,7 +257,7 @@ async function main() {
247
257
  // read once and deleted by setup. We skip it when the caller supplied a session
248
258
  // token only via --session-token/env with no --emit-session, i.e. non-setup use.
249
259
  const emitSession = arg("emit-session");
250
- if (emitSession) {
260
+ if (emitSession && token) {
251
261
  try {
252
262
  const handoff = JSON.stringify({ session: token, nodeId: identity.nodeId });
253
263
  fs.writeFileSync(emitSession, `${handoff}\n`, { mode: 0o600 });
@@ -257,7 +267,7 @@ async function main() {
257
267
  // best effort — setup falls back to opening the plain remote app URL
258
268
  }
259
269
  }
260
- console.log(`\n✓ Signed in and enrolled this node. Wrote ${filePath}`);
270
+ console.log(`\n✓ ${nodeClaimCode ? "Claimed and enrolled" : "Signed in and enrolled"} this node. Wrote ${filePath}`);
261
271
  console.log('Run "bivy link" to pair a phone, or use "Link remote device" in the app (bivy open).');
262
272
  }
263
273
  main().catch((error) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bivy/bivy",
3
- "version": "0.12.0-staging.406",
3
+ "version": "0.12.0-staging.407",
4
4
  "type": "module",
5
5
  "license": "AGPL-3.0-only",
6
6
  "description": "Run coding agents on machines you own. Open-source, self-hostable agent workspace.",