codeam-cli 2.39.38 → 2.39.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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.39.38] — 2026-06-19
8
+
9
+ ### Added
10
+
11
+ - **cli:** Host-agent runs the per-agent install script before spawning
12
+
7
13
  ## [2.39.37] — 2026-06-19
8
14
 
9
15
  ### 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.39",
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.39" : "0.0.0-dev",
5912
5912
  nodeVersion: process.version,
5913
5913
  platform: process.platform,
5914
5914
  arch: process.arch,
@@ -16464,37 +16464,20 @@ async function linkDryRunPreflight(ctx) {
16464
16464
  }
16465
16465
 
16466
16466
  // src/services/preview/cloudflared.ts
16467
- var import_promises = __toESM(require("dns/promises"));
16468
16467
  var import_fs = require("fs");
16469
- var import_promises2 = __toESM(require("fs/promises"));
16468
+ var import_promises = __toESM(require("fs/promises"));
16470
16469
  var import_os6 = __toESM(require("os"));
16471
16470
  var import_path4 = __toESM(require("path"));
16472
- var import_promises3 = require("stream/promises");
16471
+ var import_promises2 = require("stream/promises");
16473
16472
  var import_which = __toESM(require("which"));
16474
16473
  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
16474
  async function resolveCloudflared(opts = {}) {
16492
16475
  try {
16493
16476
  return await (0, import_which.default)("cloudflared");
16494
16477
  } catch {
16495
16478
  }
16496
16479
  try {
16497
- await import_promises2.default.access(CACHED_BINARY);
16480
+ await import_promises.default.access(CACHED_BINARY);
16498
16481
  return CACHED_BINARY;
16499
16482
  } catch {
16500
16483
  }
@@ -16508,14 +16491,14 @@ async function resolveCloudflared(opts = {}) {
16508
16491
  }
16509
16492
  async function downloadCloudflared(target) {
16510
16493
  const url = downloadUrlForPlatform();
16511
- await import_promises2.default.mkdir(import_path4.default.dirname(target), { recursive: true });
16494
+ await import_promises.default.mkdir(import_path4.default.dirname(target), { recursive: true });
16512
16495
  const response = await fetch(url);
16513
16496
  if (!response.ok || !response.body) {
16514
16497
  throw new Error(
16515
16498
  `Failed to download cloudflared from ${url}: HTTP ${response.status}. Install manually from https://github.com/cloudflare/cloudflared/releases.`
16516
16499
  );
16517
16500
  }
16518
- await (0, import_promises3.pipeline)(
16501
+ await (0, import_promises2.pipeline)(
16519
16502
  response.body,
16520
16503
  (0, import_fs.createWriteStream)(target, { mode: 493 })
16521
16504
  );
@@ -16540,7 +16523,7 @@ var import_util3 = require("util");
16540
16523
  var execFileP4 = (0, import_util3.promisify)(import_child_process11.execFile);
16541
16524
 
16542
16525
  // src/services/preview/config-file.ts
16543
- var import_promises4 = __toESM(require("fs/promises"));
16526
+ var import_promises3 = __toESM(require("fs/promises"));
16544
16527
  var import_path5 = __toESM(require("path"));
16545
16528
  var CONFIG_DIR = ".codeam";
16546
16529
  var CONFIG_FILE = "preview.json";
@@ -16557,7 +16540,7 @@ var REQUIRED_FIELDS = [
16557
16540
  async function readPreviewConfig(cwd) {
16558
16541
  let raw;
16559
16542
  try {
16560
- raw = await import_promises4.default.readFile(configPath(cwd), "utf-8");
16543
+ raw = await import_promises3.default.readFile(configPath(cwd), "utf-8");
16561
16544
  } catch {
16562
16545
  return null;
16563
16546
  }
@@ -16576,8 +16559,8 @@ async function readPreviewConfig(cwd) {
16576
16559
  }
16577
16560
  async function writePreviewConfig(cwd, detection) {
16578
16561
  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");
16562
+ await import_promises3.default.mkdir(import_path5.default.dirname(filePath), { recursive: true });
16563
+ await import_promises3.default.writeFile(filePath, JSON.stringify(detection, null, 2) + "\n", "utf-8");
16581
16564
  }
16582
16565
 
16583
16566
  // src/services/preview/parser.ts
@@ -19147,33 +19130,26 @@ var previewStartH = (ctx, _cmd, parsed) => {
19147
19130
  { stdio: ["ignore", "pipe", "pipe"] }
19148
19131
  );
19149
19132
  let candidateUrl = null;
19133
+ let registered = false;
19150
19134
  const onTunnelChunk = (chunk) => {
19151
19135
  const s = chunk.toString();
19152
19136
  if (!candidateUrl) candidateUrl = parseCloudflaredUrl(s);
19137
+ if (/Registered tunnel connection/i.test(s)) registered = true;
19153
19138
  const trimmed = s.replace(/\n+$/g, "");
19154
19139
  if (trimmed.length > 0) log.info("preview", `cloudflared: ${trimmed}`);
19155
19140
  };
19156
19141
  candidate.stderr.on("data", onTunnelChunk);
19157
19142
  candidate.stdout.on("data", onTunnelChunk);
19158
- const urlDeadline = Date.now() + 45e3;
19159
- while (!candidateUrl && Date.now() < urlDeadline) {
19143
+ const deadline = Date.now() + 45e3;
19144
+ while ((!candidateUrl || !registered) && Date.now() < deadline) {
19160
19145
  await new Promise((r) => setTimeout(r, 250));
19161
19146
  }
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`);
19147
+ if (candidateUrl && registered) {
19148
+ log.info("preview", `cloudflared tunnel registered: ${candidateUrl}`);
19173
19149
  tunnel = candidate;
19174
19150
  parsedUrl = candidateUrl;
19175
- } catch (e) {
19176
- lastTunnelErr = e.message;
19151
+ } else {
19152
+ lastTunnelErr = candidateUrl ? "cloudflared did not register a tunnel connection within 45s" : "cloudflared did not emit a URL within 45s";
19177
19153
  try {
19178
19154
  candidate.kill("SIGTERM");
19179
19155
  } catch {
@@ -27191,16 +27167,16 @@ function checkChokidar() {
27191
27167
  }
27192
27168
  async function doctor(args2 = []) {
27193
27169
  const json = args2.includes("--json");
27194
- const cliVersion = true ? "2.39.38" : "0.0.0-dev";
27170
+ const cliVersion = true ? "2.39.39" : "0.0.0-dev";
27195
27171
  const apiBase2 = resolveApiBaseUrl();
27196
27172
  const diagnosticId = (0, import_node_crypto8.randomUUID)();
27197
27173
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
27198
- const [dns2, health] = await Promise.all([
27174
+ const [dns, health] = await Promise.all([
27199
27175
  checkDns(apiBase2),
27200
27176
  checkHealth(apiBase2)
27201
27177
  ]);
27202
27178
  const checks = [
27203
- dns2,
27179
+ dns,
27204
27180
  health,
27205
27181
  checkConfigDir(),
27206
27182
  checkSessions(),
@@ -27390,7 +27366,7 @@ async function completion(args2) {
27390
27366
  // src/commands/version.ts
27391
27367
  var import_picocolors13 = __toESM(require("picocolors"));
27392
27368
  function version2() {
27393
- const v = true ? "2.39.38" : "unknown";
27369
+ const v = true ? "2.39.39" : "unknown";
27394
27370
  console.log(`${import_picocolors13.default.bold("codeam-cli")} ${import_picocolors13.default.cyan(v)}`);
27395
27371
  }
27396
27372
 
@@ -27676,7 +27652,7 @@ function checkForUpdates() {
27676
27652
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
27677
27653
  if (process.env.CI) return;
27678
27654
  if (!process.stdout.isTTY) return;
27679
- const current = true ? "2.39.38" : null;
27655
+ const current = true ? "2.39.39" : null;
27680
27656
  if (!current) return;
27681
27657
  const cache = readCache();
27682
27658
  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.39",
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",