codeam-cli 2.39.38 → 2.39.40

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 +12 -0
  2. package/dist/index.js +154 -105
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,18 @@ 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.39.39] — 2026-06-19
8
+
9
+ ### Fixed
10
+
11
+ - **cli:** Gate preview tunnel on cloudflared 'Registered tunnel connection', not a host DNS probe
12
+
13
+ ## [2.39.38] — 2026-06-19
14
+
15
+ ### Added
16
+
17
+ - **cli:** Host-agent runs the per-agent install script before spawning
18
+
7
19
  ## [2.39.37] — 2026-06-19
8
20
 
9
21
  ### Fixed
package/dist/index.js CHANGED
@@ -498,7 +498,7 @@ var import_qrcode_terminal = __toESM(require("qrcode-terminal"));
498
498
  // package.json
499
499
  var package_default = {
500
500
  name: "codeam-cli",
501
- version: "2.39.38",
501
+ version: "2.39.40",
502
502
  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.",
503
503
  type: "commonjs",
504
504
  main: "dist/index.js",
@@ -5908,7 +5908,7 @@ function readAnonId() {
5908
5908
  }
5909
5909
  function superProperties() {
5910
5910
  return {
5911
- cliVersion: true ? "2.39.38" : "0.0.0-dev",
5911
+ cliVersion: true ? "2.39.40" : "0.0.0-dev",
5912
5912
  nodeVersion: process.version,
5913
5913
  platform: process.platform,
5914
5914
  arch: process.arch,
@@ -15259,7 +15259,7 @@ var fs35 = __toESM(require("fs"));
15259
15259
  var os28 = __toESM(require("os"));
15260
15260
  var path42 = __toESM(require("path"));
15261
15261
  var import_crypto3 = require("crypto");
15262
- var import_child_process18 = require("child_process");
15262
+ var import_child_process19 = require("child_process");
15263
15263
 
15264
15264
  // src/lib/payload.ts
15265
15265
  var import_zod = require("zod");
@@ -16464,37 +16464,21 @@ async function linkDryRunPreflight(ctx) {
16464
16464
  }
16465
16465
 
16466
16466
  // src/services/preview/cloudflared.ts
16467
- var import_promises = __toESM(require("dns/promises"));
16467
+ var import_child_process11 = require("child_process");
16468
16468
  var import_fs = require("fs");
16469
- var import_promises2 = __toESM(require("fs/promises"));
16469
+ var import_promises = __toESM(require("fs/promises"));
16470
16470
  var import_os6 = __toESM(require("os"));
16471
16471
  var import_path4 = __toESM(require("path"));
16472
- var import_promises3 = require("stream/promises");
16472
+ var import_promises2 = require("stream/promises");
16473
16473
  var import_which = __toESM(require("which"));
16474
16474
  var CACHED_BINARY = import_path4.default.join(import_os6.default.homedir(), ".codeam", "bin", "cloudflared");
16475
- async function waitForCloudflaredReady(url, timeoutMs = 6e4) {
16476
- const hostname3 = new URL(url).hostname;
16477
- const cares = new import_promises.Resolver();
16478
- const start2 = Date.now();
16479
- while (Date.now() - start2 < timeoutMs) {
16480
- const lookup = import_promises.default.lookup(hostname3, { all: true }).then((addrs) => addrs.length > 0, () => false);
16481
- const v4 = cares.resolve4(hostname3).then((addrs) => addrs.length > 0, () => false);
16482
- const v6 = cares.resolve6(hostname3).then((addrs) => addrs.length > 0, () => false);
16483
- const results = await Promise.all([lookup, v4, v6]);
16484
- if (results.some((ok) => ok)) return;
16485
- await new Promise((r) => setTimeout(r, 1e3));
16486
- }
16487
- throw new Error(
16488
- `DNS for ${hostname3} did not resolve within ${timeoutMs}ms (Cloudflare Quick Tunnel registration may have failed).`
16489
- );
16490
- }
16491
16475
  async function resolveCloudflared(opts = {}) {
16492
16476
  try {
16493
16477
  return await (0, import_which.default)("cloudflared");
16494
16478
  } catch {
16495
16479
  }
16496
16480
  try {
16497
- await import_promises2.default.access(CACHED_BINARY);
16481
+ await import_promises.default.access(CACHED_BINARY);
16498
16482
  return CACHED_BINARY;
16499
16483
  } catch {
16500
16484
  }
@@ -16508,14 +16492,14 @@ async function resolveCloudflared(opts = {}) {
16508
16492
  }
16509
16493
  async function downloadCloudflared(target) {
16510
16494
  const url = downloadUrlForPlatform();
16511
- await import_promises2.default.mkdir(import_path4.default.dirname(target), { recursive: true });
16495
+ await import_promises.default.mkdir(import_path4.default.dirname(target), { recursive: true });
16512
16496
  const response = await fetch(url);
16513
16497
  if (!response.ok || !response.body) {
16514
16498
  throw new Error(
16515
16499
  `Failed to download cloudflared from ${url}: HTTP ${response.status}. Install manually from https://github.com/cloudflare/cloudflared/releases.`
16516
16500
  );
16517
16501
  }
16518
- await (0, import_promises3.pipeline)(
16502
+ await (0, import_promises2.pipeline)(
16519
16503
  response.body,
16520
16504
  (0, import_fs.createWriteStream)(target, { mode: 493 })
16521
16505
  );
@@ -16533,14 +16517,41 @@ function downloadUrlForPlatform() {
16533
16517
  `cloudflared auto-install not supported on ${platform3}/${arch2}. Install manually from https://github.com/cloudflare/cloudflared/releases.`
16534
16518
  );
16535
16519
  }
16520
+ function decodeTunnelToken(token) {
16521
+ const decoded = JSON.parse(Buffer.from(token, "base64").toString("utf8"));
16522
+ if (!decoded.a || !decoded.t || !decoded.s) {
16523
+ throw new Error("cloudflared token missing accountTag/tunnelID/tunnelSecret");
16524
+ }
16525
+ return { AccountTag: decoded.a, TunnelID: decoded.t, TunnelSecret: decoded.s };
16526
+ }
16527
+ async function spawnNamedTunnel(bin, token, port) {
16528
+ const creds = decodeTunnelToken(token);
16529
+ const credDir = import_path4.default.join(import_os6.default.homedir(), ".codeam");
16530
+ await import_promises.default.mkdir(credDir, { recursive: true });
16531
+ const credFile = import_path4.default.join(credDir, `tunnel-${creds.TunnelID}.json`);
16532
+ await import_promises.default.writeFile(credFile, JSON.stringify(creds), { mode: 384 });
16533
+ return (0, import_child_process11.spawn)(
16534
+ bin,
16535
+ [
16536
+ "tunnel",
16537
+ "run",
16538
+ "--credentials-file",
16539
+ credFile,
16540
+ "--url",
16541
+ `http://localhost:${port}`,
16542
+ creds.TunnelID
16543
+ ],
16544
+ { stdio: ["ignore", "pipe", "pipe"] }
16545
+ );
16546
+ }
16536
16547
 
16537
16548
  // src/services/preview/codespace.ts
16538
- var import_child_process11 = require("child_process");
16549
+ var import_child_process12 = require("child_process");
16539
16550
  var import_util3 = require("util");
16540
- var execFileP4 = (0, import_util3.promisify)(import_child_process11.execFile);
16551
+ var execFileP4 = (0, import_util3.promisify)(import_child_process12.execFile);
16541
16552
 
16542
16553
  // src/services/preview/config-file.ts
16543
- var import_promises4 = __toESM(require("fs/promises"));
16554
+ var import_promises3 = __toESM(require("fs/promises"));
16544
16555
  var import_path5 = __toESM(require("path"));
16545
16556
  var CONFIG_DIR = ".codeam";
16546
16557
  var CONFIG_FILE = "preview.json";
@@ -16557,7 +16568,7 @@ var REQUIRED_FIELDS = [
16557
16568
  async function readPreviewConfig(cwd) {
16558
16569
  let raw;
16559
16570
  try {
16560
- raw = await import_promises4.default.readFile(configPath(cwd), "utf-8");
16571
+ raw = await import_promises3.default.readFile(configPath(cwd), "utf-8");
16561
16572
  } catch {
16562
16573
  return null;
16563
16574
  }
@@ -16576,8 +16587,8 @@ async function readPreviewConfig(cwd) {
16576
16587
  }
16577
16588
  async function writePreviewConfig(cwd, detection) {
16578
16589
  const filePath = configPath(cwd);
16579
- await import_promises4.default.mkdir(import_path5.default.dirname(filePath), { recursive: true });
16580
- await import_promises4.default.writeFile(filePath, JSON.stringify(detection, null, 2) + "\n", "utf-8");
16590
+ await import_promises3.default.mkdir(import_path5.default.dirname(filePath), { recursive: true });
16591
+ await import_promises3.default.writeFile(filePath, JSON.stringify(detection, null, 2) + "\n", "utf-8");
16581
16592
  }
16582
16593
 
16583
16594
  // src/services/preview/parser.ts
@@ -16691,10 +16702,10 @@ var import_fs2 = require("fs");
16691
16702
  var import_path6 = __toESM(require("path"));
16692
16703
 
16693
16704
  // src/services/preview/run-setup.ts
16694
- var import_child_process12 = require("child_process");
16705
+ var import_child_process13 = require("child_process");
16695
16706
  function runSetupCommand(cmd, args2, cwd, env, opts) {
16696
16707
  return new Promise((resolve7) => {
16697
- const child = (0, import_child_process12.spawn)(cmd, args2, {
16708
+ const child = (0, import_child_process13.spawn)(cmd, args2, {
16698
16709
  cwd,
16699
16710
  env: { ...process.env, ...env ?? {} },
16700
16711
  stdio: ["ignore", "pipe", "pipe"]
@@ -16991,7 +17002,7 @@ function activePreviewSessionIds() {
16991
17002
  }
16992
17003
 
16993
17004
  // src/beads/bd-adapter.ts
16994
- var import_child_process13 = require("child_process");
17005
+ var import_child_process14 = require("child_process");
16995
17006
  var fs31 = __toESM(require("fs"));
16996
17007
  var os25 = __toESM(require("os"));
16997
17008
  var path37 = __toESM(require("path"));
@@ -17045,7 +17056,7 @@ function _defaultSpawn(binaryPath, args2, opts) {
17045
17056
  return new Promise((resolve7) => {
17046
17057
  let proc;
17047
17058
  try {
17048
- proc = (0, import_child_process13.spawn)(binaryPath, args2, { cwd: opts.cwd, env: opts.env });
17059
+ proc = (0, import_child_process14.spawn)(binaryPath, args2, { cwd: opts.cwd, env: opts.env });
17049
17060
  } catch (err) {
17050
17061
  resolve7({ code: -1, stdout: "", stderr: err.message });
17051
17062
  return;
@@ -17199,13 +17210,13 @@ function coerceIssue(row, projectKey) {
17199
17210
  }
17200
17211
 
17201
17212
  // src/beads/provisioner.ts
17202
- var import_child_process17 = require("child_process");
17213
+ var import_child_process18 = require("child_process");
17203
17214
  var fs34 = __toESM(require("fs"));
17204
17215
  var os27 = __toESM(require("os"));
17205
17216
  var path40 = __toESM(require("path"));
17206
17217
 
17207
17218
  // src/beads/install-bd.ts
17208
- var import_child_process14 = require("child_process");
17219
+ var import_child_process15 = require("child_process");
17209
17220
  var INSTALL_SH_URL = "https://raw.githubusercontent.com/gastownhall/beads/main/scripts/install.sh";
17210
17221
  var INSTALL_PS1_URL = "https://raw.githubusercontent.com/gastownhall/beads/main/install.ps1";
17211
17222
  function resolveInstallStrategy(platform3) {
@@ -17236,7 +17247,7 @@ function _defaultInstallSpawn(strategy) {
17236
17247
  return new Promise((resolve7) => {
17237
17248
  let proc;
17238
17249
  try {
17239
- proc = (0, import_child_process14.spawn)(strategy.command, strategy.args, { env: process.env });
17250
+ proc = (0, import_child_process15.spawn)(strategy.command, strategy.args, { env: process.env });
17240
17251
  } catch (err) {
17241
17252
  resolve7({ ok: false, code: -1, stderr: err.message });
17242
17253
  return;
@@ -17266,7 +17277,7 @@ async function installBd(platform3 = process.platform) {
17266
17277
  }
17267
17278
 
17268
17279
  // src/beads/install-dolt.ts
17269
- var import_child_process15 = require("child_process");
17280
+ var import_child_process16 = require("child_process");
17270
17281
  var fs32 = __toESM(require("fs"));
17271
17282
  var os26 = __toESM(require("os"));
17272
17283
  var path38 = __toESM(require("path"));
@@ -17428,7 +17439,7 @@ function _defaultDoltInstallSpawn(strategy) {
17428
17439
  };
17429
17440
  let proc;
17430
17441
  try {
17431
- proc = (0, import_child_process15.spawn)(strategy.command, strategy.args, {
17442
+ proc = (0, import_child_process16.spawn)(strategy.command, strategy.args, {
17432
17443
  env: process.env,
17433
17444
  stdio: ["ignore", "pipe", "pipe"]
17434
17445
  });
@@ -17501,7 +17512,7 @@ async function ensureSharedServer(adapter) {
17501
17512
  }
17502
17513
 
17503
17514
  // src/beads/project-key.ts
17504
- var import_child_process16 = require("child_process");
17515
+ var import_child_process17 = require("child_process");
17505
17516
  var crypto2 = __toESM(require("crypto"));
17506
17517
  var fs33 = __toESM(require("fs"));
17507
17518
  var path39 = __toESM(require("path"));
@@ -17548,7 +17559,7 @@ function findRepoRoot(cwd) {
17548
17559
  }
17549
17560
  var _execSeam2 = {
17550
17561
  exec: (file, args2, opts) => {
17551
- const out2 = (0, import_child_process16.execFileSync)(file, args2, opts);
17562
+ const out2 = (0, import_child_process17.execFileSync)(file, args2, opts);
17552
17563
  return typeof out2 === "string" ? out2 : out2.toString("utf8");
17553
17564
  },
17554
17565
  realpath: (p2) => fs33.realpathSync(p2)
@@ -17708,7 +17719,7 @@ function linkBdOntoPath(binaryPath) {
17708
17719
  log.info("beads", `linked bd onto PATH: ${linkPath} -> ${binaryPath}`);
17709
17720
  }
17710
17721
  function setGitBeadsRole() {
17711
- (0, import_child_process17.execFileSync)("git", ["config", "--global", "beads.role", "contributor"], {
17722
+ (0, import_child_process18.execFileSync)("git", ["config", "--global", "beads.role", "contributor"], {
17712
17723
  stdio: "ignore"
17713
17724
  });
17714
17725
  }
@@ -18394,7 +18405,7 @@ var sessionTerminated = async (ctx, cmd) => {
18394
18405
  } catch {
18395
18406
  }
18396
18407
  try {
18397
- const proc = (0, import_child_process18.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
18408
+ const proc = (0, import_child_process19.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
18398
18409
  detached: true,
18399
18410
  stdio: "ignore"
18400
18411
  });
@@ -18416,7 +18427,7 @@ var shutdownSession = async (ctx, cmd) => {
18416
18427
  }
18417
18428
  if (ctx.keepAliveCtx.inCodespace && ctx.keepAliveCtx.codespaceName) {
18418
18429
  try {
18419
- const stopProc = (0, import_child_process18.spawn)(
18430
+ const stopProc = (0, import_child_process19.spawn)(
18420
18431
  "bash",
18421
18432
  ["-lc", `sleep 1; gh codespace stop -c ${JSON.stringify(ctx.keepAliveCtx.codespaceName)} >/dev/null 2>&1 || true`],
18422
18433
  { detached: true, stdio: "ignore" }
@@ -18426,7 +18437,7 @@ var shutdownSession = async (ctx, cmd) => {
18426
18437
  }
18427
18438
  }
18428
18439
  try {
18429
- const proc = (0, import_child_process18.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
18440
+ const proc = (0, import_child_process19.spawn)("bash", ["-lc", "pm2 delete codeam-pair >/dev/null 2>&1 || true"], {
18430
18441
  detached: true,
18431
18442
  stdio: "ignore"
18432
18443
  });
@@ -19031,7 +19042,7 @@ var previewStartH = (ctx, _cmd, parsed) => {
19031
19042
  "BOOT_SEQUENCE",
19032
19043
  `${spawnable.command} ${spawnable.args.join(" ")}`
19033
19044
  );
19034
- const devServer = (0, import_child_process18.spawn)(spawnable.command, spawnable.args, {
19045
+ const devServer = (0, import_child_process19.spawn)(spawnable.command, spawnable.args, {
19035
19046
  cwd: process.cwd(),
19036
19047
  env: { ...process.env, ...spawnable.env ?? {} },
19037
19048
  stdio: ["ignore", "pipe", "pipe"]
@@ -19134,6 +19145,47 @@ var previewStartH = (ctx, _cmd, parsed) => {
19134
19145
  const MAX_TUNNEL_ATTEMPTS = 3;
19135
19146
  let parsedUrl = null;
19136
19147
  let lastTunnelErr = "cloudflared did not emit a URL within 45s";
19148
+ const namedToken = process.env.PREVIEW_TUNNEL_TOKEN;
19149
+ const namedHostname = process.env.PREVIEW_TUNNEL_HOSTNAME;
19150
+ if (namedToken && namedHostname) {
19151
+ try {
19152
+ emitProgress("TUNNEL_STARTING", `named tunnel ${namedHostname}`);
19153
+ const candidate = await spawnNamedTunnel(bin, namedToken, detection.port);
19154
+ let registered = false;
19155
+ const onNamedChunk = (chunk) => {
19156
+ const s = chunk.toString();
19157
+ if (/Registered tunnel connection/i.test(s)) registered = true;
19158
+ const trimmed = s.replace(/\n+$/g, "");
19159
+ if (trimmed.length > 0) log.info("preview", `cloudflared: ${trimmed}`);
19160
+ };
19161
+ candidate.stderr.on("data", onNamedChunk);
19162
+ candidate.stdout.on("data", onNamedChunk);
19163
+ const namedDeadline = Date.now() + 45e3;
19164
+ while (!registered && Date.now() < namedDeadline) {
19165
+ await new Promise((r) => setTimeout(r, 250));
19166
+ }
19167
+ if (registered) {
19168
+ const namedUrl = `https://${namedHostname}`;
19169
+ log.info("preview", `named tunnel registered: ${namedUrl}`);
19170
+ tunnel = candidate;
19171
+ parsedUrl = namedUrl;
19172
+ } else {
19173
+ log.info(
19174
+ "preview",
19175
+ "named tunnel did not register within 45s \u2014 falling back to quick tunnel"
19176
+ );
19177
+ try {
19178
+ candidate.kill("SIGTERM");
19179
+ } catch {
19180
+ }
19181
+ }
19182
+ } catch (e) {
19183
+ log.info(
19184
+ "preview",
19185
+ `named tunnel failed (${e.message}) \u2014 falling back to quick tunnel`
19186
+ );
19187
+ }
19188
+ }
19137
19189
  for (let attempt = 1; attempt <= MAX_TUNNEL_ATTEMPTS && !parsedUrl; attempt += 1) {
19138
19190
  if (attempt > 1) {
19139
19191
  emitProgress(
@@ -19141,39 +19193,32 @@ var previewStartH = (ctx, _cmd, parsed) => {
19141
19193
  `cloudflared quick tunnel (retry ${attempt}/${MAX_TUNNEL_ATTEMPTS})`
19142
19194
  );
19143
19195
  }
19144
- const candidate = (0, import_child_process18.spawn)(
19196
+ const candidate = (0, import_child_process19.spawn)(
19145
19197
  bin,
19146
19198
  ["tunnel", "--url", `http://localhost:${detection.port}`],
19147
19199
  { stdio: ["ignore", "pipe", "pipe"] }
19148
19200
  );
19149
19201
  let candidateUrl = null;
19202
+ let registered = false;
19150
19203
  const onTunnelChunk = (chunk) => {
19151
19204
  const s = chunk.toString();
19152
19205
  if (!candidateUrl) candidateUrl = parseCloudflaredUrl(s);
19206
+ if (/Registered tunnel connection/i.test(s)) registered = true;
19153
19207
  const trimmed = s.replace(/\n+$/g, "");
19154
19208
  if (trimmed.length > 0) log.info("preview", `cloudflared: ${trimmed}`);
19155
19209
  };
19156
19210
  candidate.stderr.on("data", onTunnelChunk);
19157
19211
  candidate.stdout.on("data", onTunnelChunk);
19158
- const urlDeadline = Date.now() + 45e3;
19159
- while (!candidateUrl && Date.now() < urlDeadline) {
19212
+ const deadline = Date.now() + 45e3;
19213
+ while ((!candidateUrl || !registered) && Date.now() < deadline) {
19160
19214
  await new Promise((r) => setTimeout(r, 250));
19161
19215
  }
19162
- if (!candidateUrl) {
19163
- lastTunnelErr = "cloudflared did not emit a URL within 45s";
19164
- try {
19165
- candidate.kill("SIGTERM");
19166
- } catch {
19167
- }
19168
- continue;
19169
- }
19170
- try {
19171
- await waitForCloudflaredReady(candidateUrl, 4e4);
19172
- log.info("preview", `cloudflared probe: ${candidateUrl} reachable from CLI host`);
19216
+ if (candidateUrl && registered) {
19217
+ log.info("preview", `cloudflared tunnel registered: ${candidateUrl}`);
19173
19218
  tunnel = candidate;
19174
19219
  parsedUrl = candidateUrl;
19175
- } catch (e) {
19176
- lastTunnelErr = e.message;
19220
+ } else {
19221
+ lastTunnelErr = candidateUrl ? "cloudflared did not register a tunnel connection within 45s" : "cloudflared did not emit a URL within 45s";
19177
19222
  try {
19178
19223
  candidate.kill("SIGTERM");
19179
19224
  } catch {
@@ -19315,7 +19360,7 @@ async function dispatchCommand(ctx, cmd) {
19315
19360
  }
19316
19361
 
19317
19362
  // src/services/file-watcher.service.ts
19318
- var import_child_process19 = require("child_process");
19363
+ var import_child_process20 = require("child_process");
19319
19364
  var fs36 = __toESM(require("fs"));
19320
19365
  var os29 = __toESM(require("os"));
19321
19366
  var path43 = __toESM(require("path"));
@@ -20081,7 +20126,7 @@ async function _runGitImpl(cwd, args2, opts = {}) {
20081
20126
  return new Promise((resolve7) => {
20082
20127
  let proc;
20083
20128
  try {
20084
- proc = (0, import_child_process19.spawn)("git", args2, { cwd, env: process.env });
20129
+ proc = (0, import_child_process20.spawn)("git", args2, { cwd, env: process.env });
20085
20130
  } catch {
20086
20131
  resolve7(null);
20087
20132
  return;
@@ -20113,7 +20158,7 @@ function _runGit(cwd, args2, opts = {}) {
20113
20158
  var import_crypto4 = require("crypto");
20114
20159
 
20115
20160
  // src/services/turn-files/git-changeset.ts
20116
- var import_child_process20 = require("child_process");
20161
+ var import_child_process21 = require("child_process");
20117
20162
  var path44 = __toESM(require("path"));
20118
20163
  async function collectRepoChangeset(opts) {
20119
20164
  const status2 = await runGit3(opts.repoRoot, ["status", "--porcelain=v1", "-z"]);
@@ -20197,7 +20242,7 @@ function defaultRunGit(cwd, args2) {
20197
20242
  return new Promise((resolve7) => {
20198
20243
  let proc;
20199
20244
  try {
20200
- proc = (0, import_child_process20.spawn)("git", args2, { cwd, env: process.env });
20245
+ proc = (0, import_child_process21.spawn)("git", args2, { cwd, env: process.env });
20201
20246
  } catch {
20202
20247
  resolve7(null);
20203
20248
  return;
@@ -23206,13 +23251,13 @@ function fetchQuotaUsage(runtime, historySvc) {
23206
23251
  }
23207
23252
 
23208
23253
  // src/commands/start/keep-alive.ts
23209
- var import_child_process21 = require("child_process");
23254
+ var import_child_process22 = require("child_process");
23210
23255
  function buildKeepAlive(ctx) {
23211
23256
  let timer = null;
23212
23257
  async function setIdleTimeout(minutes) {
23213
23258
  if (!ctx.inCodespace || !ctx.codespaceName) return;
23214
23259
  await new Promise((resolve7) => {
23215
- const proc = (0, import_child_process21.spawn)(
23260
+ const proc = (0, import_child_process22.spawn)(
23216
23261
  "gh",
23217
23262
  [
23218
23263
  "api",
@@ -24279,11 +24324,11 @@ async function logout() {
24279
24324
  var import_picocolors10 = __toESM(require("picocolors"));
24280
24325
 
24281
24326
  // src/services/providers/github-codespaces.ts
24282
- var import_child_process22 = require("child_process");
24327
+ var import_child_process23 = require("child_process");
24283
24328
  var import_util4 = require("util");
24284
24329
  var import_picocolors8 = __toESM(require("picocolors"));
24285
24330
  var path49 = __toESM(require("path"));
24286
- var execFileP5 = (0, import_util4.promisify)(import_child_process22.execFile);
24331
+ var execFileP5 = (0, import_util4.promisify)(import_child_process23.execFile);
24287
24332
  var MAX_BUFFER = 8 * 1024 * 1024;
24288
24333
  function resetStdinForChild() {
24289
24334
  if (process.stdin.isTTY) {
@@ -24327,7 +24372,7 @@ var GitHubCodespacesProvider = class {
24327
24372
  if (!isAuthed) {
24328
24373
  resetStdinForChild();
24329
24374
  await new Promise((resolve7, reject) => {
24330
- const proc = (0, import_child_process22.spawn)("gh", ["auth", "login", "-s", "codespace,repo,read:user"], {
24375
+ const proc = (0, import_child_process23.spawn)("gh", ["auth", "login", "-s", "codespace,repo,read:user"], {
24331
24376
  stdio: "inherit"
24332
24377
  });
24333
24378
  proc.on("exit", (code) => {
@@ -24361,7 +24406,7 @@ var GitHubCodespacesProvider = class {
24361
24406
  wt(noteLines.join("\n"), "One more permission needed");
24362
24407
  resetStdinForChild();
24363
24408
  const refreshCode = await new Promise((resolve7, reject) => {
24364
- const proc = (0, import_child_process22.spawn)(
24409
+ const proc = (0, import_child_process23.spawn)(
24365
24410
  "gh",
24366
24411
  ["auth", "refresh", "-h", "github.com", "-s", "codespace"],
24367
24412
  { stdio: "inherit" }
@@ -24511,7 +24556,7 @@ var GitHubCodespacesProvider = class {
24511
24556
  O2.step(`Installing gh via ${installCmd.describe}\u2026`);
24512
24557
  resetStdinForChild();
24513
24558
  const ok = await new Promise((resolve7) => {
24514
- const proc = (0, import_child_process22.spawn)(installCmd.exe, installCmd.args, { stdio: "inherit" });
24559
+ const proc = (0, import_child_process23.spawn)(installCmd.exe, installCmd.args, { stdio: "inherit" });
24515
24560
  proc.on("exit", (code) => resolve7(code === 0));
24516
24561
  proc.on("error", () => resolve7(false));
24517
24562
  });
@@ -24538,7 +24583,7 @@ var GitHubCodespacesProvider = class {
24538
24583
  );
24539
24584
  resetStdinForChild();
24540
24585
  await new Promise((resolve7, reject) => {
24541
- const proc = (0, import_child_process22.spawn)(
24586
+ const proc = (0, import_child_process23.spawn)(
24542
24587
  "gh",
24543
24588
  ["auth", "refresh", "-h", "github.com", "-s", "repo,read:org"],
24544
24589
  { stdio: "inherit" }
@@ -24716,7 +24761,7 @@ var GitHubCodespacesProvider = class {
24716
24761
  async streamCommand(workspaceId, command2) {
24717
24762
  resetStdinForChild();
24718
24763
  return new Promise((resolve7, reject) => {
24719
- const proc = (0, import_child_process22.spawn)(
24764
+ const proc = (0, import_child_process23.spawn)(
24720
24765
  "gh",
24721
24766
  ["codespace", "ssh", "-c", workspaceId, "--", "-tt", command2],
24722
24767
  { stdio: "inherit" }
@@ -24743,11 +24788,11 @@ var GitHubCodespacesProvider = class {
24743
24788
  `mkdir -p ${shellQuote(remoteDir)} && tar -xzf - -C ${shellQuote(remoteDir)}`
24744
24789
  ];
24745
24790
  await new Promise((resolve7, reject) => {
24746
- const tar = (0, import_child_process22.spawn)("tar", tarArgs, {
24791
+ const tar = (0, import_child_process23.spawn)("tar", tarArgs, {
24747
24792
  stdio: ["ignore", "pipe", "pipe"],
24748
24793
  env: tarEnv
24749
24794
  });
24750
- const ssh = (0, import_child_process22.spawn)("gh", sshArgs, {
24795
+ const ssh = (0, import_child_process23.spawn)("gh", sshArgs, {
24751
24796
  stdio: [tar.stdout, "pipe", "pipe"]
24752
24797
  });
24753
24798
  let tarErr = "";
@@ -24781,7 +24826,7 @@ var GitHubCodespacesProvider = class {
24781
24826
  }
24782
24827
  const cmd = parts.join(" && ");
24783
24828
  await new Promise((resolve7, reject) => {
24784
- const proc = (0, import_child_process22.spawn)(
24829
+ const proc = (0, import_child_process23.spawn)(
24785
24830
  "gh",
24786
24831
  ["codespace", "ssh", "-c", workspaceId, "--", cmd],
24787
24832
  { stdio: ["pipe", "pipe", "pipe"] }
@@ -24839,11 +24884,11 @@ function shellQuote(s) {
24839
24884
  }
24840
24885
 
24841
24886
  // src/services/providers/gitpod.ts
24842
- var import_child_process23 = require("child_process");
24887
+ var import_child_process24 = require("child_process");
24843
24888
  var import_util5 = require("util");
24844
24889
  var path50 = __toESM(require("path"));
24845
24890
  var import_picocolors9 = __toESM(require("picocolors"));
24846
- var execFileP6 = (0, import_util5.promisify)(import_child_process23.execFile);
24891
+ var execFileP6 = (0, import_util5.promisify)(import_child_process24.execFile);
24847
24892
  var MAX_BUFFER2 = 8 * 1024 * 1024;
24848
24893
  function resetStdinForChild2() {
24849
24894
  if (process.stdin.isTTY) {
@@ -24883,7 +24928,7 @@ var GitpodProvider = class {
24883
24928
  );
24884
24929
  resetStdinForChild2();
24885
24930
  await new Promise((resolve7, reject) => {
24886
- const proc = (0, import_child_process23.spawn)("gitpod", ["login"], { stdio: "inherit" });
24931
+ const proc = (0, import_child_process24.spawn)("gitpod", ["login"], { stdio: "inherit" });
24887
24932
  proc.on("exit", (code) => {
24888
24933
  if (code === 0) resolve7();
24889
24934
  else reject(new Error("gitpod login failed."));
@@ -25035,7 +25080,7 @@ var GitpodProvider = class {
25035
25080
  async streamCommand(workspaceId, command2) {
25036
25081
  resetStdinForChild2();
25037
25082
  return new Promise((resolve7, reject) => {
25038
- const proc = (0, import_child_process23.spawn)(
25083
+ const proc = (0, import_child_process24.spawn)(
25039
25084
  "gitpod",
25040
25085
  ["workspace", "ssh", workspaceId, "--", "-tt", command2],
25041
25086
  { stdio: "inherit" }
@@ -25055,11 +25100,11 @@ var GitpodProvider = class {
25055
25100
  const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
25056
25101
  const remoteCmd = `mkdir -p ${shellQuote2(remoteDir)} && tar -xzf - -C ${shellQuote2(remoteDir)}`;
25057
25102
  await new Promise((resolve7, reject) => {
25058
- const tar = (0, import_child_process23.spawn)("tar", tarArgs, {
25103
+ const tar = (0, import_child_process24.spawn)("tar", tarArgs, {
25059
25104
  stdio: ["ignore", "pipe", "pipe"],
25060
25105
  env: tarEnv
25061
25106
  });
25062
- const ssh = (0, import_child_process23.spawn)(
25107
+ const ssh = (0, import_child_process24.spawn)(
25063
25108
  "gitpod",
25064
25109
  ["workspace", "ssh", workspaceId, "--", remoteCmd],
25065
25110
  { stdio: [tar.stdout, "pipe", "pipe"] }
@@ -25091,7 +25136,7 @@ var GitpodProvider = class {
25091
25136
  }
25092
25137
  const cmd = parts.join(" && ");
25093
25138
  await new Promise((resolve7, reject) => {
25094
- const proc = (0, import_child_process23.spawn)(
25139
+ const proc = (0, import_child_process24.spawn)(
25095
25140
  "gitpod",
25096
25141
  ["workspace", "ssh", workspaceId, "--", cmd],
25097
25142
  { stdio: ["pipe", "pipe", "pipe"] }
@@ -25115,10 +25160,10 @@ function shellQuote2(s) {
25115
25160
  }
25116
25161
 
25117
25162
  // src/services/providers/gitlab-workspaces.ts
25118
- var import_child_process24 = require("child_process");
25163
+ var import_child_process25 = require("child_process");
25119
25164
  var import_util6 = require("util");
25120
25165
  var path51 = __toESM(require("path"));
25121
- var execFileP7 = (0, import_util6.promisify)(import_child_process24.execFile);
25166
+ var execFileP7 = (0, import_util6.promisify)(import_child_process25.execFile);
25122
25167
  var MAX_BUFFER3 = 8 * 1024 * 1024;
25123
25168
  var GITLAB_API_BASE = process.env.CODEAM_GITLAB_API_URL ?? "https://gitlab.com/api/v4";
25124
25169
  function resetStdinForChild3() {
@@ -25160,7 +25205,7 @@ var GitLabWorkspacesProvider = class {
25160
25205
  );
25161
25206
  resetStdinForChild3();
25162
25207
  await new Promise((resolve7, reject) => {
25163
- const proc = (0, import_child_process24.spawn)(
25208
+ const proc = (0, import_child_process25.spawn)(
25164
25209
  "glab",
25165
25210
  ["auth", "login", "--scopes", "api,read_user,read_repository"],
25166
25211
  { stdio: "inherit" }
@@ -25332,7 +25377,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
25332
25377
  const sshHost = process.env.CODEAM_GITLAB_SSH_HOST ?? "workspaces.gitlab.com";
25333
25378
  resetStdinForChild3();
25334
25379
  return new Promise((resolve7, reject) => {
25335
- const proc = (0, import_child_process24.spawn)(
25380
+ const proc = (0, import_child_process25.spawn)(
25336
25381
  "ssh",
25337
25382
  ["-tt", "-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, command2],
25338
25383
  { stdio: "inherit" }
@@ -25353,8 +25398,8 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
25353
25398
  const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
25354
25399
  const remoteCmd = `mkdir -p ${shellQuote3(remoteDir)} && tar -xzf - -C ${shellQuote3(remoteDir)}`;
25355
25400
  await new Promise((resolve7, reject) => {
25356
- const tar = (0, import_child_process24.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
25357
- const ssh = (0, import_child_process24.spawn)(
25401
+ const tar = (0, import_child_process25.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
25402
+ const ssh = (0, import_child_process25.spawn)(
25358
25403
  "ssh",
25359
25404
  ["-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, remoteCmd],
25360
25405
  { stdio: [tar.stdout, "pipe", "pipe"] }
@@ -25384,7 +25429,7 @@ Docs: https://docs.gitlab.com/ee/user/workspace/configuration.html`
25384
25429
  }
25385
25430
  const cmd = parts.join(" && ");
25386
25431
  await new Promise((resolve7, reject) => {
25387
- const proc = (0, import_child_process24.spawn)(
25432
+ const proc = (0, import_child_process25.spawn)(
25388
25433
  "ssh",
25389
25434
  ["-o", "StrictHostKeyChecking=accept-new", `${workspaceId}@${sshHost}`, cmd],
25390
25435
  { stdio: ["pipe", "pipe", "pipe"] }
@@ -25443,10 +25488,10 @@ function shellQuote3(s) {
25443
25488
  }
25444
25489
 
25445
25490
  // src/services/providers/railway.ts
25446
- var import_child_process25 = require("child_process");
25491
+ var import_child_process26 = require("child_process");
25447
25492
  var import_util7 = require("util");
25448
25493
  var path52 = __toESM(require("path"));
25449
- var execFileP8 = (0, import_util7.promisify)(import_child_process25.execFile);
25494
+ var execFileP8 = (0, import_util7.promisify)(import_child_process26.execFile);
25450
25495
  var MAX_BUFFER4 = 8 * 1024 * 1024;
25451
25496
  function resetStdinForChild4() {
25452
25497
  if (process.stdin.isTTY) {
@@ -25487,7 +25532,7 @@ var RailwayProvider = class {
25487
25532
  );
25488
25533
  resetStdinForChild4();
25489
25534
  await new Promise((resolve7, reject) => {
25490
- const proc = (0, import_child_process25.spawn)("railway", ["login"], { stdio: "inherit" });
25535
+ const proc = (0, import_child_process26.spawn)("railway", ["login"], { stdio: "inherit" });
25491
25536
  proc.on("exit", (code) => {
25492
25537
  if (code === 0) resolve7();
25493
25538
  else reject(new Error("railway login failed."));
@@ -25630,7 +25675,7 @@ var RailwayProvider = class {
25630
25675
  }
25631
25676
  resetStdinForChild4();
25632
25677
  return new Promise((resolve7, reject) => {
25633
- const proc = (0, import_child_process25.spawn)(
25678
+ const proc = (0, import_child_process26.spawn)(
25634
25679
  "railway",
25635
25680
  ["shell", "--project", projectId, "--service", serviceId, "--command", command2],
25636
25681
  { stdio: "inherit" }
@@ -25654,8 +25699,8 @@ var RailwayProvider = class {
25654
25699
  const tarEnv = { ...process.env, COPYFILE_DISABLE: "1" };
25655
25700
  const remoteCmd = `mkdir -p ${shellQuote4(remoteDir)} && tar -xzf - -C ${shellQuote4(remoteDir)}`;
25656
25701
  await new Promise((resolve7, reject) => {
25657
- const tar = (0, import_child_process25.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
25658
- const sh = (0, import_child_process25.spawn)(
25702
+ const tar = (0, import_child_process26.spawn)("tar", tarArgs, { stdio: ["ignore", "pipe", "pipe"], env: tarEnv });
25703
+ const sh = (0, import_child_process26.spawn)(
25659
25704
  "railway",
25660
25705
  ["shell", "--project", projectId, "--service", serviceId, "--command", remoteCmd],
25661
25706
  { stdio: [tar.stdout, "pipe", "pipe"] }
@@ -25688,7 +25733,7 @@ var RailwayProvider = class {
25688
25733
  }
25689
25734
  const cmd = parts.join(" && ");
25690
25735
  await new Promise((resolve7, reject) => {
25691
- const proc = (0, import_child_process25.spawn)(
25736
+ const proc = (0, import_child_process26.spawn)(
25692
25737
  "railway",
25693
25738
  ["shell", "--project", projectId, "--service", serviceId, "--command", cmd],
25694
25739
  { stdio: ["pipe", "pipe", "pipe"] }
@@ -26863,6 +26908,10 @@ var HostAgentSupervisor = class {
26863
26908
  }
26864
26909
  const home = process.env.HOME || os36.homedir();
26865
26910
  childEnv.PATH = `${home}/.local/bin:${process.env.PATH ?? ""}`;
26911
+ if (payload.previewTunnelToken && payload.previewHostname) {
26912
+ childEnv.PREVIEW_TUNNEL_TOKEN = payload.previewTunnelToken;
26913
+ childEnv.PREVIEW_TUNNEL_HOSTNAME = payload.previewHostname;
26914
+ }
26866
26915
  report("spawning", "starting agent");
26867
26916
  const proc = this.spawnChild(childEnv, cwd, extraArgs);
26868
26917
  const child = { deployId: payload.deployId, proc };
@@ -27191,16 +27240,16 @@ function checkChokidar() {
27191
27240
  }
27192
27241
  async function doctor(args2 = []) {
27193
27242
  const json = args2.includes("--json");
27194
- const cliVersion = true ? "2.39.38" : "0.0.0-dev";
27243
+ const cliVersion = true ? "2.39.40" : "0.0.0-dev";
27195
27244
  const apiBase2 = resolveApiBaseUrl();
27196
27245
  const diagnosticId = (0, import_node_crypto8.randomUUID)();
27197
27246
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
27198
- const [dns2, health] = await Promise.all([
27247
+ const [dns, health] = await Promise.all([
27199
27248
  checkDns(apiBase2),
27200
27249
  checkHealth(apiBase2)
27201
27250
  ]);
27202
27251
  const checks = [
27203
- dns2,
27252
+ dns,
27204
27253
  health,
27205
27254
  checkConfigDir(),
27206
27255
  checkSessions(),
@@ -27390,7 +27439,7 @@ async function completion(args2) {
27390
27439
  // src/commands/version.ts
27391
27440
  var import_picocolors13 = __toESM(require("picocolors"));
27392
27441
  function version2() {
27393
- const v = true ? "2.39.38" : "unknown";
27442
+ const v = true ? "2.39.40" : "unknown";
27394
27443
  console.log(`${import_picocolors13.default.bold("codeam-cli")} ${import_picocolors13.default.cyan(v)}`);
27395
27444
  }
27396
27445
 
@@ -27676,7 +27725,7 @@ function checkForUpdates() {
27676
27725
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
27677
27726
  if (process.env.CI) return;
27678
27727
  if (!process.stdout.isTTY) return;
27679
- const current = true ? "2.39.38" : null;
27728
+ const current = true ? "2.39.40" : null;
27680
27729
  if (!current) return;
27681
27730
  const cache = readCache();
27682
27731
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.39.38",
3
+ "version": "2.39.40",
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",