codeam-cli 2.26.13 → 2.26.15

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,25 @@ 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.14] — 2026-06-04
8
+
9
+ ### Chore
10
+
11
+ - **deps:** Bump actions/setup-node from 4 to 6 (#246)
12
+ - **deps:** Bump actions/checkout from 4 to 6 (#247)
13
+ - **deps:** Bump org.jetbrains.kotlin.jvm in /apps/jetbrains-plugin (#248)
14
+ - **cli:** Bump which 2.0.2 → 5.0.0 (#251)
15
+
16
+ ### Fixed
17
+
18
+ - **cli:** Drop redundant install from agent setup_commands + warn agent (#256)
19
+
20
+ ## [2.26.13] — 2026-06-04
21
+
22
+ ### Added
23
+
24
+ - **cli:** Pre-flight install for missing node_modules before preview (#250)
25
+
7
26
  ## [2.26.12] — 2026-06-04
8
27
 
9
28
  ### Fixed
package/dist/index.js CHANGED
@@ -344,7 +344,7 @@ Return ONLY a JSON object on stdout (no prose, no markdown fences):
344
344
  "port": <number>,
345
345
  "ready_pattern": "<regex matching the server-ready stdout line>",
346
346
  "env": { "HOST": "0.0.0.0" },
347
- "setup_commands": [{"cmd":"npm","args":["install"]}],
347
+ "setup_commands": [],
348
348
  "notes": "<one-line caveat or null>"
349
349
  }
350
350
 
@@ -354,6 +354,20 @@ Rules:
354
354
  - For Expo: framework="Expo", command="npx", args=["expo","start","--tunnel"], port=8081, notes="Scan QR with Expo Go".
355
355
  - If no dev server applies (CLI library, lambda, batch script): {"framework":"unsupported","notes":"<reason>"}.
356
356
 
357
+ CRITICAL \u2014 setup_commands:
358
+ - DO NOT include an install command (npm install, pnpm install, yarn install,
359
+ yarn, bun install) in setup_commands. A lockfile-aware pre-flight installer
360
+ runs BEFORE setup_commands and picks the correct package manager from the
361
+ lockfile present (pnpm-lock.yaml -> pnpm, yarn.lock -> yarn, bun.lockb -> bun,
362
+ else npm). Emitting an install here either duplicates that work or, worse,
363
+ uses the WRONG package manager on top of node_modules just populated by the
364
+ pre-flight, which crashes (e.g. npm errors with "Cannot read properties of
365
+ null (reading 'matches')" when run over pnpm's .pnpm/ layout).
366
+ - ONLY include setup_commands for genuinely non-install work the project needs
367
+ before its dev server can boot: prisma generate, codegen, prebuild scripts,
368
+ database migrations against a local SQLite, etc.
369
+ - For most projects, setup_commands should be an empty array [].
370
+
357
371
  OUTPUT JSON ONLY. NO MARKDOWN. NO COMMENTARY.
358
372
  `.trim();
359
373
 
@@ -472,7 +486,7 @@ var import_qrcode_terminal = __toESM(require("qrcode-terminal"));
472
486
  // package.json
473
487
  var package_default = {
474
488
  name: "codeam-cli",
475
- version: "2.26.13",
489
+ version: "2.26.15",
476
490
  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
491
  type: "commonjs",
478
492
  main: "dist/index.js",
@@ -546,7 +560,7 @@ var package_default = {
546
560
  chokidar: "^3.6.0",
547
561
  picocolors: "^1.1.0",
548
562
  "qrcode-terminal": "^0.12.0",
549
- which: "^2.0.2",
563
+ which: "^5.0.0",
550
564
  ws: "^8.18.0",
551
565
  zod: "^4.3.6"
552
566
  },
@@ -5829,7 +5843,7 @@ function readAnonId() {
5829
5843
  }
5830
5844
  function superProperties() {
5831
5845
  return {
5832
- cliVersion: true ? "2.26.13" : "0.0.0-dev",
5846
+ cliVersion: true ? "2.26.15" : "0.0.0-dev",
5833
5847
  nodeVersion: process.version,
5834
5848
  platform: process.platform,
5835
5849
  arch: process.arch,
@@ -10874,6 +10888,40 @@ async function extractLocalCodexToken() {
10874
10888
  function codexCredentialsPaths() {
10875
10889
  return [codexCredentialsPath()];
10876
10890
  }
10891
+ function validateLocalCodexToken(credential) {
10892
+ let parsed;
10893
+ try {
10894
+ parsed = JSON.parse(credential);
10895
+ } catch {
10896
+ return { status: "unknown" };
10897
+ }
10898
+ const directExp = typeof parsed.expires_at === "number" ? parsed.expires_at : typeof parsed.tokens?.expires_at === "number" ? parsed.tokens.expires_at : void 0;
10899
+ const jwtExp = decodeJwtExp(parsed.tokens?.id_token);
10900
+ const exp = directExp ?? jwtExp;
10901
+ if (exp === void 0) return { status: "unknown" };
10902
+ const expiresAt = exp < 1e12 ? exp * 1e3 : exp;
10903
+ if (Date.now() >= expiresAt) {
10904
+ return {
10905
+ status: "expired",
10906
+ reason: "Codex OAuth access token expired",
10907
+ expiresAt
10908
+ };
10909
+ }
10910
+ return { status: "valid", expiresAt };
10911
+ }
10912
+ function decodeJwtExp(jwt) {
10913
+ if (!jwt) return void 0;
10914
+ const parts = jwt.split(".");
10915
+ if (parts.length !== 3) return void 0;
10916
+ try {
10917
+ const base64 = parts[1].replace(/-/g, "+").replace(/_/g, "/");
10918
+ const padded = base64 + "===".slice((base64.length + 3) % 4);
10919
+ const payload = JSON.parse(Buffer.from(padded, "base64").toString("utf8"));
10920
+ return typeof payload.exp === "number" ? payload.exp : void 0;
10921
+ } catch {
10922
+ return void 0;
10923
+ }
10924
+ }
10877
10925
 
10878
10926
  // src/agents/codex/link.ts
10879
10927
  function codexCredentialLocator() {
@@ -10882,7 +10930,11 @@ function codexCredentialLocator() {
10882
10930
  vendor: "OpenAI",
10883
10931
  hint: "~/.codex/auth.json",
10884
10932
  watchPaths: codexCredentialsPaths,
10885
- extract: extractLocalCodexToken
10933
+ extract: extractLocalCodexToken,
10934
+ validate: (token) => {
10935
+ const result = validateLocalCodexToken(token.credential);
10936
+ return { status: result.status, reason: result.reason };
10937
+ }
10886
10938
  };
10887
10939
  }
10888
10940
  function codexLoginLauncher() {
@@ -15893,6 +15945,7 @@ async function link(args2 = []) {
15893
15945
  installSpin.stop(`${ctx.displayName} is installed`);
15894
15946
  const existing = await ctx.locator.extract();
15895
15947
  if (existing) {
15948
+ if (refuseIfStale(ctx, existing)) return;
15896
15949
  showInfo(`Found existing ${ctx.displayName} credentials at ${import_picocolors2.default.bold(existing.source)}.`);
15897
15950
  await uploadAndSucceed(ctx, paired, pluginId, existing);
15898
15951
  return;
@@ -15909,8 +15962,26 @@ async function link(args2 = []) {
15909
15962
  console.log("");
15910
15963
  const captured = await captureFreshCredentials(ctx);
15911
15964
  console.log("");
15965
+ if (refuseIfStale(ctx, captured)) return;
15912
15966
  await uploadAndSucceed(ctx, paired, pluginId, captured);
15913
15967
  }
15968
+ function refuseIfStale(ctx, token) {
15969
+ const verdict = ctx.locator.validate?.(token);
15970
+ if (!verdict || verdict.status !== "expired") return false;
15971
+ const reason = verdict.reason ?? "Token expired";
15972
+ showError(
15973
+ `Your local ${ctx.displayName} credentials at ${import_picocolors2.default.bold(ctx.locator.hint)} are already expired:
15974
+ ${reason}
15975
+
15976
+ Uploading them would land a dead snapshot in the vault \u2014 every codespace bootstrapped from it would immediately return 401.
15977
+
15978
+ Run on your machine, then re-link:
15979
+ ${import_picocolors2.default.cyan(`${ctx.binary} logout`)}
15980
+ ${import_picocolors2.default.cyan(`${ctx.binary} login`)}
15981
+ ${import_picocolors2.default.cyan(`codeam link ${ctx.locator.publicId}`)}`
15982
+ );
15983
+ process.exit(1);
15984
+ }
15914
15985
  async function captureFreshCredentials(ctx) {
15915
15986
  const isWin = ctx.runtime.os.id === "win32";
15916
15987
  const watcher = import_chokidar.default.watch(ctx.locator.watchPaths(), {
@@ -16298,6 +16369,13 @@ function detectMissingNodeDeps(cwd) {
16298
16369
  }
16299
16370
  return { cmd: "npm", args: ["install"] };
16300
16371
  }
16372
+ function isJsInstallCommand(cmd, args2) {
16373
+ const known = ["npm", "pnpm", "yarn", "bun"];
16374
+ if (!known.includes(cmd)) return false;
16375
+ if (cmd === "yarn" && args2.length === 0) return true;
16376
+ const verb = args2[0];
16377
+ return verb === "install" || verb === "i" || verb === "ci";
16378
+ }
16301
16379
 
16302
16380
  // src/services/preview/index.ts
16303
16381
  var activePreviews = /* @__PURE__ */ new Map();
@@ -16940,6 +17018,7 @@ var previewStartH = (ctx, _cmd, parsed) => {
16940
17018
  });
16941
17019
  emitProgress("ENV_DETECTED", `${detection.framework}`);
16942
17020
  const missingDeps = detectMissingNodeDeps(process.cwd());
17021
+ let preflightRan = false;
16943
17022
  if (missingDeps) {
16944
17023
  emitProgress(
16945
17024
  "SETUP_RUN",
@@ -16964,8 +17043,12 @@ var previewStartH = (ctx, _cmd, parsed) => {
16964
17043
  });
16965
17044
  return;
16966
17045
  }
17046
+ preflightRan = true;
16967
17047
  }
16968
- for (const setup of detection.setup_commands ?? []) {
17048
+ const agentSetupCommands = preflightRan ? (detection.setup_commands ?? []).filter(
17049
+ (s) => !isJsInstallCommand(s.cmd, s.args)
17050
+ ) : detection.setup_commands ?? [];
17051
+ for (const setup of agentSetupCommands) {
16969
17052
  emitProgress("SETUP_RUN", `${setup.cmd} ${setup.args.join(" ")}`);
16970
17053
  const exitCode = await runOnce(setup.cmd, setup.args, process.cwd(), detection.env);
16971
17054
  if (exitCode !== 0) {
@@ -20231,7 +20314,7 @@ function checkChokidar() {
20231
20314
  }
20232
20315
  async function doctor(args2 = []) {
20233
20316
  const json = args2.includes("--json");
20234
- const cliVersion = true ? "2.26.13" : "0.0.0-dev";
20317
+ const cliVersion = true ? "2.26.15" : "0.0.0-dev";
20235
20318
  const apiBase = resolveApiBaseUrl();
20236
20319
  const diagnosticId = (0, import_node_crypto6.randomUUID)();
20237
20320
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -20430,7 +20513,7 @@ async function completion(args2) {
20430
20513
  // src/commands/version.ts
20431
20514
  var import_picocolors13 = __toESM(require("picocolors"));
20432
20515
  function version2() {
20433
- const v = true ? "2.26.13" : "unknown";
20516
+ const v = true ? "2.26.15" : "unknown";
20434
20517
  console.log(`${import_picocolors13.default.bold("codeam-cli")} ${import_picocolors13.default.cyan(v)}`);
20435
20518
  }
20436
20519
 
@@ -20658,7 +20741,7 @@ function checkForUpdates() {
20658
20741
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
20659
20742
  if (process.env.CI) return;
20660
20743
  if (!process.stdout.isTTY) return;
20661
- const current = true ? "2.26.13" : null;
20744
+ const current = true ? "2.26.15" : null;
20662
20745
  if (!current) return;
20663
20746
  const cache = readCache();
20664
20747
  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.13",
3
+ "version": "2.26.15",
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",
@@ -74,7 +74,7 @@
74
74
  "chokidar": "^3.6.0",
75
75
  "picocolors": "^1.1.0",
76
76
  "qrcode-terminal": "^0.12.0",
77
- "which": "^2.0.2",
77
+ "which": "^5.0.0",
78
78
  "ws": "^8.18.0",
79
79
  "zod": "^4.3.6"
80
80
  },