cubefarm 0.3.0 → 0.3.2

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/dist/index.html CHANGED
@@ -8,7 +8,7 @@
8
8
  <link rel="preconnect" href="https://fonts.googleapis.com" />
9
9
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
10
10
  <link href="https://fonts.googleapis.com/css2?family=Fredoka:wght@400;500;600;700&family=JetBrains+Mono:wght@400;600&display=swap" rel="stylesheet" />
11
- <script type="module" crossorigin src="/assets/index-CDs_MaRq.js"></script>
11
+ <script type="module" crossorigin src="/assets/index-BCFM-9fU.js"></script>
12
12
  <link rel="stylesheet" crossorigin href="/assets/index-BU33ZUhQ.css">
13
13
  </head>
14
14
  <body>
@@ -602,7 +602,7 @@ var clip = (s, n) => s.length > n ? `${s.slice(0, n - 1)}\u2026` : s;
602
602
  function rel(cwd, p) {
603
603
  const s = String(p ?? "");
604
604
  if (!s) return "";
605
- const r = path3.relative(cwd, s);
605
+ const r = path3.relative(cwd, path3.resolve(cwd, s));
606
606
  return r && !r.startsWith("..") ? r.replaceAll("\\", "/") : s;
607
607
  }
608
608
  var escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -1068,6 +1068,26 @@ function launchArgs(id, ctx) {
1068
1068
  }
1069
1069
  }
1070
1070
  }
1071
+ function oneAtATime() {
1072
+ const tails = /* @__PURE__ */ new Map();
1073
+ return (key, step) => {
1074
+ const next = (tails.get(key) ?? Promise.resolve()).then(step).then(
1075
+ () => void 0,
1076
+ () => void 0
1077
+ );
1078
+ tails.set(key, next);
1079
+ void next.then(() => tails.get(key) === next && tails.delete(key));
1080
+ return next;
1081
+ };
1082
+ }
1083
+ var threadSteps = oneAtATime();
1084
+ function codexThread(action, id, after) {
1085
+ return threadSteps(id, async () => {
1086
+ await after;
1087
+ const cmd = commandFor("codex");
1088
+ if (cmd) await run(cmd.file, [...cmd.args, action, id], { timeoutMs: 3e4 });
1089
+ });
1090
+ }
1071
1091
  var TRUST_PROMPT = /Quick safety check|Do you trust the (files|contents) (in|of) this|trust this folder|allow Codex to work in this folder/i;
1072
1092
  function trustKey(screen) {
1073
1093
  if (!TRUST_PROMPT.test(screen)) return null;
@@ -1360,7 +1380,7 @@ function spawnPty(file, args, o, meta) {
1360
1380
  return p2;
1361
1381
  }
1362
1382
  if (!nodePty) throw new Error("the terminal module could not be loaded on this machine");
1363
- const p = nodePty.spawn(file, args, { name: "xterm-256color", cols: o.cols, rows: o.rows, cwd: o.cwd, env: o.env });
1383
+ const p = nodePty.spawn(file, args, { name: "xterm-256color", cols: o.cols, rows: o.rows, cwd: o.cwd, env: o.env, useConptyDll: true });
1364
1384
  return {
1365
1385
  get pid() {
1366
1386
  return p.pid;
@@ -1486,17 +1506,23 @@ var hooksHinted = false;
1486
1506
  var IDLE_MS = 30 * 6e4;
1487
1507
  var ALLOW = { hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "allow", permissionDecisionReason: "Approved by cubefarm" } };
1488
1508
  var toolInput = (b) => b.tool_input && typeof b.tool_input === "object" ? b.tool_input : {};
1509
+ var exitOf = (p) => new Promise((resolve) => {
1510
+ p.onExit(() => resolve());
1511
+ setTimeout(resolve, 5e3);
1512
+ });
1489
1513
  function endLive(live) {
1490
- if (lives.get(live.term) !== live) return;
1514
+ if (lives.get(live.term) !== live) return Promise.resolve();
1491
1515
  lives.delete(live.term);
1492
1516
  clearTimeout(live.idleTimer);
1493
1517
  routes.delete(live.token);
1494
1518
  live.term.releaseIdle = null;
1495
1519
  live.term.bind(null);
1520
+ const gone = live.proc ? exitOf(live.proc) : void 0;
1496
1521
  live.proc?.kill();
1497
1522
  live.proc = null;
1498
1523
  live.term.note(`\u2500\u2500 ${cliLabel(live.cli)} session ended \u2500\u2500`);
1499
1524
  fs6.rm(live.dir, { recursive: true, force: true, maxRetries: 5 }, () => void 0);
1525
+ return live.cli === "codex" && live.resumeId ? codexThread("archive", live.resumeId, gone) : Promise.resolve();
1500
1526
  }
1501
1527
  var metaOf = (l) => ({ agentId: l.agentId, cli: l.cli, token: l.token, dir: l.dir, resumeId: l.resumeId, busy: !!l.session });
1502
1528
  var remember = (l) => l.proc?.setMeta(metaOf(l));
@@ -1529,6 +1555,7 @@ async function reconnectClis(terminalFor) {
1529
1555
  const term = meta.agentId ? terminalFor(meta.agentId) : null;
1530
1556
  if (h.exit !== null || !term || lives.has(term) || !isCli(meta.cli) || !meta.token || !meta.dir || !meta.agentId) {
1531
1557
  discardPty(h);
1558
+ if (meta.cli === "codex" && meta.resumeId) void codexThread("archive", meta.resumeId, h.exit === null ? new Promise((r) => setTimeout(r, 5e3)) : void 0);
1532
1559
  continue;
1533
1560
  }
1534
1561
  const l = { agentId: meta.agentId, cli: meta.cli, term, proc: adoptPty(h), token: meta.token, dir: meta.dir, resumeId: meta.resumeId ?? null, statusLine: "\u{1F3E2} cubefarm", shots: /* @__PURE__ */ new Set(), session: null };
@@ -1543,8 +1570,8 @@ function officeProcesses() {
1543
1570
  const pids = [keeperPid(), ...[...lives.values()].map((l) => l.proc?.pid ?? 0)].filter((p) => p > 0);
1544
1571
  return { pids, markers: [SESSIONS_DIR, BIN_DIR] };
1545
1572
  }
1546
- function releaseClis(restart) {
1547
- if (!restart) for (const l of [...lives.values()]) endLive(l);
1573
+ async function releaseClis(restart) {
1574
+ if (!restart) await Promise.all([...lives.values()].map(endLive));
1548
1575
  leaveKeeper();
1549
1576
  }
1550
1577
  function idleHook(live, b) {
@@ -1707,7 +1734,7 @@ function startCliSession(opts, callbacks) {
1707
1734
  if (sub) return {};
1708
1735
  busy();
1709
1736
  const prompt2 = String(b.prompt ?? "").trim();
1710
- const i = officePrompts.findIndex((p2) => p2.slice(0, 60) === prompt2.slice(0, 60));
1737
+ const i = officePrompts.findIndex((p) => p.slice(0, 60) === prompt2.slice(0, 60));
1711
1738
  if (i >= 0) officePrompts.splice(i, 1);
1712
1739
  else if (prompt2) log([{ kind: "manager", text: `\u25B6 Typed in the terminal: ${clip(prompt2.split("\n")[0], 200)}` }]);
1713
1740
  return {};
@@ -1743,7 +1770,7 @@ function startCliSession(opts, callbacks) {
1743
1770
  const thread = typeof b.session_id === "string" ? b.session_id : "";
1744
1771
  if (cli === "codex" && thread !== mainThread) {
1745
1772
  const input = String(b.input ?? "").trim();
1746
- if (mainThread || !launched.some((p2) => input.startsWith(p2.slice(0, 40)))) return {};
1773
+ if (mainThread || !launched.some((p) => input.startsWith(p.slice(0, 40)))) return {};
1747
1774
  mainThread = thread;
1748
1775
  }
1749
1776
  if (thread) {
@@ -1875,46 +1902,51 @@ ${JSON.stringify(opts.outputSchema)}`;
1875
1902
  term.note(`\u2500\u2500 ${label}${opts.label ? ` \xB7 ${opts.label}` : ""} \u2500\u2500`);
1876
1903
  const resumeId = cli === "claude" ? sessionId : opts.resumeSessionId ?? null;
1877
1904
  const l = { agentId: opts.agentId ?? "", cli, term, proc: null, token, dir, resumeId, statusLine, shots: /* @__PURE__ */ new Set(), session: { hook, exited } };
1878
- let p;
1879
- try {
1880
- p = spawnPty(cmd.file, [...cmd.args, ...launch2.args], { cols: term.cols, rows: term.rows, cwd: opts.cwd, env }, metaOf(l));
1881
- } catch (err) {
1882
- setTimeout(() => finish({ ok: false, text: "", errors: [`Could not start ${label}: ${err.message}`] }), 0);
1883
- return handle;
1884
- }
1885
- l.proc = p;
1886
- live = l;
1887
- wire(l, opts.office);
1888
- shotTimer = setInterval(collectShots, 2e3);
1889
- screenTimer = setInterval(() => {
1890
- if (done || Date.now() - started > BOOT_MS * 2) return clearInterval(screenTimer);
1891
- const screen = term.screen();
1892
- const review = hookReviewKey(screen);
1893
- const key = trustKeys < 12 && Date.now() - trustedAt > 3e3 ? trustKey(screen) ?? review : null;
1894
- if (key === "down") {
1895
- trustKeys += 1;
1896
- quietly(() => p.write(term.appCursor ? "\x1BOB" : "\x1B[B"));
1897
- } else if (key === "enter") {
1898
- trustKeys += 1;
1899
- trustedAt = Date.now();
1900
- if (!review) log([{ kind: "system", text: `\u2713 Trusted the worktree for ${label}.` }]);
1901
- else if (!hooksHinted) {
1902
- hooksHinted = true;
1903
- log([{ kind: "system", text: `\u2139 To see ${label}'s steps here, trust the office's hooks once: in this terminal, type /hooks and press t. Until then the office shows its task.` }]);
1905
+ const begin = () => {
1906
+ if (done) return;
1907
+ let p;
1908
+ try {
1909
+ p = spawnPty(cmd.file, [...cmd.args, ...launch2.args], { cols: term.cols, rows: term.rows, cwd: opts.cwd, env }, metaOf(l));
1910
+ } catch (err) {
1911
+ setTimeout(() => finish({ ok: false, text: "", errors: [`Could not start ${label}: ${err.message}`] }), 0);
1912
+ return;
1913
+ }
1914
+ l.proc = p;
1915
+ live = l;
1916
+ wire(l, opts.office);
1917
+ shotTimer = setInterval(collectShots, 2e3);
1918
+ screenTimer = setInterval(() => {
1919
+ if (done || Date.now() - started > BOOT_MS * 2) return clearInterval(screenTimer);
1920
+ const screen = term.screen();
1921
+ const review = hookReviewKey(screen);
1922
+ const key = trustKeys < 12 && Date.now() - trustedAt > 3e3 ? trustKey(screen) ?? review : null;
1923
+ if (key === "down") {
1924
+ trustKeys += 1;
1925
+ quietly(() => p.write(term.appCursor ? "\x1BOB" : "\x1B[B"));
1926
+ } else if (key === "enter") {
1927
+ trustKeys += 1;
1928
+ trustedAt = Date.now();
1929
+ if (!review) log([{ kind: "system", text: `\u2713 Trusted the worktree for ${label}.` }]);
1930
+ else if (!hooksHinted) {
1931
+ hooksHinted = true;
1932
+ log([{ kind: "system", text: `\u2139 To see ${label}'s steps here, trust the office's hooks once: in this terminal, type /hooks and press t. Until then the office shows its task.` }]);
1933
+ }
1934
+ quietly(() => p.write("\r"));
1935
+ }
1936
+ if (!nudged.has("login") && /Not logged in|run \/login|Select login method|Sign in with ChatGPT|Please login|Invalid API key/i.test(screen)) {
1937
+ nudged.add("login");
1938
+ log([{ kind: "error", text: `\u26A0 ${label} needs you to sign in: open the terminal and sign in there, or run "${cli}" in a terminal of your own.` }]);
1939
+ } else if (!nudged.has("setup") && !trustKey(screen) && Date.now() - trustedAt > 5e3 && /Choose the text style|Press Enter to continue|Bypass Permissions mode|Settings Error/i.test(screen)) {
1940
+ nudged.add("setup");
1941
+ log([{ kind: "error", text: `\u26A0 ${label} is waiting on a setup screen: open the terminal to answer it.` }]);
1904
1942
  }
1905
- quietly(() => p.write("\r"));
1906
- }
1907
- if (!nudged.has("login") && /Not logged in|run \/login|Select login method|Sign in with ChatGPT|Please login|Invalid API key/i.test(screen)) {
1908
- nudged.add("login");
1909
- log([{ kind: "error", text: `\u26A0 ${label} needs you to sign in: open the terminal and sign in there, or run "${cli}" in a terminal of your own.` }]);
1910
- } else if (!nudged.has("setup") && !trustKey(screen) && Date.now() - trustedAt > 5e3 && /Choose the text style|Press Enter to continue|Bypass Permissions mode|Settings Error/i.test(screen)) {
1911
- nudged.add("setup");
1912
- log([{ kind: "error", text: `\u26A0 ${label} is waiting on a setup screen: open the terminal to answer it.` }]);
1913
- }
1914
- }, 1e3);
1915
- bootTimer = setTimeout(() => {
1916
- if (!done && !begun && cli === "claude") log([{ kind: "error", text: `\u26A0 ${label} hasn't started on the task yet. Open the terminal to see what it's waiting for.` }]);
1917
- }, BOOT_MS);
1943
+ }, 1e3);
1944
+ bootTimer = setTimeout(() => {
1945
+ if (!done && !begun && cli === "claude") log([{ kind: "error", text: `\u26A0 ${label} hasn't started on the task yet. Open the terminal to see what it's waiting for.` }]);
1946
+ }, BOOT_MS);
1947
+ };
1948
+ if (cli === "codex" && opts.resumeSessionId) void codexThread("unarchive", opts.resumeSessionId).then(begin);
1949
+ else begin();
1918
1950
  return handle;
1919
1951
  }
1920
1952
 
@@ -2673,7 +2705,7 @@ Closes #${pr.closesIssues[0] ?? "?"}`,
2673
2705
  reconnectClis: async () => [],
2674
2706
  // fake sessions end with the office
2675
2707
  hooksReady: () => void 0,
2676
- releaseClis: () => void 0,
2708
+ releaseClis: async () => void 0,
2677
2709
  detectClis: async () => CLIS.map((c) => ({ id: c.id, label: c.label, installed: true, version: "demo", integrated: c.integrated })),
2678
2710
  previews: demoPreviews,
2679
2711
  office: demoOffice
@@ -4717,7 +4749,7 @@ var Swarm = class {
4717
4749
  */
4718
4750
  async shutdown(restart = false) {
4719
4751
  await this.writeState().catch((err) => console.warn("could not save the state", err));
4720
- this.backend.releaseClis(restart);
4752
+ await this.backend.releaseClis(restart);
4721
4753
  await this.saveTerminals(true);
4722
4754
  await this.previews.stopAll(this.state.repos);
4723
4755
  }