codeam-cli 2.69.0 → 2.70.0

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 (3) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/index.js +448 -113
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,12 @@ All notable changes to `codeam-cli` are documented here.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [2.69.0] — 2026-08-29
8
+
9
+ ### Added
10
+
11
+ - **cli:** El inspector habla tambien dentro del WebView de la app movil (#666)
12
+
7
13
  ## [2.68.0] — 2026-08-29
8
14
 
9
15
  ### Added
package/dist/index.js CHANGED
@@ -271,8 +271,8 @@ var init_stdio_proxy = __esm({
271
271
  }
272
272
  async spawnChild(stdout, preResolved) {
273
273
  const spec = preResolved ?? await this.opts.spawnSpec();
274
- const spawn46 = this.opts.spawnImpl ?? import_node_child_process33.spawn;
275
- const child = spawn46(spec.command, spec.args, {
274
+ const spawn47 = this.opts.spawnImpl ?? import_node_child_process33.spawn;
275
+ const child = spawn47(spec.command, spec.args, {
276
276
  env: { ...process.env, ...spec.env },
277
277
  // env only — never argv
278
278
  stdio: ["pipe", "pipe", "inherit"]
@@ -8081,7 +8081,7 @@ function readAnonId() {
8081
8081
  }
8082
8082
  function superProperties() {
8083
8083
  return {
8084
- cliVersion: true ? "2.69.0" : "0.0.0-dev",
8084
+ cliVersion: true ? "2.70.0" : "0.0.0-dev",
8085
8085
  nodeVersion: process.version,
8086
8086
  platform: process.platform,
8087
8087
  arch: process.arch,
@@ -8323,7 +8323,7 @@ var http = __toESM(require("http"));
8323
8323
  // package.json
8324
8324
  var package_default = {
8325
8325
  name: "codeam-cli",
8326
- version: "2.69.0",
8326
+ version: "2.70.0",
8327
8327
  description: "Workflow-continuity bridge for AI coding agents. Wrap Claude Code or Codex in a PTY and supervise, approve, and redirect the session from any device \u2014 async. The terminal companion for CodeAgent Mobile.",
8328
8328
  type: "commonjs",
8329
8329
  main: "dist/index.js",
@@ -9859,7 +9859,7 @@ var CommandRelayService = class _CommandRelayService {
9859
9859
  // fresh + clear the "CLI update available" banner after a self-update
9860
9860
  // (a codespace that reinstalls @latest reconnects via heartbeat, not
9861
9861
  // pair/reconnect). Older backends ignore the extra field.
9862
- ..."2.69.0" ? { ideVersion: "2.69.0" } : {}
9862
+ ..."2.70.0" ? { ideVersion: "2.70.0" } : {}
9863
9863
  }).then(() => log.trace("relay", `heartbeat ok online=${online}`)).catch((err) => log.trace("relay", `heartbeat failed online=${online}`, err));
9864
9864
  }
9865
9865
  /**
@@ -11270,7 +11270,7 @@ var fs65 = __toESM(require("fs"));
11270
11270
  var os54 = __toESM(require("os"));
11271
11271
  var path71 = __toESM(require("path"));
11272
11272
  var import_crypto3 = require("crypto");
11273
- var import_child_process27 = require("child_process");
11273
+ var import_child_process28 = require("child_process");
11274
11274
 
11275
11275
  // src/lib/payload.ts
11276
11276
  var import_zod = require("zod");
@@ -14062,7 +14062,7 @@ function subscribeToPairCompletion(pluginId, onPaired, onTimeout, pollSecret) {
14062
14062
  onPaired(info);
14063
14063
  };
14064
14064
  dispatchers.add(dispatch);
14065
- const connect3 = () => {
14065
+ const connect4 = () => {
14066
14066
  if (stopped) return;
14067
14067
  const url2 = new URL(`${API_BASE5}/api/commands/pending/stream`);
14068
14068
  url2.searchParams.set("pluginId", pluginId);
@@ -14088,7 +14088,7 @@ function subscribeToPairCompletion(pluginId, onPaired, onTimeout, pollSecret) {
14088
14088
  `sse status=${res.statusCode}; will reconnect in 1s`
14089
14089
  );
14090
14090
  res.resume();
14091
- setTimeout(connect3, 1e3);
14091
+ setTimeout(connect4, 1e3);
14092
14092
  return;
14093
14093
  }
14094
14094
  let buf = "";
@@ -14103,23 +14103,23 @@ function subscribeToPairCompletion(pluginId, onPaired, onTimeout, pollSecret) {
14103
14103
  }
14104
14104
  });
14105
14105
  res.on("end", () => {
14106
- if (!stopped) setTimeout(connect3, 250);
14106
+ if (!stopped) setTimeout(connect4, 250);
14107
14107
  });
14108
14108
  res.on("error", () => {
14109
- if (!stopped) setTimeout(connect3, 1e3);
14109
+ if (!stopped) setTimeout(connect4, 1e3);
14110
14110
  });
14111
14111
  }
14112
14112
  );
14113
14113
  req.on("error", () => {
14114
- if (!stopped) setTimeout(connect3, 1e3);
14114
+ if (!stopped) setTimeout(connect4, 1e3);
14115
14115
  });
14116
14116
  req.on("timeout", () => {
14117
14117
  req?.destroy();
14118
- if (!stopped) setTimeout(connect3, 250);
14118
+ if (!stopped) setTimeout(connect4, 250);
14119
14119
  });
14120
14120
  req.end();
14121
14121
  };
14122
- connect3();
14122
+ connect4();
14123
14123
  timeoutTimer = setTimeout(() => {
14124
14124
  stop();
14125
14125
  onTimeout();
@@ -17074,7 +17074,7 @@ async function ensurePythonInstaller(runner) {
17074
17074
  return { ok: true, installer: { kind: "pip" } };
17075
17075
  }
17076
17076
  async function resolveHeadroomPython(runner) {
17077
- const PROBE_TIMEOUT_MS = 5e3;
17077
+ const PROBE_TIMEOUT_MS2 = 5e3;
17078
17078
  const SUFFIXES = ["python3.13", "python3.12", "python3.11", "python3.10"];
17079
17079
  const PREFIX_DIRS = ["/opt/homebrew/bin", "/usr/local/bin"];
17080
17080
  const probe = async (candidate) => {
@@ -17082,7 +17082,7 @@ async function resolveHeadroomPython(runner) {
17082
17082
  const r = await runner.run(
17083
17083
  candidate,
17084
17084
  ["-c", 'import sys; print("%d.%d" % sys.version_info[:2])'],
17085
- { timeoutMs: PROBE_TIMEOUT_MS }
17085
+ { timeoutMs: PROBE_TIMEOUT_MS2 }
17086
17086
  );
17087
17087
  if (r.code !== 0) return false;
17088
17088
  const parts = (r.stdout ?? "").trim().split(".");
@@ -17090,7 +17090,7 @@ async function resolveHeadroomPython(runner) {
17090
17090
  const minor = parseInt(parts[1] ?? "", 10);
17091
17091
  if (!(major === 3 && minor >= 10)) return false;
17092
17092
  const pipCheck = await runner.run(candidate, ["-m", "pip", "--version"], {
17093
- timeoutMs: PROBE_TIMEOUT_MS
17093
+ timeoutMs: PROBE_TIMEOUT_MS2
17094
17094
  });
17095
17095
  return pipCheck.code === 0;
17096
17096
  } catch {
@@ -22118,7 +22118,7 @@ async function autoUpgradeBeforeCriticalCommand() {
22118
22118
  if (process.env.NODE_ENV === "test") return;
22119
22119
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
22120
22120
  if (process.env.CI) return;
22121
- const current2 = true ? "2.69.0" : null;
22121
+ const current2 = true ? "2.70.0" : null;
22122
22122
  if (!current2) return;
22123
22123
  const cache = readCache();
22124
22124
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -22135,7 +22135,7 @@ function checkForUpdates() {
22135
22135
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
22136
22136
  if (process.env.CI) return;
22137
22137
  if (!process.stdout.isTTY) return;
22138
- const current2 = true ? "2.69.0" : null;
22138
+ const current2 = true ? "2.70.0" : null;
22139
22139
  if (!current2) return;
22140
22140
  const cache = readCache();
22141
22141
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -22158,7 +22158,7 @@ var SELF_UPDATE_INSTALL_TIMEOUT_MS = 18e4;
22158
22158
  var SELF_UPDATE_LS_TIMEOUT_MS = 15e3;
22159
22159
  var SUDO_PREFLIGHT_TIMEOUT_MS = 5e3;
22160
22160
  function currentCliVersion() {
22161
- return true ? "2.69.0" : null;
22161
+ return true ? "2.70.0" : null;
22162
22162
  }
22163
22163
  async function installedVersion(deps) {
22164
22164
  const ls = await deps.run(
@@ -24029,17 +24029,17 @@ async function defaultFetchJson(url2) {
24029
24029
  async function readUsageReport(deps = {}) {
24030
24030
  const port = deps.port ?? HEADROOM_PROXY_PORT;
24031
24031
  const base = `http://127.0.0.1:${port}`;
24032
- const get2 = deps.fetchJson ?? ((path95) => defaultFetchJson(`${base}${path95}`));
24032
+ const get3 = deps.fetchJson ?? ((path95) => defaultFetchJson(`${base}${path95}`));
24033
24033
  let history;
24034
24034
  try {
24035
- history = await get2("/stats-history");
24035
+ history = await get3("/stats-history");
24036
24036
  } catch (err) {
24037
24037
  log.info("headroomUsage", `stats-history unavailable: ${err.message}`);
24038
24038
  return null;
24039
24039
  }
24040
24040
  let proxyVersion;
24041
24041
  try {
24042
- const health = await get2("/health");
24042
+ const health = await get3("/health");
24043
24043
  if (health && typeof health.version === "string") proxyVersion = health.version;
24044
24044
  } catch {
24045
24045
  }
@@ -24380,7 +24380,18 @@ function isBuildHealSupported(framework) {
24380
24380
  function defaultWatchDir(dir, onEvent) {
24381
24381
  try {
24382
24382
  const w3 = fs52.watch(dir, { persistent: false }, (_eventType, filename) => onEvent(filename));
24383
- return () => w3.close();
24383
+ w3.on("error", () => {
24384
+ try {
24385
+ w3.close();
24386
+ } catch {
24387
+ }
24388
+ });
24389
+ return () => {
24390
+ try {
24391
+ w3.close();
24392
+ } catch {
24393
+ }
24394
+ };
24384
24395
  } catch {
24385
24396
  return null;
24386
24397
  }
@@ -24798,11 +24809,78 @@ async function waitForPortListening(port, opts) {
24798
24809
  var import_fs4 = require("fs");
24799
24810
  var import_path7 = __toESM(require("path"));
24800
24811
 
24801
- // src/services/preview/run-setup.ts
24812
+ // src/services/preview/embedded-postgres.ts
24802
24813
  var import_child_process20 = require("child_process");
24814
+ var net3 = __toESM(require("net"));
24815
+ var BOOT = `
24816
+ const { PGlite } = require('@electric-sql/pglite');
24817
+ const { PGLiteSocketServer } = require('@electric-sql/pglite-socket');
24818
+ (async () => {
24819
+ // 'memory://' \u2014 ef\xEDmero por dise\xF1o; ver el comentario del m\xF3dulo.
24820
+ const db = await PGlite.create('memory://');
24821
+ const server = new PGLiteSocketServer({ db, port: PORT, host: '127.0.0.1' });
24822
+ await server.start();
24823
+ process.on('SIGTERM', async () => { await server.stop(); process.exit(0); });
24824
+ })().catch((e) => { console.error(String(e && e.message)); process.exit(1); });
24825
+ `;
24826
+ function isListening(port) {
24827
+ return new Promise((resolve10) => {
24828
+ const s = net3.connect({ host: "127.0.0.1", port }, () => {
24829
+ s.destroy();
24830
+ resolve10(true);
24831
+ }).on("error", () => resolve10(false));
24832
+ s.setTimeout(1e3, () => {
24833
+ s.destroy();
24834
+ resolve10(false);
24835
+ });
24836
+ });
24837
+ }
24838
+ var DEFAULT_PORT = 5432;
24839
+ async function startEmbeddedPostgres(opts = {}) {
24840
+ const port = opts.port ?? DEFAULT_PORT;
24841
+ const probe = opts.probe ?? isListening;
24842
+ const log2 = opts.log ?? (() => void 0);
24843
+ if (await probe(port)) {
24844
+ log2(`embedded postgres skipped \u2014 port ${port} already serving`);
24845
+ return null;
24846
+ }
24847
+ const child = (opts.spawnFn ?? import_child_process20.spawn)(
24848
+ process.execPath,
24849
+ ["-e", `const PORT=${port};${BOOT}`],
24850
+ { stdio: ["ignore", "pipe", "pipe"], detached: false }
24851
+ );
24852
+ let failure = "";
24853
+ child.stderr?.on("data", (d3) => {
24854
+ failure += String(d3).slice(0, 400);
24855
+ });
24856
+ const deadline = Date.now() + (opts.readyTimeoutMs ?? 2e4);
24857
+ while (Date.now() < deadline) {
24858
+ if (child.exitCode !== null) {
24859
+ log2(`embedded postgres unavailable (${failure.trim() || "exited"})`);
24860
+ return null;
24861
+ }
24862
+ if (await probe(port)) {
24863
+ log2(`embedded postgres serving on ${port}`);
24864
+ return { port, stop: () => stopChild(child) };
24865
+ }
24866
+ await new Promise((r) => setTimeout(r, 250));
24867
+ }
24868
+ log2("embedded postgres did not become ready in time");
24869
+ stopChild(child);
24870
+ return null;
24871
+ }
24872
+ function stopChild(child) {
24873
+ try {
24874
+ child.kill("SIGTERM");
24875
+ } catch {
24876
+ }
24877
+ }
24878
+
24879
+ // src/services/preview/run-setup.ts
24880
+ var import_child_process21 = require("child_process");
24803
24881
  function runSetupCommand(cmd, args2, cwd, env, opts) {
24804
24882
  return new Promise((resolve10) => {
24805
- const child = (0, import_child_process20.spawn)(cmd, args2, {
24883
+ const child = (0, import_child_process21.spawn)(cmd, args2, {
24806
24884
  cwd,
24807
24885
  env: { ...process.env, ...env ?? {} },
24808
24886
  stdio: ["ignore", "pipe", "pipe"]
@@ -24886,6 +24964,35 @@ var REDIS = {
24886
24964
  healthTest: ["CMD", "redis-cli", "ping"],
24887
24965
  envLines: ["REDIS_URL=redis://localhost:6379", "REDIS_HOST=localhost", "REDIS_PORT=6379"]
24888
24966
  };
24967
+ async function readPrismaProvider(cwd) {
24968
+ for (const rel of ["prisma/schema.prisma", "schema.prisma", "src/prisma/schema.prisma"]) {
24969
+ try {
24970
+ return prismaProvider(await import_fs4.promises.readFile(import_path7.default.join(cwd, rel), "utf8"));
24971
+ } catch {
24972
+ }
24973
+ }
24974
+ return null;
24975
+ }
24976
+ function prismaProvider(schema) {
24977
+ const m = /datasource\s+\w+\s*\{[^}]*?provider\s*=\s*"([^"]+)"/s.exec(schema);
24978
+ return m ? m[1] : null;
24979
+ }
24980
+ function serviceForPrismaProvider(provider) {
24981
+ switch (provider) {
24982
+ case "postgresql":
24983
+ case "postgres":
24984
+ return POSTGRES;
24985
+ case "mysql":
24986
+ // PlanetScale habla el protocolo de MySQL.
24987
+ case "planetscale":
24988
+ return MYSQL;
24989
+ case "mongodb":
24990
+ return MONGO;
24991
+ // `sqlite` es un FICHERO: no necesita contenedor y no es un hueco.
24992
+ default:
24993
+ return null;
24994
+ }
24995
+ }
24889
24996
  function detectServicesFromDeps(pkg) {
24890
24997
  const deps = /* @__PURE__ */ new Set([
24891
24998
  ...Object.keys(pkg.dependencies ?? {}),
@@ -24994,12 +25101,65 @@ function runDockerComposeUp(cwd, composeArgs) {
24994
25101
  { timeoutMs: 18e4 }
24995
25102
  );
24996
25103
  }
25104
+ var embeddedPg = null;
25105
+ function registerEmbeddedPostgres(pg) {
25106
+ embeddedPg?.stop();
25107
+ embeddedPg = pg;
25108
+ }
25109
+ function envVarFor(service) {
25110
+ const first = service.envLines[0] ?? "";
25111
+ return first.split("=")[0] || "DATABASE_URL";
25112
+ }
24997
25113
  async function provisionProjectDependencies(cwd) {
25114
+ let needed = [];
25115
+ const outcome = (reason) => ({
25116
+ reason,
25117
+ missing: reason === "ok" ? [] : needed.map((sv) => ({ service: sv.name, envVar: envVarFor(sv) }))
25118
+ });
24998
25119
  try {
24999
- const docker = await runSetupCommand("docker", ["info"], cwd, void 0, { timeoutMs: 15e3 });
25120
+ let docker = await runSetupCommand("docker", ["info"], cwd, void 0, {
25121
+ timeoutMs: 15e3
25122
+ });
25123
+ if (docker.status !== "ok") {
25124
+ log.info("provision", "docker not responding \u2014 trying to start the daemon");
25125
+ for (const argv of [
25126
+ ["-n", "systemctl", "start", "docker"],
25127
+ ["-n", "service", "docker", "start"]
25128
+ ]) {
25129
+ const started2 = await runSetupCommand("sudo", argv, cwd, void 0, {
25130
+ timeoutMs: 45e3
25131
+ });
25132
+ if (started2.status !== "ok") continue;
25133
+ docker = await runSetupCommand("docker", ["info"], cwd, void 0, {
25134
+ timeoutMs: 2e4
25135
+ });
25136
+ if (docker.status === "ok") {
25137
+ log.info("provision", `docker daemon started via ${argv[1]}`);
25138
+ break;
25139
+ }
25140
+ }
25141
+ }
25000
25142
  if (docker.status !== "ok") {
25001
25143
  log.info("provision", "docker not usable \u2014 skipping dependency provisioning");
25002
- return;
25144
+ const pkgForNeeds = await readPackageJson(cwd);
25145
+ needed = pkgForNeeds ? detectServicesFromDeps(pkgForNeeds) : [];
25146
+ const prismaNeed = serviceForPrismaProvider(await readPrismaProvider(cwd));
25147
+ if (prismaNeed && !needed.some((n) => n.name === prismaNeed.name)) {
25148
+ needed.push(prismaNeed);
25149
+ }
25150
+ const pgIndex = needed.findIndex((n) => n.name === "postgres");
25151
+ if (pgIndex !== -1) {
25152
+ const embedded = await startEmbeddedPostgres({
25153
+ port: 5432,
25154
+ log: (m) => log.info("provision", m)
25155
+ });
25156
+ if (embedded) {
25157
+ registerEmbeddedPostgres(embedded);
25158
+ await ensureEnvFile(cwd, [needed[pgIndex]]);
25159
+ needed.splice(pgIndex, 1);
25160
+ }
25161
+ }
25162
+ return outcome(needed.length === 0 ? "ok" : "no-docker");
25003
25163
  }
25004
25164
  const pkg = await readPackageJson(cwd);
25005
25165
  let started = false;
@@ -25012,6 +25172,11 @@ async function provisionProjectDependencies(cwd) {
25012
25172
  if (!started) log.warn("provision", `compose up \u2192 ${up.status} (non-fatal)`);
25013
25173
  } else if (pkg) {
25014
25174
  generated = detectServicesFromDeps(pkg);
25175
+ const prismaSvc = serviceForPrismaProvider(await readPrismaProvider(cwd));
25176
+ if (prismaSvc && !generated.some((g) => g.name === prismaSvc.name)) {
25177
+ generated.push(prismaSvc);
25178
+ }
25179
+ needed = generated;
25015
25180
  if (generated.length > 0) {
25016
25181
  const dir = import_path7.default.join(cwd, ".codeam", "provision");
25017
25182
  await import_fs4.promises.mkdir(dir, { recursive: true });
@@ -25031,8 +25196,10 @@ async function provisionProjectDependencies(cwd) {
25031
25196
  await ensureEnvFile(cwd, generated);
25032
25197
  if (started && pkg?.scripts) await runMigrationsIfPresent(cwd, pkg.scripts);
25033
25198
  log.info("provision", "project dependency provisioning complete");
25199
+ return outcome(needed.length > 0 && !started ? "compose-failed" : "ok");
25034
25200
  } catch (err) {
25035
25201
  log.warn("provision", "provisionProjectDependencies failed (non-fatal)", err);
25202
+ return outcome("unknown");
25036
25203
  }
25037
25204
  }
25038
25205
 
@@ -25100,6 +25267,51 @@ async function awaitTunnelRegistered(candidate, hostname3, deadlineMs) {
25100
25267
  }
25101
25268
  }
25102
25269
 
25270
+ // src/services/preview/adopt-port.ts
25271
+ var http6 = __toESM(require("http"));
25272
+ var DIRECTORY_LISTING_RE = /<title>\s*(?:Directory listing for|Index of)/i;
25273
+ var PROBE_TIMEOUT_MS = 2e3;
25274
+ var PROBE_BODY_LIMIT = 4 * 1024;
25275
+ function verdictFor(res) {
25276
+ if (!res) return { adopt: false, reason: "unreachable" };
25277
+ if (!res.contentType || !res.contentType.toLowerCase().includes("text/html")) {
25278
+ return { adopt: false, reason: "not-html" };
25279
+ }
25280
+ if (DIRECTORY_LISTING_RE.test(res.bodyStart)) {
25281
+ return { adopt: false, reason: "directory-listing" };
25282
+ }
25283
+ return { adopt: true };
25284
+ }
25285
+ var probePort = (port) => new Promise((resolve10) => {
25286
+ const req = http6.get(
25287
+ { host: "127.0.0.1", port, path: "/", timeout: PROBE_TIMEOUT_MS },
25288
+ (res) => {
25289
+ let body = "";
25290
+ res.setEncoding("utf8");
25291
+ res.on("data", (c2) => {
25292
+ if (body.length < PROBE_BODY_LIMIT) body += c2;
25293
+ });
25294
+ res.on(
25295
+ "end",
25296
+ () => resolve10({ contentType: res.headers["content-type"], bodyStart: body })
25297
+ );
25298
+ res.on("error", () => resolve10(null));
25299
+ }
25300
+ );
25301
+ req.on("timeout", () => {
25302
+ req.destroy();
25303
+ resolve10(null);
25304
+ });
25305
+ req.on("error", () => resolve10(null));
25306
+ });
25307
+ async function canAdoptPort(port, probe = probePort) {
25308
+ try {
25309
+ return verdictFor(await probe(port));
25310
+ } catch {
25311
+ return { adopt: false, reason: "unreachable" };
25312
+ }
25313
+ }
25314
+
25103
25315
  // src/services/preview/index.ts
25104
25316
  var activePreviews = /* @__PURE__ */ new Map();
25105
25317
  function registerPreview(sessionId, preview) {
@@ -25133,9 +25345,9 @@ async function killPreview(sessionId) {
25133
25345
  await preview.inspector?.close();
25134
25346
  } catch {
25135
25347
  }
25136
- killProcessTree(preview.devServer, "SIGTERM");
25348
+ if (preview.devServer) killProcessTree(preview.devServer, "SIGTERM");
25137
25349
  const sigkillTimer = setTimeout(() => {
25138
- killProcessTree(preview.devServer, "SIGKILL");
25350
+ if (preview.devServer) killProcessTree(preview.devServer, "SIGKILL");
25139
25351
  if (preview.tunnel) killProcessTree(preview.tunnel, "SIGKILL");
25140
25352
  }, 250);
25141
25353
  sigkillTimer.unref?.();
@@ -25151,7 +25363,7 @@ function activePreviewSessionIds() {
25151
25363
  }
25152
25364
 
25153
25365
  // src/services/preview/start-orchestrator.ts
25154
- var import_child_process22 = require("child_process");
25366
+ var import_child_process23 = require("child_process");
25155
25367
  var fs59 = __toESM(require("fs"));
25156
25368
  var path64 = __toESM(require("path"));
25157
25369
  var import_which2 = __toESM(require("which"));
@@ -25161,7 +25373,7 @@ var import_fs6 = require("fs");
25161
25373
  var path63 = __toESM(require("path"));
25162
25374
 
25163
25375
  // src/beads/project-key.ts
25164
- var import_child_process21 = require("child_process");
25376
+ var import_child_process22 = require("child_process");
25165
25377
  var crypto2 = __toESM(require("crypto"));
25166
25378
  var fs57 = __toESM(require("fs"));
25167
25379
  var path62 = __toESM(require("path"));
@@ -25208,7 +25420,7 @@ function findRepoRoot(cwd) {
25208
25420
  }
25209
25421
  var _execSeam3 = {
25210
25422
  exec: (file, args2, opts) => {
25211
- const out2 = (0, import_child_process21.execFileSync)(file, args2, opts);
25423
+ const out2 = (0, import_child_process22.execFileSync)(file, args2, opts);
25212
25424
  return typeof out2 === "string" ? out2 : out2.toString("utf8");
25213
25425
  },
25214
25426
  realpath: (p2) => fs57.realpathSync(p2)
@@ -25318,12 +25530,16 @@ async function resolveNamedTunnel(ctx, fetchTunnel) {
25318
25530
  }
25319
25531
 
25320
25532
  // src/services/preview/inspector-proxy.ts
25321
- var http6 = __toESM(require("http"));
25322
- var net3 = __toESM(require("net"));
25533
+ var http7 = __toESM(require("http"));
25534
+ var net4 = __toESM(require("net"));
25323
25535
  var INJECTION_SCAN_BUDGET_BYTES = 64 * 1024;
25324
25536
  var INJECTION_ANCHORS = ["</head>", "<body>", "</body>"];
25325
- function wantsHtml(accept) {
25326
- return typeof accept === "string" && accept.includes("text/html");
25537
+ function isNavigation(headers) {
25538
+ if (headers.secFetchDest !== void 0) {
25539
+ return headers.secFetchDest === "document" || headers.secFetchDest === "iframe";
25540
+ }
25541
+ if (headers.secFetchMode !== void 0) return headers.secFetchMode === "navigate";
25542
+ return typeof headers.accept === "string" && headers.accept.includes("text/html");
25327
25543
  }
25328
25544
  function isHtmlResponse(contentType) {
25329
25545
  return typeof contentType === "string" && contentType.toLowerCase().includes("text/html");
@@ -25340,11 +25556,16 @@ function injectAt(html, script) {
25340
25556
  async function startInspectorProxy(opts) {
25341
25557
  const targetHost = opts.targetHost ?? "127.0.0.1";
25342
25558
  const upgraded = /* @__PURE__ */ new Set();
25343
- const server = http6.createServer((req, res) => {
25344
- const inject = wantsHtml(req.headers.accept);
25559
+ const server = http7.createServer((req, res) => {
25560
+ const one = (v) => Array.isArray(v) ? v[0] : v;
25561
+ const inject = isNavigation({
25562
+ accept: one(req.headers.accept),
25563
+ secFetchDest: one(req.headers["sec-fetch-dest"]),
25564
+ secFetchMode: one(req.headers["sec-fetch-mode"])
25565
+ });
25345
25566
  const headers = { ...req.headers };
25346
25567
  if (inject) delete headers["accept-encoding"];
25347
- const upstream = http6.request(
25568
+ const upstream = http7.request(
25348
25569
  { host: targetHost, port: opts.targetPort, method: req.method, path: req.url, headers },
25349
25570
  (originRes) => {
25350
25571
  const outHeaders = { ...originRes.headers };
@@ -25391,7 +25612,7 @@ async function startInspectorProxy(opts) {
25391
25612
  });
25392
25613
  server.on("upgrade", (req, clientSocket, head) => {
25393
25614
  upgraded.add(clientSocket);
25394
- const upstream = net3.connect(opts.targetPort, targetHost, () => {
25615
+ const upstream = net4.connect(opts.targetPort, targetHost, () => {
25395
25616
  const lines = [`${req.method} ${req.url} HTTP/1.1`];
25396
25617
  for (const [k2, v] of Object.entries(req.headers)) {
25397
25618
  for (const one of Array.isArray(v) ? v : [v]) {
@@ -25588,6 +25809,77 @@ function inspectorClientSource(opts) {
25588
25809
  send('hover', { element: describe(el) });
25589
25810
  }
25590
25811
 
25812
+ // Captura, y se traga el evento: mientras el modo esta activo un clic
25813
+ // SELECCIONA, no navega. Sin esto, pulsar un enlace se llevaria el
25814
+ // preview a otra pagina en mitad del gesto.
25815
+ /**
25816
+ * Las marcas FIJADAS, dibujadas DENTRO de la pagina.
25817
+ *
25818
+ * \u26A0\uFE0F Antes las pintaba el dashboard, en una capa sobre el iframe. Como esa
25819
+ * capa vive fuera del documento, sus coordenadas son del VIEWPORT: al
25820
+ * hacer scroll dentro del preview las marcas se quedaban clavadas en la
25821
+ * pantalla mientras el elemento se iba (reportado el 2026-08-29).
25822
+ *
25823
+ * Aqui se anclan al DOCUMENTO (position:absolute mas el scroll sumado), asi
25824
+ * que acompa\xF1an al contenido igual que cualquier otro nodo de la pagina \u2014 y
25825
+ * ademas se recolocan si el layout cambia, que una capa externa no puede
25826
+ * saber.
25827
+ */
25828
+ var picked = [];
25829
+ var seq = 0;
25830
+ var marksHost = null;
25831
+
25832
+ function ensureMarksHost() {
25833
+ if (marksHost && marksHost.parentNode) return marksHost;
25834
+ marksHost = document.createElement('div');
25835
+ marksHost.setAttribute('data-codeam', 'marks');
25836
+ marksHost.style.cssText = 'position:absolute;left:0;top:0;width:0;height:0;z-index:2147483646';
25837
+ document.body.appendChild(marksHost);
25838
+ return marksHost;
25839
+ }
25840
+
25841
+ function redrawMarks() {
25842
+ if (picked.length === 0) {
25843
+ if (marksHost && marksHost.parentNode) marksHost.parentNode.removeChild(marksHost);
25844
+ marksHost = null;
25845
+ return;
25846
+ }
25847
+ var host = ensureMarksHost();
25848
+ host.innerHTML = '';
25849
+ for (var i = 0; i < picked.length; i++) {
25850
+ var r = picked[i].el.getBoundingClientRect();
25851
+ // Coordenadas de DOCUMENTO: es lo que hace que la marca viaje con el
25852
+ // contenido en vez de quedarse pegada a la pantalla.
25853
+ var top = r.top + (window.scrollY || window.pageYOffset || 0);
25854
+ var left = r.left + (window.scrollX || window.pageXOffset || 0);
25855
+
25856
+ var box = document.createElement('div');
25857
+ box.setAttribute('data-codeam', 'mark');
25858
+ box.style.cssText = [
25859
+ 'position:absolute', 'pointer-events:none', 'box-sizing:border-box',
25860
+ 'border:1.5px solid #a855f7', 'background:rgba(168,85,247,0.12)',
25861
+ 'border-radius:3px',
25862
+ 'left:' + left + 'px', 'top:' + top + 'px',
25863
+ 'width:' + r.width + 'px', 'height:' + r.height + 'px'
25864
+ ].join(';');
25865
+
25866
+ var badge = document.createElement('div');
25867
+ badge.setAttribute('data-codeam', 'mark-badge');
25868
+ badge.style.cssText = [
25869
+ 'position:absolute', 'left:-10px', 'top:-10px', 'width:20px', 'height:20px',
25870
+ 'border-radius:10px', 'background:#a855f7', 'color:#000',
25871
+ 'font:700 11px/20px ui-monospace,SFMono-Regular,Menlo,monospace',
25872
+ 'text-align:center', 'pointer-events:none'
25873
+ ].join(';');
25874
+ badge.textContent = String(i + 1);
25875
+
25876
+ box.appendChild(badge);
25877
+ host.appendChild(box);
25878
+ }
25879
+ }
25880
+
25881
+ function reposition() { if (picked.length) redrawMarks(); }
25882
+
25591
25883
  // Captura, y se traga el evento: mientras el modo esta activo un clic
25592
25884
  // SELECCIONA, no navega. Sin esto, pulsar un enlace se llevaria el
25593
25885
  // preview a otra pagina en mitad del gesto.
@@ -25597,7 +25889,30 @@ function inspectorClientSource(opts) {
25597
25889
  if (el && el.getAttribute && el.getAttribute('data-codeam')) return;
25598
25890
  e.preventDefault();
25599
25891
  e.stopPropagation();
25600
- if (el) send('pick', { element: describe(el) });
25892
+ if (!el) return;
25893
+ var id = 'p' + (++seq);
25894
+ picked.push({ id: id, el: el });
25895
+ redrawMarks();
25896
+ var described = describe(el);
25897
+ described.markId = id;
25898
+ send('pick', { element: described });
25899
+ }
25900
+
25901
+ /**
25902
+ * El dashboard manda la lista de ids que SIGUEN vivos, en su orden.
25903
+ *
25904
+ * Es la otra mitad de \xABla marca es un objeto\xBB: si el usuario borra un chip
25905
+ * en el composer, la marca tiene que desaparecer de la pagina \u2014 y las que
25906
+ * quedan renumerarse, porque lo que el usuario escribe (\xAB\u2460 se sale\xBB) mira
25907
+ * a estos numeros.
25908
+ */
25909
+ function syncMarks(ids) {
25910
+ var byId = {};
25911
+ for (var i = 0; i < picked.length; i++) byId[picked[i].id] = picked[i];
25912
+ var next = [];
25913
+ for (var j = 0; j < ids.length; j++) if (byId[ids[j]]) next.push(byId[ids[j]]);
25914
+ picked = next;
25915
+ redrawMarks();
25601
25916
  }
25602
25917
 
25603
25918
  function enable(origin) {
@@ -25606,6 +25921,9 @@ function inspectorClientSource(opts) {
25606
25921
  driver = origin;
25607
25922
  document.addEventListener('mousemove', onMove, true);
25608
25923
  document.addEventListener('click', onClick, true);
25924
+ // El scroll y el resize mueven los elementos; las marcas los siguen.
25925
+ window.addEventListener('scroll', reposition, true);
25926
+ window.addEventListener('resize', reposition);
25609
25927
  send('ready', {});
25610
25928
  }
25611
25929
 
@@ -25615,7 +25933,11 @@ function inspectorClientSource(opts) {
25615
25933
  current = null;
25616
25934
  document.removeEventListener('mousemove', onMove, true);
25617
25935
  document.removeEventListener('click', onClick, true);
25936
+ window.removeEventListener('scroll', reposition, true);
25937
+ window.removeEventListener('resize', reposition);
25618
25938
  removeOverlay();
25939
+ // Las marcas fijadas SI sobreviven a salir del modo: siguen siendo el
25940
+ // contexto del mensaje que el usuario esta escribiendo.
25619
25941
  driver = null;
25620
25942
  }
25621
25943
 
@@ -25625,6 +25947,7 @@ function inspectorClientSource(opts) {
25625
25947
  if (!data || data.source !== CHANNEL) return;
25626
25948
  if (data.type === 'enable') enable(e.origin);
25627
25949
  else if (data.type === 'disable') disable();
25950
+ else if (data.type === 'marks' && data.ids) syncMarks(data.ids);
25628
25951
  });
25629
25952
 
25630
25953
  /**
@@ -25772,7 +26095,7 @@ async function runPreviewStart(args2) {
25772
26095
  }
25773
26096
  };
25774
26097
  const existing = activePreviews.get(sessionId);
25775
- if (existing && existing.devServer.exitCode === null) {
26098
+ if (existing && existing.devServer?.exitCode !== 0 && existing.devServer?.exitCode == null) {
25776
26099
  log.info(
25777
26100
  "preview",
25778
26101
  `reusing running preview for session=${sessionId} url=${existing.url}`
@@ -25806,7 +26129,9 @@ async function runPreviewStart(args2) {
25806
26129
  detection,
25807
26130
  cwd: ctx.cwd
25808
26131
  });
25809
- recordPreviewPort(detection.port, dev.devServer.pid, sessionId, Date.now());
26132
+ if (dev.devServer) {
26133
+ recordPreviewPort(detection.port, dev.devServer.pid, sessionId, Date.now());
26134
+ }
25810
26135
  log.info("preview", `ready: ${detection.framework} at ${tun.url}`);
25811
26136
  emit2(USER_EVENTS.PREVIEW_READY, {
25812
26137
  url: tun.url,
@@ -25919,7 +26244,7 @@ async function startDevServer(ctx) {
25919
26244
  const { sessionId, detection, cwd, emit: emit2, emitProgress } = ctx;
25920
26245
  if (await isPortListening(detection.port)) {
25921
26246
  const raceExisting = activePreviews.get(sessionId);
25922
- if (raceExisting && raceExisting.devServer.exitCode === null) {
26247
+ if (raceExisting && raceExisting.devServer?.exitCode == null) {
25923
26248
  log.info(
25924
26249
  "preview",
25925
26250
  `port race: reusing running preview for session=${sessionId} url=${raceExisting.url}`
@@ -25935,7 +26260,7 @@ async function startDevServer(ctx) {
25935
26260
  if (raceExisting) {
25936
26261
  log.info(
25937
26262
  "preview",
25938
- `reclaiming stale preview holding port ${detection.port} for session=${sessionId} (exit=${raceExisting.devServer.exitCode})`
26263
+ `reclaiming stale preview holding port ${detection.port} for session=${sessionId} (exit=${raceExisting.devServer?.exitCode})`
25939
26264
  );
25940
26265
  await killPreview(sessionId);
25941
26266
  const freeDeadline = Date.now() + 4e3;
@@ -25963,9 +26288,18 @@ async function startDevServer(ctx) {
25963
26288
  return null;
25964
26289
  }
25965
26290
  } else {
26291
+ const verdict = await canAdoptPort(detection.port);
26292
+ if (verdict.adopt) {
26293
+ log.info(
26294
+ "preview",
26295
+ `adopting the dev server already serving on port ${detection.port}`
26296
+ );
26297
+ emitProgress("READY_DETECTED", `adopted the server already on port ${detection.port}`);
26298
+ return { devServer: null, expoUrlRef: { current: null } };
26299
+ }
25966
26300
  emit2(USER_EVENTS.PREVIEW_ERROR, {
25967
26301
  stage: "spawn",
25968
- message: `Port ${detection.port} is already in use by another process, so the dev server can't start there. Stop whatever is listening on port ${detection.port} and try the preview again.`
26302
+ message: verdict.reason === "directory-listing" ? `Port ${detection.port} is serving a directory listing, not your app \u2014 that's usually a leftover \`python -m http.server\`. Stop it and try the preview again.` : `Port ${detection.port} is already in use by something that isn't a dev server, so the preview can't start there. Stop whatever is listening on port ${detection.port} and try again.`
25969
26303
  });
25970
26304
  return null;
25971
26305
  }
@@ -25973,7 +26307,7 @@ async function startDevServer(ctx) {
25973
26307
  await applyPreviewHostAllow(cwd);
25974
26308
  const spawnable = normalizeDetectionForSpawn(detection, cwd);
25975
26309
  emitProgress("BOOT_SEQUENCE", `${spawnable.command} ${spawnable.args.join(" ")}`);
25976
- const devServer = (0, import_child_process22.spawn)(spawnable.command, spawnable.args, {
26310
+ const devServer = (0, import_child_process23.spawn)(spawnable.command, spawnable.args, {
25977
26311
  cwd,
25978
26312
  env: { ...process.env, ...spawnable.env ?? {} },
25979
26313
  stdio: ["ignore", "pipe", "pipe"],
@@ -26039,7 +26373,7 @@ async function establishTunnel(ctx, dev) {
26039
26373
  }
26040
26374
  const expoUrl = dev.expoUrlRef.current;
26041
26375
  if (!expoUrl) {
26042
- killProcessTree(devServer, "SIGTERM");
26376
+ if (devServer) killProcessTree(devServer, "SIGTERM");
26043
26377
  emit2(USER_EVENTS.PREVIEW_ERROR, {
26044
26378
  stage: "tunnel",
26045
26379
  message: "Expo did not report a tunnel URL."
@@ -26056,7 +26390,7 @@ async function establishTunnel(ctx, dev) {
26056
26390
  try {
26057
26391
  bin = await resolveCloudflared();
26058
26392
  } catch (e) {
26059
- killProcessTree(devServer, "SIGTERM");
26393
+ if (devServer) killProcessTree(devServer, "SIGTERM");
26060
26394
  emit2(USER_EVENTS.PREVIEW_ERROR, {
26061
26395
  stage: "tunnel",
26062
26396
  message: e.message
@@ -26110,7 +26444,7 @@ async function establishTunnel(ctx, dev) {
26110
26444
  `cloudflared quick tunnel (retry ${attempt}/${MAX_TUNNEL_ATTEMPTS})`
26111
26445
  );
26112
26446
  }
26113
- const candidate = (0, import_child_process22.spawn)(
26447
+ const candidate = (0, import_child_process23.spawn)(
26114
26448
  bin,
26115
26449
  ["tunnel", "--url", `http://localhost:${tunnelPort}`],
26116
26450
  { stdio: ["ignore", "pipe", "pipe"] }
@@ -26130,7 +26464,7 @@ async function establishTunnel(ctx, dev) {
26130
26464
  }
26131
26465
  }
26132
26466
  if (!parsedUrl) {
26133
- killProcessTree(devServer, "SIGTERM");
26467
+ if (devServer) killProcessTree(devServer, "SIGTERM");
26134
26468
  emit2(USER_EVENTS.PREVIEW_ERROR, {
26135
26469
  stage: "tunnel",
26136
26470
  message: `Tunnel did not become reachable after ${MAX_TUNNEL_ATTEMPTS} attempts (${lastTunnelErr}). Cloudflare Quick Tunnels occasionally fail to register \u2014 please retry.`
@@ -26141,7 +26475,7 @@ async function establishTunnel(ctx, dev) {
26141
26475
  }
26142
26476
 
26143
26477
  // src/beads/bd-adapter.ts
26144
- var import_child_process23 = require("child_process");
26478
+ var import_child_process24 = require("child_process");
26145
26479
  var fs60 = __toESM(require("fs"));
26146
26480
  var os48 = __toESM(require("os"));
26147
26481
  var path65 = __toESM(require("path"));
@@ -26195,7 +26529,7 @@ function _defaultSpawn(binaryPath, args2, opts) {
26195
26529
  return new Promise((resolve10) => {
26196
26530
  let proc;
26197
26531
  try {
26198
- proc = (0, import_child_process23.spawn)(binaryPath, args2, { cwd: opts.cwd, env: opts.env });
26532
+ proc = (0, import_child_process24.spawn)(binaryPath, args2, { cwd: opts.cwd, env: opts.env });
26199
26533
  } catch (err) {
26200
26534
  resolve10({ code: -1, stdout: "", stderr: err.message });
26201
26535
  return;
@@ -26352,13 +26686,13 @@ function coerceIssue(row, projectKey) {
26352
26686
  }
26353
26687
 
26354
26688
  // src/beads/provisioner.ts
26355
- var import_child_process26 = require("child_process");
26689
+ var import_child_process27 = require("child_process");
26356
26690
  var fs62 = __toESM(require("fs"));
26357
26691
  var os50 = __toESM(require("os"));
26358
26692
  var path67 = __toESM(require("path"));
26359
26693
 
26360
26694
  // src/beads/install-bd.ts
26361
- var import_child_process24 = require("child_process");
26695
+ var import_child_process25 = require("child_process");
26362
26696
  var INSTALL_SH_URL = "https://raw.githubusercontent.com/gastownhall/beads/main/scripts/install.sh";
26363
26697
  var INSTALL_PS1_URL = "https://raw.githubusercontent.com/gastownhall/beads/main/install.ps1";
26364
26698
  function resolveInstallStrategy(platform3) {
@@ -26389,7 +26723,7 @@ function _defaultInstallSpawn(strategy) {
26389
26723
  return new Promise((resolve10) => {
26390
26724
  let proc;
26391
26725
  try {
26392
- proc = (0, import_child_process24.spawn)(strategy.command, strategy.args, { env: process.env });
26726
+ proc = (0, import_child_process25.spawn)(strategy.command, strategy.args, { env: process.env });
26393
26727
  } catch (err) {
26394
26728
  resolve10({ ok: false, code: -1, stderr: err.message });
26395
26729
  return;
@@ -26419,7 +26753,7 @@ async function installBd(platform3 = process.platform) {
26419
26753
  }
26420
26754
 
26421
26755
  // src/beads/install-dolt.ts
26422
- var import_child_process25 = require("child_process");
26756
+ var import_child_process26 = require("child_process");
26423
26757
  var fs61 = __toESM(require("fs"));
26424
26758
  var os49 = __toESM(require("os"));
26425
26759
  var path66 = __toESM(require("path"));
@@ -26581,7 +26915,7 @@ function _defaultDoltInstallSpawn(strategy) {
26581
26915
  };
26582
26916
  let proc;
26583
26917
  try {
26584
- proc = (0, import_child_process25.spawn)(strategy.command, strategy.args, {
26918
+ proc = (0, import_child_process26.spawn)(strategy.command, strategy.args, {
26585
26919
  env: process.env,
26586
26920
  stdio: ["ignore", "pipe", "pipe"]
26587
26921
  });
@@ -26808,7 +27142,7 @@ function linkBdOntoPath(binaryPath) {
26808
27142
  log.info("beads", `linked bd onto PATH: ${linkPath} -> ${binaryPath}`);
26809
27143
  }
26810
27144
  function setGitBeadsRole() {
26811
- (0, import_child_process26.execFileSync)("git", ["config", "--global", "beads.role", "contributor"], {
27145
+ (0, import_child_process27.execFileSync)("git", ["config", "--global", "beads.role", "contributor"], {
26812
27146
  stdio: "ignore"
26813
27147
  });
26814
27148
  }
@@ -27663,7 +27997,7 @@ var sessionTerminated = async (ctx, cmd) => {
27663
27997
  }
27664
27998
  quiet(() => ctx.agent.kill());
27665
27999
  try {
27666
- const proc = (0, import_child_process27.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
28000
+ const proc = (0, import_child_process28.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
27667
28001
  detached: true,
27668
28002
  stdio: "ignore"
27669
28003
  });
@@ -27682,7 +28016,7 @@ var shutdownSession = async (ctx, cmd) => {
27682
28016
  quiet(() => ctx.agent.kill());
27683
28017
  if (ctx.keepAliveCtx.inCodespace && ctx.keepAliveCtx.codespaceName) {
27684
28018
  try {
27685
- const stopProc = (0, import_child_process27.spawn)(
28019
+ const stopProc = (0, import_child_process28.spawn)(
27686
28020
  "bash",
27687
28021
  ["-lc", `sleep 1; gh codespace stop -c ${JSON.stringify(ctx.keepAliveCtx.codespaceName)} >/dev/null 2>&1 || true`],
27688
28022
  { detached: true, stdio: "ignore" }
@@ -27692,7 +28026,7 @@ var shutdownSession = async (ctx, cmd) => {
27692
28026
  }
27693
28027
  }
27694
28028
  try {
27695
- const proc = (0, import_child_process27.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
28029
+ const proc = (0, import_child_process28.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
27696
28030
  detached: true,
27697
28031
  stdio: "ignore"
27698
28032
  });
@@ -28297,7 +28631,7 @@ var defaultCliUpdateDeps = {
28297
28631
  // Only a truly local `codeam start` (neither marker) re-execs in place below.
28298
28632
  isSupervised: () => process.env["CODEAM_AUTO_APPROVE"] === "1" || process.env["CODESPACES"] === "true",
28299
28633
  relaunch: (args2) => {
28300
- const child = (0, import_child_process27.spawnSync)("codeam", args2, { stdio: "inherit", env: process.env });
28634
+ const child = (0, import_child_process28.spawnSync)("codeam", args2, { stdio: "inherit", env: process.env });
28301
28635
  process.exit(child.status ?? 0);
28302
28636
  }
28303
28637
  };
@@ -28378,7 +28712,7 @@ async function runNpmInstallLatest() {
28378
28712
  const useShell = process.platform === "win32";
28379
28713
  const command2 = useShell && invocation.command.includes(" ") ? `"${invocation.command}"` : invocation.command;
28380
28714
  const result = await new Promise((resolve10) => {
28381
- (0, import_child_process27.execFile)(
28715
+ (0, import_child_process28.execFile)(
28382
28716
  command2,
28383
28717
  invocation.args,
28384
28718
  { timeout: CLI_UPDATE_INSTALL_TIMEOUT_MS, shell: useShell },
@@ -28804,7 +29138,7 @@ function makePreviewReaffirm(args2) {
28804
29138
  return makePreviewHeartbeatReaffirm({
28805
29139
  serving: () => {
28806
29140
  const active2 = activePreviews.get(sessionId);
28807
- if (!active2 || active2.devServer.exitCode !== null) return null;
29141
+ if (!active2 || active2.devServer && active2.devServer.exitCode !== null) return null;
28808
29142
  return {
28809
29143
  url: active2.url,
28810
29144
  framework: active2.framework,
@@ -29048,13 +29382,13 @@ async function dispatchCommand(ctx, cmd) {
29048
29382
  }
29049
29383
 
29050
29384
  // src/commands/start/keep-alive.ts
29051
- var import_child_process28 = require("child_process");
29385
+ var import_child_process29 = require("child_process");
29052
29386
  function buildKeepAlive(ctx) {
29053
29387
  let timer = null;
29054
29388
  async function setIdleTimeout(minutes) {
29055
29389
  if (!ctx.inCodespace || !ctx.codespaceName) return;
29056
29390
  await new Promise((resolve10) => {
29057
- const proc = (0, import_child_process28.spawn)(
29391
+ const proc = (0, import_child_process29.spawn)(
29058
29392
  "gh",
29059
29393
  [
29060
29394
  "api",
@@ -30227,7 +30561,7 @@ var fs67 = __toESM(require("fs"));
30227
30561
  var path74 = __toESM(require("path"));
30228
30562
  var os56 = __toESM(require("os"));
30229
30563
  var https7 = __toESM(require("https"));
30230
- var http7 = __toESM(require("http"));
30564
+ var http8 = __toESM(require("http"));
30231
30565
  var import_zod2 = require("zod");
30232
30566
  var historyRecordSchema = import_zod2.z.object({
30233
30567
  type: import_zod2.z.string().optional(),
@@ -30310,7 +30644,7 @@ function post(endpoint, body, pluginAuthToken) {
30310
30644
  return new Promise((resolve10) => {
30311
30645
  const payload = JSON.stringify(body);
30312
30646
  const u2 = new URL(`${API_BASE8}${endpoint}`);
30313
- const transport = u2.protocol === "https:" ? https7 : http7;
30647
+ const transport = u2.protocol === "https:" ? https7 : http8;
30314
30648
  const req = transport.request(
30315
30649
  {
30316
30650
  hostname: u2.hostname,
@@ -36036,12 +36370,12 @@ function buildRelaunchProxyEnv(baseEnv) {
36036
36370
  return env;
36037
36371
  }
36038
36372
  var relaunchProxyWithoutBudget = async () => {
36039
- const { spawn: spawn46 } = await import("child_process");
36373
+ const { spawn: spawn47 } = await import("child_process");
36040
36374
  killHeadroomProxy();
36041
36375
  await new Promise((r) => setTimeout(r, 500));
36042
36376
  const proxyEnv = buildRelaunchProxyEnv(process.env);
36043
36377
  try {
36044
- const proxy = spawn46(
36378
+ const proxy = spawn47(
36045
36379
  "headroom",
36046
36380
  ["proxy", "--port", "8787"],
36047
36381
  { stdio: "ignore", detached: true, env: proxyEnv }
@@ -36261,7 +36595,7 @@ async function performAgentSwitch(deps, rawAgentId, fastPath = {}) {
36261
36595
  }
36262
36596
 
36263
36597
  // src/services/streaming/transport.ts
36264
- var http8 = __toESM(require("http"));
36598
+ var http9 = __toESM(require("http"));
36265
36599
  var https8 = __toESM(require("https"));
36266
36600
  var _transport4 = {
36267
36601
  post: _post3,
@@ -36271,7 +36605,7 @@ function _post3(url2, headers, payload) {
36271
36605
  return new Promise((resolve10, reject) => {
36272
36606
  let settled = false;
36273
36607
  const u2 = new URL(url2);
36274
- const lib = u2.protocol === "https:" ? https8 : http8;
36608
+ const lib = u2.protocol === "https:" ? https8 : http9;
36275
36609
  const req = lib.request(
36276
36610
  {
36277
36611
  hostname: u2.hostname,
@@ -36313,7 +36647,7 @@ function _get(url2, headers) {
36313
36647
  return new Promise((resolve10, reject) => {
36314
36648
  let settled = false;
36315
36649
  const u2 = new URL(url2);
36316
- const lib = u2.protocol === "https:" ? https8 : http8;
36650
+ const lib = u2.protocol === "https:" ? https8 : http9;
36317
36651
  const req = lib.request(
36318
36652
  {
36319
36653
  hostname: u2.hostname,
@@ -36947,7 +37281,7 @@ function withholdTrailingPartialFenceMarker(text) {
36947
37281
  }
36948
37282
 
36949
37283
  // src/agents/acp/onboarding.ts
36950
- var import_child_process29 = require("child_process");
37284
+ var import_child_process30 = require("child_process");
36951
37285
  var fs69 = __toESM(require("fs"));
36952
37286
  var os59 = __toESM(require("os"));
36953
37287
  var path77 = __toESM(require("path"));
@@ -36968,7 +37302,7 @@ var _onboardingSeam = {
36968
37302
  */
36969
37303
  gitRemoteUrl: (cwd) => {
36970
37304
  try {
36971
- return (0, import_child_process29.execFileSync)("git", ["-C", cwd, "remote", "get-url", "origin"], {
37305
+ return (0, import_child_process30.execFileSync)("git", ["-C", cwd, "remote", "get-url", "origin"], {
36972
37306
  encoding: "utf8",
36973
37307
  stdio: ["ignore", "pipe", "ignore"],
36974
37308
  timeout: 2e3
@@ -37324,7 +37658,7 @@ function extractSelectPrompt(text) {
37324
37658
  var import_crypto5 = require("crypto");
37325
37659
 
37326
37660
  // src/services/turn-files/git-changeset.ts
37327
- var import_child_process30 = require("child_process");
37661
+ var import_child_process31 = require("child_process");
37328
37662
  var fs71 = __toESM(require("fs/promises"));
37329
37663
  var path79 = __toESM(require("path"));
37330
37664
 
@@ -37510,7 +37844,7 @@ function defaultRunGit(cwd, args2) {
37510
37844
  return new Promise((resolve10) => {
37511
37845
  let proc;
37512
37846
  try {
37513
- proc = (0, import_child_process30.spawn)("git", args2, { cwd, env: process.env });
37847
+ proc = (0, import_child_process31.spawn)("git", args2, { cwd, env: process.env });
37514
37848
  } catch {
37515
37849
  resolve10(null);
37516
37850
  return;
@@ -43537,8 +43871,9 @@ async function start(requestedAgent, presetSession) {
43537
43871
  })
43538
43872
  );
43539
43873
  provisionSkillsForStart();
43540
- const depsReady = process.env.CODESPACES === "true" ? provisionProjectDependencies(cwd).catch(() => void 0) : Promise.resolve();
43541
- if (process.env.CODESPACES === "true") {
43874
+ const managedBox = !isLocalSession();
43875
+ const depsReady = managedBox ? provisionProjectDependencies(cwd).catch(() => void 0) : Promise.resolve();
43876
+ if (managedBox) {
43542
43877
  const GATE_TIMEOUT_MS = 24e4;
43543
43878
  const acpAdapterForGate = requiresAcp(session.agent) ? await resolveAcpAdapterWithRetry(session.agent) : null;
43544
43879
  const agentBinaryReady = acpAdapterForGate ? Promise.all([
@@ -44158,11 +44493,11 @@ async function logout() {
44158
44493
  var import_picocolors11 = __toESM(require("picocolors"));
44159
44494
 
44160
44495
  // src/services/providers/github-codespaces.ts
44161
- var import_child_process31 = require("child_process");
44496
+ var import_child_process32 = require("child_process");
44162
44497
  var import_util4 = require("util");
44163
44498
  var import_picocolors9 = __toESM(require("picocolors"));
44164
44499
  var path89 = __toESM(require("path"));
44165
- var execFileP6 = (0, import_util4.promisify)(import_child_process31.execFile);
44500
+ var execFileP6 = (0, import_util4.promisify)(import_child_process32.execFile);
44166
44501
  var MAX_BUFFER = 8 * 1024 * 1024;
44167
44502
  function resetStdinForChild() {
44168
44503
  if (process.stdin.isTTY) {
@@ -44206,7 +44541,7 @@ var GitHubCodespacesProvider = class {
44206
44541
  if (!isAuthed) {
44207
44542
  resetStdinForChild();
44208
44543
  await new Promise((resolve10, reject) => {
44209
- const proc = (0, import_child_process31.spawn)("gh", ["auth", "login", "-s", "codespace,repo,read:user"], {
44544
+ const proc = (0, import_child_process32.spawn)("gh", ["auth", "login", "-s", "codespace,repo,read:user"], {
44210
44545
  stdio: "inherit"
44211
44546
  });
44212
44547
  proc.on("exit", (code) => {
@@ -44240,7 +44575,7 @@ var GitHubCodespacesProvider = class {
44240
44575
  wt(noteLines.join("\n"), "One more permission needed");
44241
44576
  resetStdinForChild();
44242
44577
  const refreshCode = await new Promise((resolve10, reject) => {
44243
- const proc = (0, import_child_process31.spawn)(
44578
+ const proc = (0, import_child_process32.spawn)(
44244
44579
  "gh",
44245
44580
  ["auth", "refresh", "-h", "github.com", "-s", "codespace"],
44246
44581
  { stdio: "inherit" }
@@ -44390,7 +44725,7 @@ var GitHubCodespacesProvider = class {
44390
44725
  O2.step(`Installing gh via ${installCmd.describe}\u2026`);
44391
44726
  resetStdinForChild();
44392
44727
  const ok = await new Promise((resolve10) => {
44393
- const proc = (0, import_child_process31.spawn)(installCmd.exe, installCmd.args, { stdio: "inherit" });
44728
+ const proc = (0, import_child_process32.spawn)(installCmd.exe, installCmd.args, { stdio: "inherit" });
44394
44729
  proc.on("exit", (code) => resolve10(code === 0));
44395
44730
  proc.on("error", () => resolve10(false));
44396
44731
  });
@@ -44417,7 +44752,7 @@ var GitHubCodespacesProvider = class {
44417
44752
  );
44418
44753
  resetStdinForChild();
44419
44754
  await new Promise((resolve10, reject) => {
44420
- const proc = (0, import_child_process31.spawn)(
44755
+ const proc = (0, import_child_process32.spawn)(
44421
44756
  "gh",
44422
44757
  ["auth", "refresh", "-h", "github.com", "-s", "repo,read:org"],
44423
44758
  { stdio: "inherit" }
@@ -44595,7 +44930,7 @@ var GitHubCodespacesProvider = class {
44595
44930
  async streamCommand(workspaceId, command2) {
44596
44931
  resetStdinForChild();
44597
44932
  return new Promise((resolve10, reject) => {
44598
- const proc = (0, import_child_process31.spawn)(
44933
+ const proc = (0, import_child_process32.spawn)(
44599
44934
  "gh",
44600
44935
  ["codespace", "ssh", "-c", workspaceId, "--", "-tt", command2],
44601
44936
  { stdio: "inherit" }
@@ -44622,11 +44957,11 @@ var GitHubCodespacesProvider = class {
44622
44957
  `mkdir -p ${shellQuote(remoteDir)} && tar -xzf - -C ${shellQuote(remoteDir)}`
44623
44958
  ];
44624
44959
  await new Promise((resolve10, reject) => {
44625
- const tar = (0, import_child_process31.spawn)("tar", tarArgs, {
44960
+ const tar = (0, import_child_process32.spawn)("tar", tarArgs, {
44626
44961
  stdio: ["ignore", "pipe", "pipe"],
44627
44962
  env: tarEnv
44628
44963
  });
44629
- const ssh = (0, import_child_process31.spawn)("gh", sshArgs, {
44964
+ const ssh = (0, import_child_process32.spawn)("gh", sshArgs, {
44630
44965
  stdio: [tar.stdout, "pipe", "pipe"]
44631
44966
  });
44632
44967
  let tarErr = "";
@@ -44660,7 +44995,7 @@ var GitHubCodespacesProvider = class {
44660
44995
  }
44661
44996
  const cmd = parts.join(" && ");
44662
44997
  await new Promise((resolve10, reject) => {
44663
- const proc = (0, import_child_process31.spawn)(
44998
+ const proc = (0, import_child_process32.spawn)(
44664
44999
  "gh",
44665
45000
  ["codespace", "ssh", "-c", workspaceId, "--", cmd],
44666
45001
  { stdio: ["pipe", "pipe", "pipe"] }
@@ -44718,11 +45053,11 @@ function shellQuote(s) {
44718
45053
  }
44719
45054
 
44720
45055
  // src/services/providers/gitpod.ts
44721
- var import_child_process32 = require("child_process");
45056
+ var import_child_process33 = require("child_process");
44722
45057
  var import_util5 = require("util");
44723
45058
  var path90 = __toESM(require("path"));
44724
45059
  var import_picocolors10 = __toESM(require("picocolors"));
44725
- var execFileP7 = (0, import_util5.promisify)(import_child_process32.execFile);
45060
+ var execFileP7 = (0, import_util5.promisify)(import_child_process33.execFile);
44726
45061
  var MAX_BUFFER2 = 8 * 1024 * 1024;
44727
45062
  function resetStdinForChild2() {
44728
45063
  if (process.stdin.isTTY) {
@@ -44762,7 +45097,7 @@ var GitpodProvider = class {
44762
45097
  );
44763
45098
  resetStdinForChild2();
44764
45099
  await new Promise((resolve10, reject) => {
44765
- const proc = (0, import_child_process32.spawn)("gitpod", ["login"], { stdio: "inherit" });
45100
+ const proc = (0, import_child_process33.spawn)("gitpod", ["login"], { stdio: "inherit" });
44766
45101
  proc.on("exit", (code) => {
44767
45102
  if (code === 0) resolve10();
44768
45103
  else reject(new Error("gitpod login failed."));
@@ -44914,7 +45249,7 @@ var GitpodProvider = class {
44914
45249
  async streamCommand(workspaceId, command2) {
44915
45250
  resetStdinForChild2();
44916
45251
  return new Promise((resolve10, reject) => {
44917
- const proc = (0, import_child_process32.spawn)(
45252
+ const proc = (0, import_child_process33.spawn)(
44918
45253
  "gitpod",
44919
45254
  ["workspace", "ssh", workspaceId, "--", "-tt", command2],
44920
45255
  { stdio: "inherit" }
@@ -44934,11 +45269,11 @@ var GitpodProvider = class {
44934
45269
  const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
44935
45270
  const remoteCmd = `mkdir -p ${shellQuote2(remoteDir)} && tar -xzf - -C ${shellQuote2(remoteDir)}`;
44936
45271
  await new Promise((resolve10, reject) => {
44937
- const tar = (0, import_child_process32.spawn)("tar", tarArgs, {
45272
+ const tar = (0, import_child_process33.spawn)("tar", tarArgs, {
44938
45273
  stdio: ["ignore", "pipe", "pipe"],
44939
45274
  env: tarEnv
44940
45275
  });
44941
- const ssh = (0, import_child_process32.spawn)(
45276
+ const ssh = (0, import_child_process33.spawn)(
44942
45277
  "gitpod",
44943
45278
  ["workspace", "ssh", workspaceId, "--", remoteCmd],
44944
45279
  { stdio: [tar.stdout, "pipe", "pipe"] }
@@ -44970,7 +45305,7 @@ var GitpodProvider = class {
44970
45305
  }
44971
45306
  const cmd = parts.join(" && ");
44972
45307
  await new Promise((resolve10, reject) => {
44973
- const proc = (0, import_child_process32.spawn)(
45308
+ const proc = (0, import_child_process33.spawn)(
44974
45309
  "gitpod",
44975
45310
  ["workspace", "ssh", workspaceId, "--", cmd],
44976
45311
  { stdio: ["pipe", "pipe", "pipe"] }
@@ -44994,10 +45329,10 @@ function shellQuote2(s) {
44994
45329
  }
44995
45330
 
44996
45331
  // src/services/providers/gitlab-workspaces.ts
44997
- var import_child_process33 = require("child_process");
45332
+ var import_child_process34 = require("child_process");
44998
45333
  var import_util6 = require("util");
44999
45334
  var path91 = __toESM(require("path"));
45000
- var execFileP8 = (0, import_util6.promisify)(import_child_process33.execFile);
45335
+ var execFileP8 = (0, import_util6.promisify)(import_child_process34.execFile);
45001
45336
  var MAX_BUFFER3 = 8 * 1024 * 1024;
45002
45337
  var GITLAB_API_BASE = process.env.CODEAM_GITLAB_API_URL ?? "https://gitlab.com/api/v4";
45003
45338
  function resetStdinForChild3() {
@@ -45039,7 +45374,7 @@ var GitLabWorkspacesProvider = class {
45039
45374
  );
45040
45375
  resetStdinForChild3();
45041
45376
  await new Promise((resolve10, reject) => {
45042
- const proc = (0, import_child_process33.spawn)(
45377
+ const proc = (0, import_child_process34.spawn)(
45043
45378
  "glab",
45044
45379
  ["auth", "login", "--scopes", "api,read_user,read_repository"],
45045
45380
  { stdio: "inherit" }
@@ -45211,7 +45546,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
45211
45546
  const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
45212
45547
  resetStdinForChild3();
45213
45548
  return new Promise((resolve10, reject) => {
45214
- const proc = (0, import_child_process33.spawn)(
45549
+ const proc = (0, import_child_process34.spawn)(
45215
45550
  "ssh",
45216
45551
  ["-tt", "-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, command2],
45217
45552
  { stdio: "inherit" }
@@ -45232,8 +45567,8 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
45232
45567
  const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
45233
45568
  const remoteCmd = `mkdir -p ${shellQuote3(remoteDir)} && tar -xzf - -C ${shellQuote3(remoteDir)}`;
45234
45569
  await new Promise((resolve10, reject) => {
45235
- const tar = (0, import_child_process33.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
45236
- const ssh = (0, import_child_process33.spawn)(
45570
+ const tar = (0, import_child_process34.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
45571
+ const ssh = (0, import_child_process34.spawn)(
45237
45572
  "ssh",
45238
45573
  ["-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, remoteCmd],
45239
45574
  { stdio: [tar.stdout, "pipe", "pipe"] }
@@ -45263,7 +45598,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
45263
45598
  }
45264
45599
  const cmd = parts.join(" && ");
45265
45600
  await new Promise((resolve10, reject) => {
45266
- const proc = (0, import_child_process33.spawn)(
45601
+ const proc = (0, import_child_process34.spawn)(
45267
45602
  "ssh",
45268
45603
  ["-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, cmd],
45269
45604
  { stdio: ["pipe", "pipe", "pipe"] }
@@ -45322,10 +45657,10 @@ function shellQuote3(s) {
45322
45657
  }
45323
45658
 
45324
45659
  // src/services/providers/railway.ts
45325
- var import_child_process34 = require("child_process");
45660
+ var import_child_process35 = require("child_process");
45326
45661
  var import_util7 = require("util");
45327
45662
  var path92 = __toESM(require("path"));
45328
- var execFileP9 = (0, import_util7.promisify)(import_child_process34.execFile);
45663
+ var execFileP9 = (0, import_util7.promisify)(import_child_process35.execFile);
45329
45664
  var MAX_BUFFER4 = 8 * 1024 * 1024;
45330
45665
  function resetStdinForChild4() {
45331
45666
  if (process.stdin.isTTY) {
@@ -45366,7 +45701,7 @@ var RailwayProvider = class {
45366
45701
  );
45367
45702
  resetStdinForChild4();
45368
45703
  await new Promise((resolve10, reject) => {
45369
- const proc = (0, import_child_process34.spawn)("railway", ["login"], { stdio: "inherit" });
45704
+ const proc = (0, import_child_process35.spawn)("railway", ["login"], { stdio: "inherit" });
45370
45705
  proc.on("exit", (code) => {
45371
45706
  if (code === 0) resolve10();
45372
45707
  else reject(new Error("railway login failed."));
@@ -45509,7 +45844,7 @@ var RailwayProvider = class {
45509
45844
  }
45510
45845
  resetStdinForChild4();
45511
45846
  return new Promise((resolve10, reject) => {
45512
- const proc = (0, import_child_process34.spawn)(
45847
+ const proc = (0, import_child_process35.spawn)(
45513
45848
  "railway",
45514
45849
  ["shell", "--project", projectId, "--service", serviceId, "--command", command2],
45515
45850
  { stdio: "inherit" }
@@ -45533,8 +45868,8 @@ var RailwayProvider = class {
45533
45868
  const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
45534
45869
  const remoteCmd = `mkdir -p ${shellQuote4(remoteDir)} && tar -xzf - -C ${shellQuote4(remoteDir)}`;
45535
45870
  await new Promise((resolve10, reject) => {
45536
- const tar = (0, import_child_process34.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
45537
- const sh = (0, import_child_process34.spawn)(
45871
+ const tar = (0, import_child_process35.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
45872
+ const sh = (0, import_child_process35.spawn)(
45538
45873
  "railway",
45539
45874
  ["shell", "--project", projectId, "--service", serviceId, "--command", remoteCmd],
45540
45875
  { stdio: [tar.stdout, "pipe", "pipe"] }
@@ -45567,7 +45902,7 @@ var RailwayProvider = class {
45567
45902
  }
45568
45903
  const cmd = parts.join(" && ");
45569
45904
  await new Promise((resolve10, reject) => {
45570
- const proc = (0, import_child_process34.spawn)(
45905
+ const proc = (0, import_child_process35.spawn)(
45571
45906
  "railway",
45572
45907
  ["shell", "--project", projectId, "--service", serviceId, "--command", cmd],
45573
45908
  { stdio: ["pipe", "pipe", "pipe"] }
@@ -46375,7 +46710,7 @@ function checkChokidar() {
46375
46710
  }
46376
46711
  async function doctor(args2 = []) {
46377
46712
  const json = args2.includes("--json");
46378
- const cliVersion = true ? "2.69.0" : "0.0.0-dev";
46713
+ const cliVersion = true ? "2.70.0" : "0.0.0-dev";
46379
46714
  const apiBase2 = resolveApiBaseUrl();
46380
46715
  const diagnosticId = (0, import_node_crypto13.randomUUID)();
46381
46716
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -46766,7 +47101,7 @@ async function mcpRun(args2) {
46766
47101
  // src/commands/version.ts
46767
47102
  var import_picocolors15 = __toESM(require("picocolors"));
46768
47103
  function version2() {
46769
- const v = true ? "2.69.0" : "unknown";
47104
+ const v = true ? "2.70.0" : "unknown";
46770
47105
  console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
46771
47106
  }
46772
47107
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.69.0",
3
+ "version": "2.70.0",
4
4
  "description": "Workflow-continuity bridge for AI coding agents. Wrap Claude Code or Codex in a PTY and supervise, approve, and redirect the session from any device — async. The terminal companion for CodeAgent Mobile.",
5
5
  "type": "commonjs",
6
6
  "main": "dist/index.js",