codeam-cli 2.47.1 → 2.48.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.
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.47.1] — 2026-06-26
8
+
9
+ ### Chore
10
+
11
+ - **plugins:** Cloud-fallback follow-ups — test await, validated narrowing, repoSlug+learnMoreUrl tests, path-preserving probe URL, detached-HEAD parity, drop vestigial param (#393)
12
+
7
13
  ## [2.47.0] — 2026-06-26
8
14
 
9
15
  ### Added
package/dist/index.js CHANGED
@@ -1093,12 +1093,12 @@ var PromiseQueue = class {
1093
1093
  return promise;
1094
1094
  }
1095
1095
  async join() {
1096
- let promises2 = Object.values(this.promiseByIds);
1097
- let length = promises2.length;
1096
+ let promises3 = Object.values(this.promiseByIds);
1097
+ let length = promises3.length;
1098
1098
  while (length > 0) {
1099
- await Promise.all(promises2);
1100
- promises2 = Object.values(this.promiseByIds);
1101
- length = promises2.length;
1099
+ await Promise.all(promises3);
1100
+ promises3 = Object.values(this.promiseByIds);
1101
+ length = promises3.length;
1102
1102
  }
1103
1103
  }
1104
1104
  get length() {
@@ -1436,8 +1436,8 @@ function safeSetTimeout(fn, timeout) {
1436
1436
  return t2;
1437
1437
  }
1438
1438
  var isError = (x) => x instanceof Error;
1439
- function allSettled(promises2) {
1440
- return Promise.all(promises2.map((p2) => (p2 ?? Promise.resolve()).then((value) => ({
1439
+ function allSettled(promises3) {
1440
+ return Promise.all(promises3.map((p2) => (p2 ?? Promise.resolve()).then((value) => ({
1441
1441
  status: "fulfilled",
1442
1442
  value
1443
1443
  }), (reason) => ({
@@ -5397,7 +5397,7 @@ function readAnonId() {
5397
5397
  }
5398
5398
  function superProperties() {
5399
5399
  return {
5400
- cliVersion: true ? "2.47.1" : "0.0.0-dev",
5400
+ cliVersion: true ? "2.48.0" : "0.0.0-dev",
5401
5401
  nodeVersion: process.version,
5402
5402
  platform: process.platform,
5403
5403
  arch: process.arch,
@@ -5578,7 +5578,7 @@ var os4 = __toESM(require("os"));
5578
5578
  // package.json
5579
5579
  var package_default = {
5580
5580
  name: "codeam-cli",
5581
- version: "2.47.1",
5581
+ version: "2.48.0",
5582
5582
  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.",
5583
5583
  type: "commonjs",
5584
5584
  main: "dist/index.js",
@@ -7817,7 +7817,16 @@ var startCommandSchema = import_zod.z.object({
7817
7817
  })
7818
7818
  ).max(32).optional(),
7819
7819
  notes: import_zod.z.string().max(4096).nullable().optional()
7820
- }).optional()
7820
+ }).optional(),
7821
+ // `env_write` carries the full desired set of environment variables
7822
+ // for the project `.env`. Bounded so a malformed payload can't flood
7823
+ // the disk-side serializer. `env_read` / `preview_restart` send no payload.
7824
+ vars: import_zod.z.array(
7825
+ import_zod.z.object({
7826
+ key: import_zod.z.string().min(1).max(256),
7827
+ value: import_zod.z.string().max(32768)
7828
+ })
7829
+ ).max(512).optional()
7821
7830
  });
7822
7831
  function parsePayload2(schema, raw) {
7823
7832
  const result = schema.safeParse(raw);
@@ -13991,6 +14000,47 @@ async function writePreviewConfig(cwd, detection) {
13991
14000
  await import_promises3.default.writeFile(filePath, JSON.stringify(detection, null, 2) + "\n", "utf-8");
13992
14001
  }
13993
14002
 
14003
+ // src/services/preview/dotenv.ts
14004
+ var ENV_KEY_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
14005
+ function parseDotenv(raw) {
14006
+ const order = [];
14007
+ const map = /* @__PURE__ */ new Map();
14008
+ for (const rawLine of raw.split(/\r?\n/)) {
14009
+ const line = rawLine.trim();
14010
+ if (line === "" || line.startsWith("#")) continue;
14011
+ const body = line.startsWith("export ") ? line.slice("export ".length).trim() : line;
14012
+ const eq = body.indexOf("=");
14013
+ if (eq <= 0) continue;
14014
+ const key = body.slice(0, eq).trim();
14015
+ if (!ENV_KEY_RE.test(key)) continue;
14016
+ let value = body.slice(eq + 1).trim();
14017
+ value = unquote(value);
14018
+ if (!map.has(key)) order.push(key);
14019
+ map.set(key, value);
14020
+ }
14021
+ return order.map((key) => ({ key, value: map.get(key) }));
14022
+ }
14023
+ function unquote(value) {
14024
+ if (value.length >= 2 && value.startsWith('"') && value.endsWith('"')) {
14025
+ return value.slice(1, -1).replace(/\\n/g, "\n").replace(/\\"/g, '"');
14026
+ }
14027
+ if (value.length >= 2 && value.startsWith("'") && value.endsWith("'")) {
14028
+ return value.slice(1, -1);
14029
+ }
14030
+ return value;
14031
+ }
14032
+ function serializeDotenv(vars) {
14033
+ const lines = vars.map(({ key, value }) => `${key}=${quoteIfNeeded(value)}`);
14034
+ return `# Managed by CodeAgent
14035
+ ${lines.join("\n")}${lines.length ? "\n" : ""}`;
14036
+ }
14037
+ function quoteIfNeeded(value) {
14038
+ if (/[\s#=]/.test(value) || value.includes("\n")) {
14039
+ return `"${value.replace(/"/g, '\\"').replace(/\n/g, "\\n")}"`;
14040
+ }
14041
+ return value;
14042
+ }
14043
+
13994
14044
  // src/services/preview/parser.ts
13995
14045
  var REQUIRED_FIELDS2 = [
13996
14046
  "framework",
@@ -15917,6 +15967,51 @@ var listFiles = async (ctx, cmd, parsed) => {
15917
15967
  const result = await listProjectFiles({ query: parsed.query });
15918
15968
  await ctx.relay.sendResult(cmd.id, "completed", result);
15919
15969
  };
15970
+ var envReadH = async (ctx, cmd) => {
15971
+ const envPath = path40.join(process.cwd(), ".env");
15972
+ try {
15973
+ const raw = await fs34.promises.readFile(envPath, "utf8");
15974
+ await ctx.relay.sendResult(cmd.id, "completed", {
15975
+ exists: true,
15976
+ vars: parseDotenv(raw)
15977
+ });
15978
+ } catch (err) {
15979
+ if (err.code === "ENOENT") {
15980
+ await ctx.relay.sendResult(cmd.id, "completed", { exists: false, vars: [] });
15981
+ return;
15982
+ }
15983
+ await ctx.relay.sendResult(cmd.id, "failed", { error: err.message });
15984
+ }
15985
+ };
15986
+ var envWriteH = async (ctx, cmd, parsed) => {
15987
+ const vars = parsed.vars;
15988
+ if (!Array.isArray(vars)) {
15989
+ await ctx.relay.sendResult(cmd.id, "failed", { error: "Missing vars" });
15990
+ return;
15991
+ }
15992
+ const seen = /* @__PURE__ */ new Set();
15993
+ for (const v of vars) {
15994
+ if (!ENV_KEY_RE.test(v.key)) {
15995
+ await ctx.relay.sendResult(cmd.id, "failed", { error: `Invalid key: ${v.key}` });
15996
+ return;
15997
+ }
15998
+ if (seen.has(v.key)) {
15999
+ await ctx.relay.sendResult(cmd.id, "failed", { error: `Duplicate key: ${v.key}` });
16000
+ return;
16001
+ }
16002
+ seen.add(v.key);
16003
+ }
16004
+ const envPath = path40.join(process.cwd(), ".env");
16005
+ const tmpPath = path40.join(process.cwd(), ".env.codeam.tmp");
16006
+ try {
16007
+ await fs34.promises.writeFile(tmpPath, serializeDotenv(vars), "utf8");
16008
+ await fs34.promises.rename(tmpPath, envPath);
16009
+ await ctx.relay.sendResult(cmd.id, "completed", { ok: true, count: vars.length });
16010
+ } catch (err) {
16011
+ await fs34.promises.rm(tmpPath, { force: true }).catch(() => void 0);
16012
+ await ctx.relay.sendResult(cmd.id, "failed", { error: err.message });
16013
+ }
16014
+ };
15920
16015
  var terminalOpenH = async (ctx, cmd, parsed) => {
15921
16016
  const r = openTerminal({
15922
16017
  cols: typeof parsed.cols === "number" ? parsed.cols : void 0,
@@ -16372,8 +16467,9 @@ var previewStartH = (ctx, _cmd, parsed) => {
16372
16467
  log.info("preview", "start: no detection in payload");
16373
16468
  return;
16374
16469
  }
16375
- const detection = rawDetection;
16376
- const pluginAuthToken = ctx.pluginAuthToken;
16470
+ startPreviewFromDetection(ctx, rawDetection, ctx.pluginAuthToken);
16471
+ };
16472
+ function startPreviewFromDetection(ctx, detection, pluginAuthToken) {
16377
16473
  const emitProgress = (step, message) => {
16378
16474
  void postPreviewEvent({
16379
16475
  sessionId: ctx.sessionId,
@@ -16791,7 +16887,8 @@ var previewStartH = (ctx, _cmd, parsed) => {
16791
16887
  devServer,
16792
16888
  tunnel,
16793
16889
  url,
16794
- framework: detection.framework
16890
+ framework: detection.framework,
16891
+ detection
16795
16892
  });
16796
16893
  log.info("preview", `ready: ${detection.framework} at ${url}`);
16797
16894
  void postPreviewEvent({
@@ -16812,7 +16909,7 @@ var previewStartH = (ctx, _cmd, parsed) => {
16812
16909
  payload: { stage: "spawn", message: `Preview failed to start: ${message}` }
16813
16910
  });
16814
16911
  });
16815
- };
16912
+ }
16816
16913
  var previewStopH = (ctx) => {
16817
16914
  if (!ctx.pluginAuthToken) {
16818
16915
  log.info("preview", "no pluginAuthToken \u2014 skipping stop");
@@ -16831,6 +16928,21 @@ var previewStopH = (ctx) => {
16831
16928
  });
16832
16929
  })();
16833
16930
  };
16931
+ var previewRestartH = async (ctx, cmd) => {
16932
+ if (!ctx.pluginAuthToken) {
16933
+ await ctx.relay.sendResult(cmd.id, "completed", { restarted: false });
16934
+ return;
16935
+ }
16936
+ const preview = activePreviews.get(ctx.sessionId);
16937
+ if (!preview) {
16938
+ await ctx.relay.sendResult(cmd.id, "completed", { restarted: false });
16939
+ return;
16940
+ }
16941
+ await killPreview(ctx.sessionId);
16942
+ await new Promise((r) => setTimeout(r, 150));
16943
+ startPreviewFromDetection(ctx, preview.detection, ctx.pluginAuthToken);
16944
+ await ctx.relay.sendResult(cmd.id, "completed", { restarted: true });
16945
+ };
16834
16946
  var savePreviewConfigH = (_ctx, _cmd, parsed) => {
16835
16947
  const detection = parsed.detection;
16836
16948
  if (!detection) {
@@ -16880,7 +16992,10 @@ var handlers = {
16880
16992
  request_preview_detect: requestPreviewDetectH,
16881
16993
  preview_start: previewStartH,
16882
16994
  preview_stop: previewStopH,
16883
- save_preview_config: savePreviewConfigH
16995
+ preview_restart: previewRestartH,
16996
+ save_preview_config: savePreviewConfigH,
16997
+ env_read: envReadH,
16998
+ env_write: envWriteH
16884
16999
  };
16885
17000
  async function dispatchCommand(ctx, cmd) {
16886
17001
  if (cmd.type === "beads_action") {
@@ -18022,7 +18137,7 @@ async function autoUpgradeBeforeCriticalCommand() {
18022
18137
  if (process.env.NODE_ENV === "test") return;
18023
18138
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
18024
18139
  if (process.env.CI) return;
18025
- const current = true ? "2.47.1" : null;
18140
+ const current = true ? "2.48.0" : null;
18026
18141
  if (!current) return;
18027
18142
  const cache = readCache();
18028
18143
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -18039,7 +18154,7 @@ function checkForUpdates() {
18039
18154
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
18040
18155
  if (process.env.CI) return;
18041
18156
  if (!process.stdout.isTTY) return;
18042
- const current = true ? "2.47.1" : null;
18157
+ const current = true ? "2.48.0" : null;
18043
18158
  if (!current) return;
18044
18159
  const cache = readCache();
18045
18160
  const fresh = cache && Date.now() - cache.fetchedAt < TTL_MS;
@@ -18481,7 +18596,7 @@ var defaultSpawner = (env, cwd, args2 = []) => (0, import_node_child_process14.s
18481
18596
  detached: false
18482
18597
  });
18483
18598
  function currentCliVersion() {
18484
- return true ? "2.47.1" : null;
18599
+ return true ? "2.48.0" : null;
18485
18600
  }
18486
18601
  function runCmd(cmd, args2, timeoutMs) {
18487
18602
  return new Promise((resolve7) => {
@@ -29849,7 +29964,7 @@ function checkChokidar() {
29849
29964
  }
29850
29965
  async function doctor(args2 = []) {
29851
29966
  const json = args2.includes("--json");
29852
- const cliVersion = true ? "2.47.1" : "0.0.0-dev";
29967
+ const cliVersion = true ? "2.48.0" : "0.0.0-dev";
29853
29968
  const apiBase2 = resolveApiBaseUrl();
29854
29969
  const diagnosticId = (0, import_node_crypto8.randomUUID)();
29855
29970
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -30048,7 +30163,7 @@ async function completion(args2) {
30048
30163
  // src/commands/version.ts
30049
30164
  var import_picocolors15 = __toESM(require("picocolors"));
30050
30165
  function version2() {
30051
- const v = true ? "2.47.1" : "unknown";
30166
+ const v = true ? "2.48.0" : "unknown";
30052
30167
  console.log(`${import_picocolors15.default.bold("codeam-cli")} ${import_picocolors15.default.cyan(v)}`);
30053
30168
  }
30054
30169
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.47.1",
3
+ "version": "2.48.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",