codeam-cli 2.1.1 → 2.2.1

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 (2) hide show
  1. package/dist/index.js +225 -73
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -87,9 +87,9 @@ var require_src = __commonJS({
87
87
  });
88
88
 
89
89
  // src/commands/start.ts
90
- var fs5 = __toESM(require("fs"));
90
+ var fs6 = __toESM(require("fs"));
91
91
  var os5 = __toESM(require("os"));
92
- var path5 = __toESM(require("path"));
92
+ var path6 = __toESM(require("path"));
93
93
  var import_crypto = require("crypto");
94
94
  var import_child_process3 = require("child_process");
95
95
  var import_picocolors2 = __toESM(require("picocolors"));
@@ -179,7 +179,7 @@ var import_qrcode_terminal = __toESM(require("qrcode-terminal"));
179
179
  // package.json
180
180
  var package_default = {
181
181
  name: "codeam-cli",
182
- version: "2.1.1",
182
+ version: "2.2.1",
183
183
  description: "Remote control Claude Code (and other AI coding agents) from your mobile phone. Pair your device, send prompts, stream responses in real-time, and approve commands \u2014 from anywhere.",
184
184
  main: "dist/index.js",
185
185
  bin: {
@@ -298,6 +298,16 @@ function showPairingCode(code, expiresAt) {
298
298
 
299
299
  // src/services/websocket.service.ts
300
300
  var import_ws = __toESM(require("ws"));
301
+
302
+ // src/lib/poll-delay.ts
303
+ var MAX_DELAY_MS = 3e4;
304
+ function computePollDelay({ baseMs, failures }) {
305
+ const exp = Math.min(MAX_DELAY_MS, baseMs * Math.pow(2, failures));
306
+ const jitter = exp * (0.9 + Math.random() * 0.2);
307
+ return Math.round(jitter);
308
+ }
309
+
310
+ // src/services/websocket.service.ts
301
311
  var API_BASE = process.env.CODEAM_API_URL ?? "https://codeagent-mobile-api.vercel.app";
302
312
  var WS_URL = API_BASE.replace("https://", "wss://").replace("http://", "ws://") + "/api/ws";
303
313
  var HEARTBEAT_MS = 3e4;
@@ -350,7 +360,7 @@ var WebSocketService = class {
350
360
  this.handlers.forEach((h) => h.onDisconnected());
351
361
  if (this.reconnectAttempts < MAX_RECONNECT) {
352
362
  this.reconnectAttempts++;
353
- const delay = Math.min(1e3 * 2 ** this.reconnectAttempts, 3e4);
363
+ const delay = computePollDelay({ baseMs: 1e3, failures: this.reconnectAttempts });
354
364
  this.reconnectTimer = setTimeout(() => this.connect(), delay);
355
365
  }
356
366
  });
@@ -393,16 +403,6 @@ var WebSocketService = class {
393
403
  var https = __toESM(require("https"));
394
404
  var http = __toESM(require("http"));
395
405
  var os2 = __toESM(require("os"));
396
-
397
- // src/lib/poll-delay.ts
398
- var MAX_DELAY_MS = 3e4;
399
- function computePollDelay({ baseMs, failures }) {
400
- const exp = Math.min(MAX_DELAY_MS, baseMs * Math.pow(2, failures));
401
- const jitter = exp * (0.9 + Math.random() * 0.2);
402
- return Math.round(jitter);
403
- }
404
-
405
- // src/services/pairing.service.ts
406
406
  var API_BASE2 = process.env.CODEAM_API_URL ?? "https://codeagent-mobile-api.vercel.app";
407
407
  async function requestCode(pluginId) {
408
408
  try {
@@ -476,7 +476,7 @@ var _transport = {
476
476
  getJson: _getJson
477
477
  };
478
478
  async function _postJson(url, body) {
479
- return new Promise((resolve, reject) => {
479
+ return new Promise((resolve2, reject) => {
480
480
  const data = JSON.stringify(body);
481
481
  const u2 = new URL(url);
482
482
  const transport = u2.protocol === "https:" ? https : http;
@@ -504,9 +504,9 @@ async function _postJson(url, body) {
504
504
  return;
505
505
  }
506
506
  try {
507
- resolve(JSON.parse(body2));
507
+ resolve2(JSON.parse(body2));
508
508
  } catch {
509
- resolve(null);
509
+ resolve2(null);
510
510
  }
511
511
  });
512
512
  }
@@ -521,7 +521,7 @@ async function _postJson(url, body) {
521
521
  });
522
522
  }
523
523
  async function _getJson(url) {
524
- return new Promise((resolve, reject) => {
524
+ return new Promise((resolve2, reject) => {
525
525
  const u2 = new URL(url);
526
526
  const transport = u2.protocol === "https:" ? https : http;
527
527
  const req = transport.request(
@@ -544,9 +544,9 @@ async function _getJson(url) {
544
544
  return;
545
545
  }
546
546
  try {
547
- resolve(JSON.parse(body));
547
+ resolve2(JSON.parse(body));
548
548
  } catch {
549
- resolve(null);
549
+ resolve2(null);
550
550
  }
551
551
  });
552
552
  }
@@ -1661,20 +1661,20 @@ var OutputService = class _OutputService {
1661
1661
  if (this.pluginAuthToken) {
1662
1662
  headers["X-Plugin-Auth-Token"] = this.pluginAuthToken;
1663
1663
  }
1664
- return new Promise((resolve) => {
1664
+ return new Promise((resolve2) => {
1665
1665
  const attempt = (attemptsLeft) => {
1666
1666
  _transport2.sendOutputChunk(`${API_BASE4}/api/commands/output`, headers, payload).then(({ statusCode, body: resBody }) => {
1667
1667
  if (statusCode >= 400) {
1668
1668
  process.stderr.write(`[codeam] output API error ${statusCode}: ${resBody}
1669
1669
  `);
1670
1670
  }
1671
- resolve();
1671
+ resolve2();
1672
1672
  }).catch(() => {
1673
1673
  if (attemptsLeft > 0) {
1674
1674
  const delay = 200 * (maxRetries - attemptsLeft + 1);
1675
1675
  setTimeout(() => attempt(attemptsLeft - 1), delay);
1676
1676
  } else {
1677
- resolve();
1677
+ resolve2();
1678
1678
  }
1679
1679
  });
1680
1680
  };
@@ -1686,7 +1686,7 @@ var _transport2 = {
1686
1686
  sendOutputChunk: _sendOutputChunk
1687
1687
  };
1688
1688
  function _sendOutputChunk(url, headers, payload) {
1689
- return new Promise((resolve, reject) => {
1689
+ return new Promise((resolve2, reject) => {
1690
1690
  let settled = false;
1691
1691
  const u2 = new URL(url);
1692
1692
  const transport = u2.protocol === "https:" ? https2 : http2;
@@ -1710,7 +1710,7 @@ function _sendOutputChunk(url, headers, payload) {
1710
1710
  res.on("end", () => {
1711
1711
  if (settled) return;
1712
1712
  settled = true;
1713
- resolve({ statusCode: res.statusCode ?? 0, body: resData });
1713
+ resolve2({ statusCode: res.statusCode ?? 0, body: resData });
1714
1714
  });
1715
1715
  }
1716
1716
  );
@@ -1733,6 +1733,7 @@ var path4 = __toESM(require("path"));
1733
1733
  var os4 = __toESM(require("os"));
1734
1734
  var https3 = __toESM(require("https"));
1735
1735
  var http3 = __toESM(require("http"));
1736
+ var import_zod = require("zod");
1736
1737
 
1737
1738
  // src/services/logger.ts
1738
1739
  var LEVELS = { silent: 0, error: 1, warn: 2, info: 3, debug: 4 };
@@ -1754,6 +1755,16 @@ var log = {
1754
1755
  };
1755
1756
 
1756
1757
  // src/services/history.service.ts
1758
+ var historyRecordSchema = import_zod.z.object({
1759
+ type: import_zod.z.string().optional(),
1760
+ timestamp: import_zod.z.union([import_zod.z.string(), import_zod.z.number()]).optional(),
1761
+ uuid: import_zod.z.string().optional(),
1762
+ isMeta: import_zod.z.boolean().optional(),
1763
+ message: import_zod.z.object({
1764
+ // Claude content is either a string or an array of typed blocks.
1765
+ content: import_zod.z.union([import_zod.z.string(), import_zod.z.array(import_zod.z.unknown())]).optional()
1766
+ }).passthrough().optional()
1767
+ }).passthrough();
1757
1768
  var API_BASE5 = process.env.CODEAM_API_URL ?? "https://codeagent-mobile-api.vercel.app";
1758
1769
  function encodeCwd(cwd) {
1759
1770
  return cwd.replace(/\//g, "-");
@@ -1779,28 +1790,35 @@ function parseJsonl(filePath) {
1779
1790
  }
1780
1791
  const lines = raw.split("\n").filter(Boolean);
1781
1792
  for (const line of lines) {
1793
+ let parsedJson;
1782
1794
  try {
1783
- const record = JSON.parse(line);
1784
- const type = record["type"];
1785
- const msg = record["message"];
1786
- const ts = record["timestamp"];
1787
- const timestamp = typeof ts === "string" ? new Date(ts).getTime() : typeof ts === "number" ? ts : Date.now();
1788
- const uuid = record["uuid"] ?? `${Date.now()}-${Math.random()}`;
1789
- if (record["isMeta"]) continue;
1790
- if (type === "user" && msg) {
1791
- const text = extractText(msg["content"]).trim();
1792
- if (text) messages.push({ id: uuid, role: "user", text, timestamp });
1793
- } else if (type === "assistant" && msg) {
1794
- const text = extractText(msg["content"]).trim();
1795
- if (text) messages.push({ id: uuid, role: "agent", text, timestamp });
1796
- }
1795
+ parsedJson = JSON.parse(line);
1797
1796
  } catch {
1797
+ continue;
1798
+ }
1799
+ const result = historyRecordSchema.safeParse(parsedJson);
1800
+ if (!result.success) {
1801
+ log.warn("history:parseJsonl", `record failed schema validation in ${filePath}`, result.error.issues);
1802
+ continue;
1803
+ }
1804
+ const record = result.data;
1805
+ if (record.isMeta) continue;
1806
+ const ts = record.timestamp;
1807
+ const timestamp = typeof ts === "string" ? new Date(ts).getTime() : typeof ts === "number" ? ts : Date.now();
1808
+ const uuid = record.uuid ?? `${Date.now()}-${Math.random()}`;
1809
+ const msg = record.message;
1810
+ if (record.type === "user" && msg) {
1811
+ const text = extractText(msg.content).trim();
1812
+ if (text) messages.push({ id: uuid, role: "user", text, timestamp });
1813
+ } else if (record.type === "assistant" && msg) {
1814
+ const text = extractText(msg.content).trim();
1815
+ if (text) messages.push({ id: uuid, role: "agent", text, timestamp });
1798
1816
  }
1799
1817
  }
1800
1818
  return messages;
1801
1819
  }
1802
1820
  function post(endpoint, body) {
1803
- return new Promise((resolve) => {
1821
+ return new Promise((resolve2) => {
1804
1822
  const payload = JSON.stringify(body);
1805
1823
  const u2 = new URL(`${API_BASE5}${endpoint}`);
1806
1824
  const transport = u2.protocol === "https:" ? https3 : http3;
@@ -1820,17 +1838,17 @@ function post(endpoint, body) {
1820
1838
  res.resume();
1821
1839
  const ok = res.statusCode !== void 0 && res.statusCode >= 200 && res.statusCode < 300;
1822
1840
  if (!ok) log.warn("history:post", `${endpoint} \u2192 HTTP ${res.statusCode}`);
1823
- resolve(ok);
1841
+ resolve2(ok);
1824
1842
  }
1825
1843
  );
1826
1844
  req.on("error", (err) => {
1827
1845
  log.warn("history:post", `${endpoint} network error`, err);
1828
- resolve(false);
1846
+ resolve2(false);
1829
1847
  });
1830
1848
  req.on("timeout", () => {
1831
1849
  log.warn("history:post", `${endpoint} timeout after 15s`);
1832
1850
  req.destroy();
1833
- resolve(false);
1851
+ resolve2(false);
1834
1852
  });
1835
1853
  req.write(payload);
1836
1854
  req.end();
@@ -2127,32 +2145,146 @@ var HistoryService = class {
2127
2145
  };
2128
2146
 
2129
2147
  // src/lib/payload.ts
2130
- var import_zod = require("zod");
2131
- var fileEntrySchema = import_zod.z.object({
2132
- filename: import_zod.z.string().min(1).max(256),
2133
- mimeType: import_zod.z.string(),
2134
- base64: import_zod.z.string()
2148
+ var import_zod2 = require("zod");
2149
+ var fileEntrySchema = import_zod2.z.object({
2150
+ filename: import_zod2.z.string().min(1).max(256),
2151
+ mimeType: import_zod2.z.string(),
2152
+ base64: import_zod2.z.string()
2135
2153
  });
2136
- var startCommandSchema = import_zod.z.object({
2137
- prompt: import_zod.z.string().optional(),
2138
- files: import_zod.z.array(fileEntrySchema).optional(),
2139
- input: import_zod.z.string().optional(),
2140
- index: import_zod.z.number().optional(),
2141
- from: import_zod.z.number().optional(),
2142
- id: import_zod.z.string().optional(),
2143
- auto: import_zod.z.boolean().optional()
2154
+ var startCommandSchema = import_zod2.z.object({
2155
+ prompt: import_zod2.z.string().optional(),
2156
+ files: import_zod2.z.array(fileEntrySchema).optional(),
2157
+ input: import_zod2.z.string().optional(),
2158
+ index: import_zod2.z.number().optional(),
2159
+ from: import_zod2.z.number().optional(),
2160
+ id: import_zod2.z.string().optional(),
2161
+ auto: import_zod2.z.boolean().optional(),
2162
+ // `read_file` / `write_file` for the mobile + landing mini-IDE modal.
2163
+ // `path` is bounded to 4096 chars (a comfortable POSIX path max) so a
2164
+ // malformed payload can't blow up the disk-side validator.
2165
+ path: import_zod2.z.string().min(1).max(4096).optional(),
2166
+ content: import_zod2.z.string().optional()
2144
2167
  });
2145
2168
  function parsePayload(schema, raw) {
2146
2169
  const result = schema.safeParse(raw);
2147
2170
  return result.success ? result.data : null;
2148
2171
  }
2149
2172
 
2173
+ // src/services/file-ops.service.ts
2174
+ var fs5 = __toESM(require("fs/promises"));
2175
+ var path5 = __toESM(require("path"));
2176
+ var MAX_FILE_BYTES = 5 * 1024 * 1024;
2177
+ var SUBDIR_IGNORE = /* @__PURE__ */ new Set([
2178
+ "node_modules",
2179
+ ".git",
2180
+ ".next",
2181
+ ".expo",
2182
+ "dist",
2183
+ "build",
2184
+ "out",
2185
+ ".cache",
2186
+ "coverage",
2187
+ ".turbo",
2188
+ ".parcel-cache",
2189
+ ".idea",
2190
+ ".vscode",
2191
+ "ios",
2192
+ "android"
2193
+ // expo-managed native dirs are huge and rarely interesting
2194
+ ]);
2195
+ async function listSubdirs(dir) {
2196
+ try {
2197
+ const entries = await fs5.readdir(dir, { withFileTypes: true });
2198
+ return entries.filter((e) => e.isDirectory() && !e.name.startsWith(".") ? true : !SUBDIR_IGNORE.has(e.name)).filter((e) => e.isDirectory() && !SUBDIR_IGNORE.has(e.name)).map((e) => path5.join(dir, e.name));
2199
+ } catch {
2200
+ return [];
2201
+ }
2202
+ }
2203
+ function isUnder(parent, candidate) {
2204
+ const rel = path5.relative(parent, candidate);
2205
+ return !rel.startsWith("..") && !path5.isAbsolute(rel);
2206
+ }
2207
+ async function findFile(rawPath) {
2208
+ const cwd = process.cwd();
2209
+ const candidates = [];
2210
+ if (path5.isAbsolute(rawPath)) {
2211
+ candidates.push(path5.normalize(rawPath));
2212
+ } else {
2213
+ candidates.push(path5.resolve(cwd, rawPath));
2214
+ const subdirs = await listSubdirs(cwd);
2215
+ for (const sub of subdirs) {
2216
+ candidates.push(path5.resolve(sub, rawPath));
2217
+ }
2218
+ }
2219
+ for (const cand of candidates) {
2220
+ if (!isUnder(cwd, cand)) continue;
2221
+ try {
2222
+ const stat2 = await fs5.stat(cand);
2223
+ if (stat2.isFile()) return cand;
2224
+ } catch {
2225
+ }
2226
+ }
2227
+ return null;
2228
+ }
2229
+ async function findWriteTarget(rawPath) {
2230
+ const found = await findFile(rawPath);
2231
+ if (found) return found;
2232
+ const cwd = process.cwd();
2233
+ const fallback = path5.isAbsolute(rawPath) ? path5.normalize(rawPath) : path5.resolve(cwd, rawPath);
2234
+ if (!isUnder(cwd, fallback)) return null;
2235
+ return fallback;
2236
+ }
2237
+ function looksBinary(buf) {
2238
+ const sample = buf.subarray(0, Math.min(8192, buf.length));
2239
+ for (let i = 0; i < sample.length; i++) {
2240
+ if (sample[i] === 0) return true;
2241
+ }
2242
+ return false;
2243
+ }
2244
+ async function readProjectFile(rawPath) {
2245
+ try {
2246
+ const abs = await findFile(rawPath);
2247
+ if (!abs) {
2248
+ return { error: `File not found in the project tree: ${rawPath}` };
2249
+ }
2250
+ const stat2 = await fs5.stat(abs);
2251
+ if (stat2.size > MAX_FILE_BYTES) {
2252
+ return { error: `File too large (${(stat2.size / 1024 / 1024).toFixed(1)} MB > ${MAX_FILE_BYTES / 1024 / 1024} MB).` };
2253
+ }
2254
+ const buf = await fs5.readFile(abs);
2255
+ if (looksBinary(buf)) {
2256
+ return { error: "Binary file \u2014 refusing to open in a code editor." };
2257
+ }
2258
+ return { content: buf.toString("utf-8") };
2259
+ } catch (e) {
2260
+ const msg = e instanceof Error ? e.message : "Read failed";
2261
+ return { error: msg };
2262
+ }
2263
+ }
2264
+ async function writeProjectFile(rawPath, content) {
2265
+ try {
2266
+ const abs = await findWriteTarget(rawPath);
2267
+ if (!abs) {
2268
+ return { error: `Path escapes the project root: ${rawPath}` };
2269
+ }
2270
+ if (Buffer.byteLength(content, "utf-8") > MAX_FILE_BYTES) {
2271
+ return { error: "Content too large." };
2272
+ }
2273
+ await fs5.mkdir(path5.dirname(abs), { recursive: true });
2274
+ await fs5.writeFile(abs, content, "utf-8");
2275
+ return { ok: true };
2276
+ } catch (e) {
2277
+ const msg = e instanceof Error ? e.message : "Write failed";
2278
+ return { error: msg };
2279
+ }
2280
+ }
2281
+
2150
2282
  // src/commands/start.ts
2151
2283
  function saveFilesTemp(files) {
2152
2284
  return files.filter(({ base64 }) => base64 && base64.length > 0).map(({ filename, base64 }) => {
2153
2285
  const safeName = filename.replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 80);
2154
- const tmpPath = path5.join(os5.tmpdir(), `codeam-${(0, import_crypto.randomUUID)()}-${safeName}`);
2155
- fs5.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
2286
+ const tmpPath = path6.join(os5.tmpdir(), `codeam-${(0, import_crypto.randomUUID)()}-${safeName}`);
2287
+ fs6.writeFileSync(tmpPath, Buffer.from(base64, "base64"));
2156
2288
  return tmpPath;
2157
2289
  });
2158
2290
  }
@@ -2225,8 +2357,8 @@ try:
2225
2357
  sys.exit((st>>8)&0xFF)
2226
2358
  except Exception:sys.exit(0)
2227
2359
  `;
2228
- const helperPath = path5.join(os5.tmpdir(), "codeam-quota-helper.py");
2229
- fs5.writeFileSync(helperPath, helperScript, { mode: 420 });
2360
+ const helperPath = path6.join(os5.tmpdir(), "codeam-quota-helper.py");
2361
+ fs6.writeFileSync(helperPath, helperScript, { mode: 420 });
2230
2362
  const python = findInPath("python3") ?? findInPath("python");
2231
2363
  if (!python) {
2232
2364
  quotaFetchInProgress = false;
@@ -2258,7 +2390,7 @@ except Exception:sys.exit(0)
2258
2390
  } catch {
2259
2391
  }
2260
2392
  try {
2261
- fs5.unlinkSync(helperPath);
2393
+ fs6.unlinkSync(helperPath);
2262
2394
  } catch {
2263
2395
  }
2264
2396
  quotaFetchInProgress = false;
@@ -2308,7 +2440,7 @@ except Exception:sys.exit(0)
2308
2440
  setTimeout(() => {
2309
2441
  for (const p2 of paths) {
2310
2442
  try {
2311
- fs5.unlinkSync(p2);
2443
+ fs6.unlinkSync(p2);
2312
2444
  } catch {
2313
2445
  }
2314
2446
  }
@@ -2380,6 +2512,26 @@ except Exception:sys.exit(0)
2380
2512
  await relay.sendResult(cmd.id, "completed", { models });
2381
2513
  break;
2382
2514
  }
2515
+ case "read_file": {
2516
+ const { path: filePath } = parsed;
2517
+ if (!filePath) {
2518
+ await relay.sendResult(cmd.id, "failed", { error: "Missing path" });
2519
+ break;
2520
+ }
2521
+ const result = await readProjectFile(filePath);
2522
+ await relay.sendResult(cmd.id, "completed", result);
2523
+ break;
2524
+ }
2525
+ case "write_file": {
2526
+ const { path: filePath, content } = parsed;
2527
+ if (!filePath || typeof content !== "string") {
2528
+ await relay.sendResult(cmd.id, "failed", { error: "Missing path or content" });
2529
+ break;
2530
+ }
2531
+ const result = await writeProjectFile(filePath, content);
2532
+ await relay.sendResult(cmd.id, "completed", result);
2533
+ break;
2534
+ }
2383
2535
  }
2384
2536
  });
2385
2537
  ws.addHandler({
@@ -2407,7 +2559,7 @@ except Exception:sys.exit(0)
2407
2559
  setTimeout(() => {
2408
2560
  for (const p2 of paths) {
2409
2561
  try {
2410
- fs5.unlinkSync(p2);
2562
+ fs6.unlinkSync(p2);
2411
2563
  } catch {
2412
2564
  }
2413
2565
  }
@@ -2509,7 +2661,7 @@ __export(dist_exports, {
2509
2661
  S_ERROR: () => ge,
2510
2662
  S_INFO: () => he,
2511
2663
  S_PASSWORD_MASK: () => xe,
2512
- S_RADIO_ACTIVE: () => z3,
2664
+ S_RADIO_ACTIVE: () => z4,
2513
2665
  S_RADIO_INACTIVE: () => H2,
2514
2666
  S_STEP_ACTIVE: () => _e,
2515
2667
  S_STEP_CANCEL: () => oe,
@@ -2988,7 +3140,7 @@ function w(r, t2) {
2988
3140
  const e = r;
2989
3141
  e.isTTY && e.setRawMode(t2);
2990
3142
  }
2991
- function z2({ input: r = import_node_process.stdin, output: t2 = import_node_process.stdout, overwrite: e = true, hideCursor: s = true } = {}) {
3143
+ function z3({ input: r = import_node_process.stdin, output: t2 = import_node_process.stdout, overwrite: e = true, hideCursor: s = true } = {}) {
2992
3144
  const i = _.createInterface({ input: r, output: t2, prompt: "", tabSize: 1 });
2993
3145
  _.emitKeypressEvents(r, i), r instanceof import_node_tty.ReadStream && r.isTTY && r.setRawMode(true);
2994
3146
  const n = (o, { name: a, sequence: h }) => {
@@ -3600,7 +3752,7 @@ var d2 = w2("\u2502", "|");
3600
3752
  var E2 = w2("\u2514", "\u2014");
3601
3753
  var Ie = w2("\u2510", "T");
3602
3754
  var Ee = w2("\u2518", "\u2014");
3603
- var z3 = w2("\u25CF", ">");
3755
+ var z4 = w2("\u25CF", ">");
3604
3756
  var H2 = w2("\u25CB", " ");
3605
3757
  var te = w2("\u25FB", "[\u2022]");
3606
3758
  var U = w2("\u25FC", "[+]");
@@ -3692,7 +3844,7 @@ var Ae = (e) => new H({ options: e.options, initialValue: e.initialValue ? [e.in
3692
3844
  const $2 = Me(a), y2 = a.hint && a.value === this.focusedValue ? (0, import_node_util2.styleText)("dim", ` (${a.hint})`) : "";
3693
3845
  switch (l) {
3694
3846
  case "active":
3695
- return `${(0, import_node_util2.styleText)("green", z3)} ${$2}${y2}`;
3847
+ return `${(0, import_node_util2.styleText)("green", z4)} ${$2}${y2}`;
3696
3848
  case "inactive":
3697
3849
  return `${(0, import_node_util2.styleText)("dim", H2)} ${(0, import_node_util2.styleText)("dim", $2)}`;
3698
3850
  case "disabled":
@@ -3805,9 +3957,9 @@ ${(0, import_node_util2.styleText)("gray", d2)}` : ""}`;
3805
3957
  }
3806
3958
  default: {
3807
3959
  const l = r ? `${(0, import_node_util2.styleText)("cyan", d2)} ` : "", $2 = r ? (0, import_node_util2.styleText)("cyan", E2) : "";
3808
- return `${c2}${l}${this.value ? `${(0, import_node_util2.styleText)("green", z3)} ${i}` : `${(0, import_node_util2.styleText)("dim", H2)} ${(0, import_node_util2.styleText)("dim", i)}`}${e.vertical ? r ? `
3960
+ return `${c2}${l}${this.value ? `${(0, import_node_util2.styleText)("green", z4)} ${i}` : `${(0, import_node_util2.styleText)("dim", H2)} ${(0, import_node_util2.styleText)("dim", i)}`}${e.vertical ? r ? `
3809
3961
  ${(0, import_node_util2.styleText)("cyan", d2)} ` : `
3810
- ` : ` ${(0, import_node_util2.styleText)("dim", "/")} `}${this.value ? `${(0, import_node_util2.styleText)("dim", H2)} ${(0, import_node_util2.styleText)("dim", s)}` : `${(0, import_node_util2.styleText)("green", z3)} ${s}`}
3962
+ ` : ` ${(0, import_node_util2.styleText)("dim", "/")} `}${this.value ? `${(0, import_node_util2.styleText)("dim", H2)} ${(0, import_node_util2.styleText)("dim", s)}` : `${(0, import_node_util2.styleText)("green", z4)} ${s}`}
3811
3963
  ${$2}
3812
3964
  `;
3813
3965
  }
@@ -4138,7 +4290,7 @@ var fe = ({ indicator: e = "dots", onCancel: i, output: s = process.stdout, canc
4138
4290
  const A2 = (performance.now() - _2) / 1e3, k2 = Math.floor(A2 / 60), L2 = Math.floor(A2 % 60);
4139
4291
  return k2 > 0 ? `[${k2}m ${L2}s]` : `[${L2}s]`;
4140
4292
  }, D2 = a.withGuide ?? u.withGuide, ie = (_2 = "") => {
4141
- p2 = true, $2 = z2({ output: s }), g = R2(_2), h = performance.now(), D2 && s.write(`${(0, import_node_util2.styleText)("gray", d2)}
4293
+ p2 = true, $2 = z3({ output: s }), g = R2(_2), h = performance.now(), D2 && s.write(`${(0, import_node_util2.styleText)("gray", d2)}
4142
4294
  `);
4143
4295
  let A2 = 0, k2 = 0;
4144
4296
  x(), y2 = setInterval(() => {
@@ -4209,7 +4361,7 @@ var _t = (e) => {
4209
4361
  case "selected":
4210
4362
  return `${re(u2, (n) => (0, import_node_util2.styleText)("dim", n))}`;
4211
4363
  case "active":
4212
- return `${(0, import_node_util2.styleText)("green", z3)} ${u2}${s.hint ? ` ${(0, import_node_util2.styleText)("dim", `(${s.hint})`)}` : ""}`;
4364
+ return `${(0, import_node_util2.styleText)("green", z4)} ${u2}${s.hint ? ` ${(0, import_node_util2.styleText)("dim", `(${s.hint})`)}` : ""}`;
4213
4365
  case "cancelled":
4214
4366
  return `${re(u2, (n) => (0, import_node_util2.styleText)(["strikethrough", "dim"], n))}`;
4215
4367
  default:
@@ -4437,7 +4589,7 @@ async function pair() {
4437
4589
  console.log("");
4438
4590
  const waitSpin = dist_exports.spinner();
4439
4591
  waitSpin.start("Waiting for mobile app...");
4440
- await new Promise((resolve) => {
4592
+ await new Promise((resolve2) => {
4441
4593
  let stopPolling = null;
4442
4594
  function sigintHandler() {
4443
4595
  stopPolling?.();
@@ -4460,7 +4612,7 @@ async function pair() {
4460
4612
  });
4461
4613
  showSuccess(`Paired with ${info.userName} (${info.plan})`);
4462
4614
  console.log("");
4463
- resolve();
4615
+ resolve2();
4464
4616
  },
4465
4617
  () => {
4466
4618
  waitSpin.stop("Timed out");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeam-cli",
3
- "version": "2.1.1",
3
+ "version": "2.2.1",
4
4
  "description": "Remote control Claude Code (and other AI coding agents) from your mobile phone. Pair your device, send prompts, stream responses in real-time, and approve commands — from anywhere.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {