codeam-cli 2.26.10 → 2.26.12

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,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.26.11] — 2026-06-03
8
+
9
+ ### Added
10
+
11
+ - **cli:** Emit preview_progress SSE events at each bring-up milestone (#244)
12
+
13
+ ## [2.26.10] — 2026-06-03
14
+
15
+ ### Fixed
16
+
17
+ - **cli:** Cloudflared DNS probe is best-effort, no longer fails the preview (#243)
18
+
7
19
  ## [2.26.9] — 2026-06-03
8
20
 
9
21
  ### Added
package/dist/index.js CHANGED
@@ -472,7 +472,7 @@ var import_qrcode_terminal = __toESM(require("qrcode-terminal"));
472
472
  // package.json
473
473
  var package_default = {
474
474
  name: "codeam-cli",
475
- version: "2.26.10",
475
+ version: "2.26.12",
476
476
  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.",
477
477
  type: "commonjs",
478
478
  main: "dist/index.js",
@@ -5829,7 +5829,7 @@ function readAnonId() {
5829
5829
  }
5830
5830
  function superProperties() {
5831
5831
  return {
5832
- cliVersion: true ? "2.26.10" : "0.0.0-dev",
5832
+ cliVersion: true ? "2.26.12" : "0.0.0-dev",
5833
5833
  nodeVersion: process.version,
5834
5834
  platform: process.platform,
5835
5835
  arch: process.arch,
@@ -16057,25 +16057,29 @@ async function linkDryRunPreflight(ctx) {
16057
16057
  }
16058
16058
 
16059
16059
  // src/services/preview/cloudflared.ts
16060
+ var import_promises = require("dns/promises");
16060
16061
  var import_fs = require("fs");
16061
- var import_promises = __toESM(require("fs/promises"));
16062
+ var import_promises2 = __toESM(require("fs/promises"));
16062
16063
  var import_os7 = __toESM(require("os"));
16063
16064
  var import_path4 = __toESM(require("path"));
16064
- var import_promises2 = require("stream/promises");
16065
+ var import_promises3 = require("stream/promises");
16065
16066
  var import_which = __toESM(require("which"));
16066
16067
  var CACHED_BINARY = import_path4.default.join(import_os7.default.homedir(), ".codeam", "bin", "cloudflared");
16067
- async function waitForCloudflaredReady(url, timeoutMs = 3e4) {
16068
+ async function waitForCloudflaredReady(url, timeoutMs = 6e4) {
16069
+ const hostname3 = new URL(url).hostname;
16070
+ const resolver = new import_promises.Resolver();
16071
+ resolver.setServers(["1.1.1.1", "1.0.0.1"]);
16068
16072
  const start2 = Date.now();
16069
16073
  while (Date.now() - start2 < timeoutMs) {
16070
16074
  try {
16071
- const res = await fetch(url, { method: "HEAD" });
16072
- if (res.status < 500) return;
16075
+ const addrs = await resolver.resolve4(hostname3);
16076
+ if (addrs.length > 0) return;
16073
16077
  } catch {
16074
16078
  }
16075
16079
  await new Promise((r) => setTimeout(r, 500));
16076
16080
  }
16077
16081
  throw new Error(
16078
- `Tunnel URL ${url} not reachable after ${timeoutMs}ms (DNS may still be propagating).`
16082
+ `DNS for ${hostname3} did not resolve within ${timeoutMs}ms (Cloudflare Quick Tunnel registration may have failed).`
16079
16083
  );
16080
16084
  }
16081
16085
  async function resolveCloudflared(opts = {}) {
@@ -16084,7 +16088,7 @@ async function resolveCloudflared(opts = {}) {
16084
16088
  } catch {
16085
16089
  }
16086
16090
  try {
16087
- await import_promises.default.access(CACHED_BINARY);
16091
+ await import_promises2.default.access(CACHED_BINARY);
16088
16092
  return CACHED_BINARY;
16089
16093
  } catch {
16090
16094
  }
@@ -16098,14 +16102,14 @@ async function resolveCloudflared(opts = {}) {
16098
16102
  }
16099
16103
  async function downloadCloudflared(target) {
16100
16104
  const url = downloadUrlForPlatform();
16101
- await import_promises.default.mkdir(import_path4.default.dirname(target), { recursive: true });
16105
+ await import_promises2.default.mkdir(import_path4.default.dirname(target), { recursive: true });
16102
16106
  const response = await fetch(url);
16103
16107
  if (!response.ok || !response.body) {
16104
16108
  throw new Error(
16105
16109
  `Failed to download cloudflared from ${url}: HTTP ${response.status}. Install manually from https://github.com/cloudflare/cloudflared/releases.`
16106
16110
  );
16107
16111
  }
16108
- await (0, import_promises2.pipeline)(
16112
+ await (0, import_promises3.pipeline)(
16109
16113
  response.body,
16110
16114
  (0, import_fs.createWriteStream)(target, { mode: 493 })
16111
16115
  );
@@ -16158,7 +16162,7 @@ async function waitForCodespacePortReady(url, timeoutMs = 15e3) {
16158
16162
  }
16159
16163
 
16160
16164
  // src/services/preview/config-file.ts
16161
- var import_promises3 = __toESM(require("fs/promises"));
16165
+ var import_promises4 = __toESM(require("fs/promises"));
16162
16166
  var import_path5 = __toESM(require("path"));
16163
16167
  var CONFIG_DIR = ".codeam";
16164
16168
  var CONFIG_FILE = "preview.json";
@@ -16175,7 +16179,7 @@ var REQUIRED_FIELDS = [
16175
16179
  async function readPreviewConfig(cwd) {
16176
16180
  let raw;
16177
16181
  try {
16178
- raw = await import_promises3.default.readFile(configPath(cwd), "utf-8");
16182
+ raw = await import_promises4.default.readFile(configPath(cwd), "utf-8");
16179
16183
  } catch {
16180
16184
  return null;
16181
16185
  }
@@ -16194,8 +16198,8 @@ async function readPreviewConfig(cwd) {
16194
16198
  }
16195
16199
  async function writePreviewConfig(cwd, detection) {
16196
16200
  const filePath = configPath(cwd);
16197
- await import_promises3.default.mkdir(import_path5.default.dirname(filePath), { recursive: true });
16198
- await import_promises3.default.writeFile(filePath, JSON.stringify(detection, null, 2) + "\n", "utf-8");
16201
+ await import_promises4.default.mkdir(import_path5.default.dirname(filePath), { recursive: true });
16202
+ await import_promises4.default.writeFile(filePath, JSON.stringify(detection, null, 2) + "\n", "utf-8");
16199
16203
  }
16200
16204
 
16201
16205
  // src/services/preview/parser.ts
@@ -16899,6 +16903,15 @@ var previewStartH = (ctx, _cmd, parsed) => {
16899
16903
  return;
16900
16904
  }
16901
16905
  const pluginAuthToken = ctx.pluginAuthToken;
16906
+ const emitProgress = (step, message) => {
16907
+ void postPreviewEvent({
16908
+ sessionId: ctx.sessionId,
16909
+ pluginId: ctx.pluginId,
16910
+ pluginAuthToken,
16911
+ type: "preview_progress",
16912
+ payload: { step, message, timestamp: Date.now() }
16913
+ });
16914
+ };
16902
16915
  void (async () => {
16903
16916
  void postPreviewEvent({
16904
16917
  sessionId: ctx.sessionId,
@@ -16907,7 +16920,9 @@ var previewStartH = (ctx, _cmd, parsed) => {
16907
16920
  type: "preview_starting",
16908
16921
  payload: { framework: detection.framework, port: detection.port }
16909
16922
  });
16923
+ emitProgress("ENV_DETECTED", `${detection.framework}`);
16910
16924
  for (const setup of detection.setup_commands ?? []) {
16925
+ emitProgress("SETUP_RUN", `${setup.cmd} ${setup.args.join(" ")}`);
16911
16926
  const exitCode = await runOnce(setup.cmd, setup.args, process.cwd(), detection.env);
16912
16927
  if (exitCode !== 0) {
16913
16928
  void postPreviewEvent({
@@ -16923,11 +16938,17 @@ var previewStartH = (ctx, _cmd, parsed) => {
16923
16938
  return;
16924
16939
  }
16925
16940
  }
16941
+ emitProgress(
16942
+ "BOOT_SEQUENCE",
16943
+ `${detection.command} ${detection.args.join(" ")}`
16944
+ );
16926
16945
  const devServer = (0, import_child_process15.spawn)(detection.command, detection.args, {
16927
16946
  cwd: process.cwd(),
16928
16947
  env: { ...process.env, ...detection.env ?? {} },
16929
16948
  stdio: ["ignore", "pipe", "pipe"]
16930
16949
  });
16950
+ emitProgress("BIND_PORT", String(detection.port));
16951
+ emitProgress("WAITING_FOR_READY", detection.ready_pattern);
16931
16952
  let readyMatched = false;
16932
16953
  let expoUrl = null;
16933
16954
  const readyRe = new RegExp(detection.ready_pattern);
@@ -16969,6 +16990,11 @@ var previewStartH = (ctx, _cmd, parsed) => {
16969
16990
  });
16970
16991
  return;
16971
16992
  }
16993
+ emitProgress("READY_DETECTED", `port ${detection.port}`);
16994
+ emitProgress(
16995
+ "TUNNEL_STARTING",
16996
+ detection.framework === "Expo" ? "Expo (self-tunnelled)" : isCodespaceSession() ? "GitHub Codespaces public port" : "cloudflared quick tunnel"
16997
+ );
16972
16998
  let tunnel = null;
16973
16999
  let url;
16974
17000
  if (detection.framework === "Expo") {
@@ -17080,9 +17106,33 @@ var previewStartH = (ctx, _cmd, parsed) => {
17080
17106
  });
17081
17107
  return;
17082
17108
  }
17083
- void waitForCloudflaredReady(parsedUrl, 3e4).then(() => log.info("preview", `cloudflared probe: ${parsedUrl} reachable from CLI host`)).catch((e) => log.info("preview", `cloudflared probe: ${String(e.message)} (non-fatal)`));
17109
+ try {
17110
+ await waitForCloudflaredReady(parsedUrl, 6e4);
17111
+ log.info("preview", `cloudflared probe: ${parsedUrl} reachable from CLI host`);
17112
+ } catch (e) {
17113
+ try {
17114
+ tunnel.kill("SIGTERM");
17115
+ } catch {
17116
+ }
17117
+ try {
17118
+ devServer.kill("SIGTERM");
17119
+ } catch {
17120
+ }
17121
+ void postPreviewEvent({
17122
+ sessionId: ctx.sessionId,
17123
+ pluginId: ctx.pluginId,
17124
+ pluginAuthToken,
17125
+ type: "preview_error",
17126
+ payload: {
17127
+ stage: "tunnel",
17128
+ message: `Tunnel ${parsedUrl} did not become reachable within 60s (${e.message}). Cloudflare Quick Tunnels occasionally fail to register \u2014 retry usually succeeds.`
17129
+ }
17130
+ });
17131
+ return;
17132
+ }
17084
17133
  url = parsedUrl;
17085
17134
  }
17135
+ emitProgress("TUNNEL_READY", url);
17086
17136
  registerPreview(ctx.sessionId, {
17087
17137
  sessionId: ctx.sessionId,
17088
17138
  devServer,
@@ -20137,7 +20187,7 @@ function checkChokidar() {
20137
20187
  }
20138
20188
  async function doctor(args2 = []) {
20139
20189
  const json = args2.includes("--json");
20140
- const cliVersion = true ? "2.26.10" : "0.0.0-dev";
20190
+ const cliVersion = true ? "2.26.12" : "0.0.0-dev";
20141
20191
  const apiBase = resolveApiBaseUrl();
20142
20192
  const diagnosticId = (0, import_node_crypto6.randomUUID)();
20143
20193
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -20336,7 +20386,7 @@ async function completion(args2) {
20336
20386
  // src/commands/version.ts
20337
20387
  var import_picocolors13 = __toESM(require("picocolors"));
20338
20388
  function version2() {
20339
- const v = true ? "2.26.10" : "unknown";
20389
+ const v = true ? "2.26.12" : "unknown";
20340
20390
  console.log(`${import_picocolors13.default.bold("codeam-cli")} ${import_picocolors13.default.cyan(v)}`);
20341
20391
  }
20342
20392
 
@@ -20564,7 +20614,7 @@ function checkForUpdates() {
20564
20614
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
20565
20615
  if (process.env.CI) return;
20566
20616
  if (!process.stdout.isTTY) return;
20567
- const current = true ? "2.26.10" : null;
20617
+ const current = true ? "2.26.12" : null;
20568
20618
  if (!current) return;
20569
20619
  const cache = readCache();
20570
20620
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ // src/postinstall.ts
5
+ var c = {
6
+ reset: "\x1B[0m",
7
+ bold: "\x1B[1m",
8
+ dim: "\x1B[2m",
9
+ green: "\x1B[32m",
10
+ cyan: "\x1B[36m",
11
+ violet: "\x1B[35m",
12
+ white: "\x1B[97m"
13
+ };
14
+ var lines = [
15
+ "",
16
+ ` ${c.violet}${c.bold}codeam-cli${c.reset} ${c.dim}\u2014 Claude Code remote control${c.reset}`,
17
+ "",
18
+ ` ${c.dim}1.${c.reset} Pair your phone:`,
19
+ ` ${c.cyan}codeam pair${c.reset}`,
20
+ "",
21
+ ` ${c.dim}2.${c.reset} Launch Claude Code with mobile control:`,
22
+ ` ${c.cyan}codeam${c.reset}`,
23
+ "",
24
+ ` ${c.dim}Other commands:${c.reset}`,
25
+ ` ${c.white}codeam sessions${c.reset} ${c.dim}list paired devices${c.reset}`,
26
+ ` ${c.white}codeam status${c.reset} ${c.dim}show connection info${c.reset}`,
27
+ ` ${c.white}codeam logout${c.reset} ${c.dim}remove all sessions${c.reset}`,
28
+ "",
29
+ ` ${c.dim}Requires Claude Code:${c.reset} ${c.green}npm install -g @anthropic-ai/claude-code${c.reset}`,
30
+ ` ${c.dim}Mobile app:${c.reset} ${c.green}https://www.codeagent-mobile.com${c.reset}`,
31
+ ""
32
+ ];
33
+ process.stdout.write(lines.join("\n") + "\n");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.26.10",
3
+ "version": "2.26.12",
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",