@abuaboud/box 0.7.9 → 0.7.11

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/dist/cbox.mjs +27 -6
  2. package/package.json +1 -1
package/dist/cbox.mjs CHANGED
@@ -351,14 +351,14 @@ async function setUpEnvironment(repo, dir, envs) {
351
351
  const detected = detectSetup(dir), sized = detectSize(dir), names = secretNames(dir);
352
352
  let name = repo.split("/")[1].toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/^-+/, "").slice(0, 28) || "env";
353
353
  for (let i = 2, base = name; envs.some((e) => e.name === name); i++) name = `${base}-${i}`;
354
- let setup = detected?.setup ?? "", size = sized.size, secrets = "";
354
+ let setup2 = detected?.setup ?? "", size = sized.size, secrets = "";
355
355
  const sizes = { small: "2 vCPU, 4 GB", medium: "4 vCPU, 8 GB", large: "8 vCPU, 16 GB" };
356
356
  for (; ; ) {
357
357
  console.log();
358
358
  console.log(` ${dim("Name ")} ${name}`);
359
359
  console.log(` ${dim("Repo ")} ${repo}`);
360
360
  console.log(` ${dim("Machine ")} ${size[0].toUpperCase() + size.slice(1)} ${dim(`\xB7 ${sizes[size]}${size === sized.size ? ` (${sized.why})` : ""}`)}`);
361
- console.log(` ${dim("Setup ")} ${setup ? styleText3("cyan", setup) : dim("none")} ${dim(detected && setup === detected.setup ? `\xB7 from ${detected.from}` : setup ? "" : "\xB7 boxes start from a plain clone")}`);
361
+ console.log(` ${dim("Setup ")} ${setup2 ? styleText3("cyan", setup2) : dim("none")} ${dim(detected && setup2 === detected.setup ? `\xB7 from ${detected.from}` : setup2 ? "" : "\xB7 boxes start from a plain clone")}`);
362
362
  if (names.length) console.log(` ${dim("Secrets ")} ${names.join(", ")} ${dim(secrets ? "\xB7 values copied from .env" : "\xB7 names from .env.example, values empty")}`);
363
363
  console.log();
364
364
  const choice = await select({ message: "Create it?", choices: [
@@ -377,13 +377,13 @@ async function setUpEnvironment(repo, dir, envs) {
377
377
  if (choice === "edit") {
378
378
  name = await input({ message: "Name", default: name, validate: (v) => /^[a-z0-9][a-z0-9-]{0,30}$/.test(v) || "Lowercase letters, digits and dashes" });
379
379
  size = await select({ message: "Machine", default: size, choices: Object.entries(sizes).map(([value, spec]) => ({ name: `${value.padEnd(8)}${dim(spec)}`, value })) });
380
- setup = (await input({ message: "Setup script (runs once in the snapshot; empty for none)", default: setup })).trim();
380
+ setup2 = (await input({ message: "Setup script (runs once in the snapshot; empty for none)", default: setup2 })).trim();
381
381
  continue;
382
382
  }
383
383
  break;
384
384
  }
385
385
  await ensureGitHub(repo);
386
- const { id } = await api("/api/environments", { method: "POST", body: JSON.stringify({ name, repo, size, setup, secrets }) });
386
+ const { id } = await api("/api/environments", { method: "POST", body: JSON.stringify({ name, repo, size, setup: setup2, secrets }) });
387
387
  ok(`Environment ${styleText3("cyan", name)} created`);
388
388
  return { id, name, repo, status: "building", size, progress: "Starting a build machine" };
389
389
  }
@@ -417,6 +417,7 @@ ${styleText3("yellow", "!")} Connect GitHub so Box can clone ${repo} and your bo
417
417
 
418
418
  // src/local.ts
419
419
  import { execFileSync as execFileSync3, spawn as spawn3 } from "node:child_process";
420
+ import { createHash } from "node:crypto";
420
421
  import { existsSync as existsSync5, mkdirSync as mkdirSync4, readdirSync as readdirSync2, readFileSync as readFileSync5, renameSync, rmSync, statSync, watch, writeFileSync as writeFileSync4 } from "node:fs";
421
422
  import { homedir as homedir4 } from "node:os";
422
423
  import { basename, dirname, join as join5 } from "node:path";
@@ -781,6 +782,7 @@ async function openLocal({ name, env, here, branch, fresh = false }) {
781
782
  api("/api/environments")
782
783
  ]).finally(() => stop());
783
784
  const environment = envs.find((e) => e.name === env) ?? fail(`No environment named ${env}.`);
785
+ void sendSetup().catch(() => void 0);
784
786
  const folder = prepareFolder(name, env, here, got.mcp);
785
787
  try {
786
788
  writeFileSync4(join5(BOXES, "llms.txt"), await fetch(new URL("/llms.txt", load().server)).then((r) => r.ok ? r.text() : Promise.reject()));
@@ -802,6 +804,14 @@ async function openLocal({ name, env, here, branch, fresh = false }) {
802
804
  ok(`Claude closed \xB7 repo synced \xB7 ${name} sleeps once it's quiet ${dim(`\xB7 come back with: box ${name}`)}`);
803
805
  process.exit(code2);
804
806
  }
807
+ async function sendSetup() {
808
+ const { tar, skills, enabled, memory, ...home2 } = localClaudeHome();
809
+ const buf = await tar;
810
+ const sha = buf && buf.length <= 30 << 20 ? createHash("sha256").update(buf).digest("hex") : void 0;
811
+ const put = (body) => api("/api/laptop-setup", { method: "PUT", body: JSON.stringify({ home: { ...home2, memory }, sha, ...body }) });
812
+ if ((await put({})).need && buf) await put({ tgz: buf.toString("base64") });
813
+ return { skills, plugins: enabled.length, memory: !!memory.trim(), tooBig: !!buf && !sha };
814
+ }
805
815
  var claude = (folder, args) => new Promise((done) => spawn3("claude", args, { cwd: folder, stdio: "inherit", env: { ...process.env, ...process.getuid?.() === 0 && { IS_SANDBOX: "1" } } }).on("error", () => fail("Claude Code is not installed on this laptop. npm i -g @anthropic-ai/claude-code, or use box --remote.")).on("close", (c) => done(c ?? 0)));
806
816
  function trustFolder(folder) {
807
817
  const file2 = join5(homedir4(), ".claude.json");
@@ -862,7 +872,7 @@ async function rm(name, opts = {}) {
862
872
  }
863
873
 
864
874
  // src/remote.ts
865
- import { createHash } from "node:crypto";
875
+ import { createHash as createHash2 } from "node:crypto";
866
876
  import { styleText as styleText4 } from "node:util";
867
877
  import { select as select3 } from "@inquirer/prompts";
868
878
  import ssh22 from "ssh2";
@@ -985,7 +995,7 @@ async function newRemoteBox(env, branch, name, o) {
985
995
  async function sendHome(name, tar) {
986
996
  const buf = await tar;
987
997
  if (!buf || buf.length > 30 << 20) return api(`/api/boxes/${name}/home`, { method: "POST", body: "{}" }).catch(() => void 0);
988
- const sha = createHash("sha256").update(buf).digest("hex");
998
+ const sha = createHash2("sha256").update(buf).digest("hex");
989
999
  const post = (body) => api(`/api/boxes/${name}/home`, { method: "POST", body: JSON.stringify(body) });
990
1000
  if ((await post({ sha }).catch(() => ({ need: false }))).need) await post({ sha, tgz: buf.toString("base64") }).catch(() => void 0);
991
1001
  }
@@ -1282,6 +1292,16 @@ async function ls() {
1282
1292
  console.log(`${b.name.padEnd(width)}${l}${" ".repeat(Math.max(1, 20 - l.replace(/\x1b\[[0-9;]*m/g, "").length))}${dim(b.agent === "local" ? "laptop" : "remote")}`);
1283
1293
  }
1284
1294
  }
1295
+ async function setup() {
1296
+ const sent = await sendSetup();
1297
+ const n = (k, word) => `${k} ${word}${k === 1 ? "" : "s"}`;
1298
+ ok(`Sent ${[n(sent.skills, "skill"), n(sent.plugins, "plugin"), ...sent.memory ? ["CLAUDE.md"] : []].join(", ")} ${dim("\xB7 @Box and webhooks start boxes with these")}`);
1299
+ if (sent.tooBig) miss("Your skills and plugins are over 30 MB, so only CLAUDE.md and the plugin list were sent.");
1300
+ const { data } = await api("/api/accounts");
1301
+ const missing = ["claude", "github"].filter((id) => data.find((a) => a.id === id)?.status !== "ready").map((id) => id === "claude" ? "sign in to Claude" : "connect GitHub");
1302
+ if (missing.length) miss(`Before @Box can start boxes as you, ${missing.join(" and ")}: ${new URL("/boxes?new", load().server)}`);
1303
+ else ok("Claude and GitHub are connected");
1304
+ }
1285
1305
  var quiet = ["proxy", "sync"].includes(process.argv[2] ?? "");
1286
1306
  if (quiet) console.log = console.error;
1287
1307
  if (!quiet && !process.env.CBOX_UPDATED && !process.argv.slice(2).some((a) => ["-V", "--version", "-h", "--help", "help"].includes(a))) console.log(dim(`box ${VERSION}`));
@@ -1295,6 +1315,7 @@ boxOptions(cli.command("new").description("always make a fresh box")).action((o)
1295
1315
  cli.command("ls").description("list your boxes").action(ls);
1296
1316
  cli.command("code [env]").description("open an environment's boxes in one VS Code window, each a git worktree, and sync your saves").action((env) => code(env));
1297
1317
  cli.command("rm <box>").option("-f, --force", "delete even while someone is attached or Claude is working").description("delete a box, pushing unpushed work to cb/<box>").action(rm);
1318
+ cli.command("setup").description("send your skills, plugins and CLAUDE.md for @Box and webhooks, and check Claude and GitHub").action(setup);
1298
1319
  cli.command("login").option("--server <url>", "box server").description("sign in from the browser").action((o) => login(o.server));
1299
1320
  cli.command("upgrade").description("update box to the latest version now").action(upgrade);
1300
1321
  cli.command("attach [box]", { hidden: true }).action((name) => name ? openBox(name) : home(void 0, {}));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abuaboud/box",
3
- "version": "0.7.9",
3
+ "version": "0.7.11",
4
4
  "description": "box: Claude Code on your laptop, the work on a cloud dev machine you reach as <name>.box, with your team databases through MCP.",
5
5
  "type": "module",
6
6
  "bin": {