codeam-cli 2.26.5 → 2.26.7

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 +129 -17
  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.26.6] — 2026-06-03
8
+
9
+ ### Fixed
10
+
11
+ - **cli:** Preview shutdown is graceful + parser tolerates prose-wrapped JSON (#239)
12
+
13
+ ## [2.26.5] — 2026-06-03
14
+
15
+ ### Fixed
16
+
17
+ - **vsc-plugin:** CopilotLmStrategy matches normalized agentId `copilot` (#238)
18
+
7
19
  ## [2.26.4] — 2026-06-03
8
20
 
9
21
  ### Fixed
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.5",
475
+ version: "2.26.7",
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.5" : "0.0.0-dev",
5832
+ cliVersion: true ? "2.26.7" : "0.0.0-dev",
5833
5833
  nodeVersion: process.version,
5834
5834
  platform: process.platform,
5835
5835
  arch: process.arch,
@@ -15944,6 +15944,20 @@ var import_path4 = __toESM(require("path"));
15944
15944
  var import_promises2 = require("stream/promises");
15945
15945
  var import_which = __toESM(require("which"));
15946
15946
  var CACHED_BINARY = import_path4.default.join(import_os7.default.homedir(), ".codeam", "bin", "cloudflared");
15947
+ async function waitForCloudflaredReady(url, timeoutMs = 3e4) {
15948
+ const start2 = Date.now();
15949
+ while (Date.now() - start2 < timeoutMs) {
15950
+ try {
15951
+ const res = await fetch(url, { method: "HEAD" });
15952
+ if (res.status < 500) return;
15953
+ } catch {
15954
+ }
15955
+ await new Promise((r) => setTimeout(r, 500));
15956
+ }
15957
+ throw new Error(
15958
+ `Tunnel URL ${url} not reachable after ${timeoutMs}ms (DNS may still be propagating).`
15959
+ );
15960
+ }
15947
15961
  async function resolveCloudflared(opts = {}) {
15948
15962
  try {
15949
15963
  return await (0, import_which.default)("cloudflared");
@@ -16074,20 +16088,64 @@ var REQUIRED_FIELDS2 = [
16074
16088
  ];
16075
16089
  function safeParseDetection(raw) {
16076
16090
  if (!raw) return null;
16077
- const stripped = raw.replace(/^```(?:json)?\s*/m, "").replace(/\s*```\s*$/m, "").trim();
16078
- let parsed;
16079
- try {
16080
- parsed = JSON.parse(stripped);
16081
- } catch {
16082
- return null;
16091
+ let parsed = tryParseObject(raw.trim());
16092
+ if (!parsed) {
16093
+ const stripped = raw.replace(/^```(?:json)?\s*/m, "").replace(/\s*```\s*$/m, "").trim();
16094
+ if (stripped !== raw.trim()) {
16095
+ parsed = tryParseObject(stripped);
16096
+ }
16083
16097
  }
16084
- if (typeof parsed !== "object" || parsed === null) return null;
16098
+ if (!parsed) {
16099
+ const candidate = extractFirstJsonObject(raw);
16100
+ if (candidate) parsed = tryParseObject(candidate);
16101
+ }
16102
+ if (!parsed) return null;
16085
16103
  const obj = parsed;
16086
16104
  for (const field of REQUIRED_FIELDS2) {
16087
16105
  if (!(field in obj)) return null;
16088
16106
  }
16089
16107
  return obj;
16090
16108
  }
16109
+ function tryParseObject(s) {
16110
+ try {
16111
+ const v = JSON.parse(s);
16112
+ return typeof v === "object" && v !== null ? v : null;
16113
+ } catch {
16114
+ return null;
16115
+ }
16116
+ }
16117
+ function extractFirstJsonObject(s) {
16118
+ const start2 = s.indexOf("{");
16119
+ if (start2 < 0) return null;
16120
+ let depth = 0;
16121
+ let inString = false;
16122
+ let escaped = false;
16123
+ for (let i = start2; i < s.length; i += 1) {
16124
+ const c2 = s[i];
16125
+ if (escaped) {
16126
+ escaped = false;
16127
+ continue;
16128
+ }
16129
+ if (inString) {
16130
+ if (c2 === "\\") {
16131
+ escaped = true;
16132
+ } else if (c2 === '"') {
16133
+ inString = false;
16134
+ }
16135
+ continue;
16136
+ }
16137
+ if (c2 === '"') {
16138
+ inString = true;
16139
+ continue;
16140
+ }
16141
+ if (c2 === "{") depth += 1;
16142
+ else if (c2 === "}") {
16143
+ depth -= 1;
16144
+ if (depth === 0) return s.slice(start2, i + 1);
16145
+ }
16146
+ }
16147
+ return null;
16148
+ }
16091
16149
  var CLOUDFLARED_URL_RE = /https:\/\/[a-z0-9-]+\.trycloudflare\.com/i;
16092
16150
  function parseCloudflaredUrl(stderr) {
16093
16151
  const match = stderr.match(CLOUDFLARED_URL_RE);
@@ -16135,6 +16193,9 @@ async function killAllPreviews() {
16135
16193
  const ids = Array.from(activePreviews.keys());
16136
16194
  await Promise.all(ids.map((id) => killPreview(id)));
16137
16195
  }
16196
+ function activePreviewSessionIds() {
16197
+ return Array.from(activePreviews.keys());
16198
+ }
16138
16199
 
16139
16200
  // src/commands/start/handlers.ts
16140
16201
  var pendingAttachmentFiles = /* @__PURE__ */ new Set();
@@ -16872,10 +16933,12 @@ var previewStartH = (ctx, _cmd, parsed) => {
16872
16933
  const onTunnelChunk = (chunk) => {
16873
16934
  const s = chunk.toString();
16874
16935
  if (!parsedUrl) parsedUrl = parseCloudflaredUrl(s);
16936
+ const trimmed = s.replace(/\n+$/g, "");
16937
+ if (trimmed.length > 0) log.info("preview", `cloudflared: ${trimmed}`);
16875
16938
  };
16876
16939
  tunnel.stderr.on("data", onTunnelChunk);
16877
16940
  tunnel.stdout.on("data", onTunnelChunk);
16878
- const tunnelDeadline = Date.now() + 15e3;
16941
+ const tunnelDeadline = Date.now() + 45e3;
16879
16942
  while (!parsedUrl && Date.now() < tunnelDeadline) {
16880
16943
  await new Promise((r) => setTimeout(r, 250));
16881
16944
  }
@@ -16893,7 +16956,27 @@ var previewStartH = (ctx, _cmd, parsed) => {
16893
16956
  pluginId: ctx.pluginId,
16894
16957
  pluginAuthToken,
16895
16958
  type: "preview_error",
16896
- payload: { stage: "tunnel", message: "cloudflared did not emit a URL within 15s." }
16959
+ payload: { stage: "tunnel", message: "cloudflared did not emit a URL within 45s." }
16960
+ });
16961
+ return;
16962
+ }
16963
+ try {
16964
+ await waitForCloudflaredReady(parsedUrl, 3e4);
16965
+ } catch (e) {
16966
+ try {
16967
+ tunnel.kill("SIGTERM");
16968
+ } catch {
16969
+ }
16970
+ try {
16971
+ devServer.kill("SIGTERM");
16972
+ } catch {
16973
+ }
16974
+ void postPreviewEvent({
16975
+ sessionId: ctx.sessionId,
16976
+ pluginId: ctx.pluginId,
16977
+ pluginAuthToken,
16978
+ type: "preview_error",
16979
+ payload: { stage: "tunnel", message: e.message }
16897
16980
  });
16898
16981
  return;
16899
16982
  }
@@ -16939,8 +17022,16 @@ function runOnce(cmd, args2, cwd, env) {
16939
17022
  const child = (0, import_child_process15.spawn)(cmd, args2, {
16940
17023
  cwd,
16941
17024
  env: { ...process.env, ...env ?? {} },
16942
- stdio: "inherit"
17025
+ stdio: ["ignore", "pipe", "pipe"]
16943
17026
  });
17027
+ const tag = `setup:${cmd}`;
17028
+ const onChunk = (chunk) => {
17029
+ const text = chunk.toString().replace(/\n+$/g, "");
17030
+ if (text.length === 0) return;
17031
+ log.info("preview", `${tag}: ${text}`);
17032
+ };
17033
+ child.stdout?.on("data", onChunk);
17034
+ child.stderr?.on("data", onChunk);
16944
17035
  child.once("exit", (code) => resolve5(code));
16945
17036
  child.once("error", () => resolve5(-1));
16946
17037
  });
@@ -17158,7 +17249,10 @@ async function start(requestedAgent) {
17158
17249
  void outputSvc.sendTerminalExit(sessionId, exitCode);
17159
17250
  }
17160
17251
  });
17161
- function sigintHandler() {
17252
+ let shuttingDown = false;
17253
+ async function sigintHandler() {
17254
+ if (shuttingDown) return;
17255
+ shuttingDown = true;
17162
17256
  agent.kill();
17163
17257
  outputSvc.dispose();
17164
17258
  relay.stop();
@@ -17167,7 +17261,25 @@ async function start(requestedAgent) {
17167
17261
  closeAllTerminals();
17168
17262
  cleanupAttachmentTempFiles();
17169
17263
  killActiveSpawnAndCaptureChildren();
17170
- void killAllPreviews();
17264
+ const previewSessionIds = activePreviewSessionIds();
17265
+ try {
17266
+ await killAllPreviews();
17267
+ } catch {
17268
+ }
17269
+ const previewAuthToken = session?.pluginAuthToken;
17270
+ if (previewAuthToken && previewSessionIds.length > 0) {
17271
+ await Promise.allSettled(
17272
+ previewSessionIds.map(
17273
+ (sid) => postPreviewEvent({
17274
+ sessionId: sid,
17275
+ pluginId,
17276
+ pluginAuthToken: previewAuthToken,
17277
+ type: "preview_stopped",
17278
+ payload: { reason: "session_end" }
17279
+ })
17280
+ )
17281
+ );
17282
+ }
17171
17283
  void shutdownTelemetry();
17172
17284
  process.exit(0);
17173
17285
  }
@@ -19924,7 +20036,7 @@ function checkChokidar() {
19924
20036
  }
19925
20037
  async function doctor(args2 = []) {
19926
20038
  const json = args2.includes("--json");
19927
- const cliVersion = true ? "2.26.5" : "0.0.0-dev";
20039
+ const cliVersion = true ? "2.26.7" : "0.0.0-dev";
19928
20040
  const apiBase = resolveApiBaseUrl();
19929
20041
  const diagnosticId = (0, import_node_crypto6.randomUUID)();
19930
20042
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -20123,7 +20235,7 @@ async function completion(args2) {
20123
20235
  // src/commands/version.ts
20124
20236
  var import_picocolors13 = __toESM(require("picocolors"));
20125
20237
  function version2() {
20126
- const v = true ? "2.26.5" : "unknown";
20238
+ const v = true ? "2.26.7" : "unknown";
20127
20239
  console.log(`${import_picocolors13.default.bold("codeam-cli")} ${import_picocolors13.default.cyan(v)}`);
20128
20240
  }
20129
20241
 
@@ -20351,7 +20463,7 @@ function checkForUpdates() {
20351
20463
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
20352
20464
  if (process.env.CI) return;
20353
20465
  if (!process.stdout.isTTY) return;
20354
- const current = true ? "2.26.5" : null;
20466
+ const current = true ? "2.26.7" : null;
20355
20467
  if (!current) return;
20356
20468
  const cache = readCache();
20357
20469
  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.26.5",
3
+ "version": "2.26.7",
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",