codeam-cli 2.39.28 → 2.39.30

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.39.29] — 2026-06-18
8
+
9
+ ### Added
10
+
11
+ - **cli:** Report host-agent enrollment progress to backend
12
+
13
+ ## [2.39.28] — 2026-06-18
14
+
15
+ ### Added
16
+
17
+ - **cli,vsc-plugin,jetbrains-plugin:** Add show_install_command relay handler (#355)
18
+
7
19
  ## [2.39.27] — 2026-06-17
8
20
 
9
21
  ### Added
package/dist/index.js CHANGED
@@ -498,7 +498,7 @@ var import_qrcode_terminal = __toESM(require("qrcode-terminal"));
498
498
  // package.json
499
499
  var package_default = {
500
500
  name: "codeam-cli",
501
- version: "2.39.28",
501
+ version: "2.39.30",
502
502
  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.",
503
503
  type: "commonjs",
504
504
  main: "dist/index.js",
@@ -5908,7 +5908,7 @@ function readAnonId() {
5908
5908
  }
5909
5909
  function superProperties() {
5910
5910
  return {
5911
- cliVersion: true ? "2.39.28" : "0.0.0-dev",
5911
+ cliVersion: true ? "2.39.30" : "0.0.0-dev",
5912
5912
  nodeVersion: process.version,
5913
5913
  platform: process.platform,
5914
5914
  arch: process.arch,
@@ -26205,6 +26205,48 @@ async function stopWorkspaceFromLocal(target) {
26205
26205
  var fs41 = __toESM(require("fs"));
26206
26206
  var os32 = __toESM(require("os"));
26207
26207
  var path53 = __toESM(require("path"));
26208
+ function sampleCpuTimes() {
26209
+ let idle = 0;
26210
+ let total = 0;
26211
+ for (const cpu of os32.cpus()) {
26212
+ const t2 = cpu.times;
26213
+ idle += t2.idle;
26214
+ total += t2.user + t2.nice + t2.sys + t2.idle + t2.irq;
26215
+ }
26216
+ return { idle, total };
26217
+ }
26218
+ var MetricsCollector = class {
26219
+ prevCpu = null;
26220
+ lastLatencyMs = 0;
26221
+ /** Record the measured round-trip of the heartbeat just sent. */
26222
+ recordLatency(latencyMs) {
26223
+ this.lastLatencyMs = Math.max(0, Math.round(latencyMs));
26224
+ }
26225
+ cpuPct() {
26226
+ const current = sampleCpuTimes();
26227
+ const prev = this.prevCpu;
26228
+ this.prevCpu = current;
26229
+ if (!prev) {
26230
+ const cores = os32.cpus().length || 1;
26231
+ const proxy = os32.loadavg()[0] / cores * 100;
26232
+ return Math.min(100, Math.max(0, Math.round(proxy)));
26233
+ }
26234
+ const idleDelta = current.idle - prev.idle;
26235
+ const totalDelta = current.total - prev.total;
26236
+ if (totalDelta <= 0) return 0;
26237
+ const busy = (1 - idleDelta / totalDelta) * 100;
26238
+ return Math.min(100, Math.max(0, Math.round(busy)));
26239
+ }
26240
+ /** Collect the current metrics snapshot for this beat. */
26241
+ collect() {
26242
+ return {
26243
+ cpuPct: this.cpuPct(),
26244
+ ramUsedMb: Math.round((os32.totalmem() - os32.freemem()) / 1048576),
26245
+ ramTotalMb: Math.round(os32.totalmem() / 1048576),
26246
+ latencyMs: this.lastLatencyMs
26247
+ };
26248
+ }
26249
+ };
26208
26250
  function apiBase() {
26209
26251
  return process.env.CODEAM_API_URL ?? resolveApiBaseUrl();
26210
26252
  }
@@ -26268,11 +26310,15 @@ async function redeemEnrollToken(token, label) {
26268
26310
  controlPluginId: data.controlPluginId
26269
26311
  };
26270
26312
  }
26271
- async function sendHostHeartbeat(identity) {
26313
+ async function sendHostHeartbeat(identity, metrics) {
26314
+ const start2 = process.hrtime.bigint();
26272
26315
  await postJson("/api/self-hosted/heartbeat", {
26273
26316
  hostId: identity.hostId,
26274
- hostToken: identity.hostToken
26317
+ hostToken: identity.hostToken,
26318
+ ...metrics ? { metrics } : {}
26275
26319
  });
26320
+ const elapsedNs = process.hrtime.bigint() - start2;
26321
+ return Math.round(Number(elapsedNs) / 1e6);
26276
26322
  }
26277
26323
  function isSealedEnvelope(v) {
26278
26324
  if (typeof v !== "object" || v === null) return false;
@@ -26302,6 +26348,25 @@ async function unsealAgentAuth(identity, sealedAgentAuth) {
26302
26348
  }
26303
26349
  return data;
26304
26350
  }
26351
+ var PROGRESS_TIMEOUT_MS = 3e3;
26352
+ async function reportProgress(auth, step, message) {
26353
+ try {
26354
+ const controller = new AbortController();
26355
+ const timer = setTimeout(() => controller.abort(), PROGRESS_TIMEOUT_MS);
26356
+ timer.unref?.();
26357
+ try {
26358
+ await fetch(`${apiBase()}/api/self-hosted/enroll-progress`, {
26359
+ method: "POST",
26360
+ headers: { "Content-Type": "application/json", ...vercelBypassHeader() },
26361
+ body: JSON.stringify({ ...auth, step, message }),
26362
+ signal: controller.signal
26363
+ });
26364
+ } finally {
26365
+ clearTimeout(timer);
26366
+ }
26367
+ } catch {
26368
+ }
26369
+ }
26305
26370
 
26306
26371
  // src/commands/host.ts
26307
26372
  function readTokenFlag(args2) {
@@ -26496,6 +26561,7 @@ var HostAgentSupervisor = class {
26496
26561
  this.deps = deps;
26497
26562
  this.spawnChild = deps.spawnChild ?? defaultSpawner;
26498
26563
  this.resolveAgentAuth = deps.resolveAgentAuth ?? unsealAgentAuth;
26564
+ this.metrics = deps.metricsCollector ?? new MetricsCollector();
26499
26565
  }
26500
26566
  identity;
26501
26567
  deps;
@@ -26504,6 +26570,10 @@ var HostAgentSupervisor = class {
26504
26570
  resolveAgentAuth;
26505
26571
  relay = null;
26506
26572
  heartbeatTimer = null;
26573
+ /** Guards the one-shot 'connected' telemetry on the first heartbeat. */
26574
+ reportedConnected = false;
26575
+ /** Live-metrics collector — stateful across beats (CPU delta + latency). */
26576
+ metrics;
26507
26577
  /** Open the control channel (reusing the relay) + start heartbeats. */
26508
26578
  start() {
26509
26579
  const make = this.deps.makeRelay ?? ((pluginId, onCommand, meta) => new CommandRelayService(pluginId, onCommand, meta));
@@ -26535,7 +26605,22 @@ var HostAgentSupervisor = class {
26535
26605
  }
26536
26606
  async beat() {
26537
26607
  try {
26538
- await sendHostHeartbeat(this.identity);
26608
+ let metrics;
26609
+ try {
26610
+ metrics = this.metrics.collect();
26611
+ } catch (err) {
26612
+ log.trace("host-agent", "metrics collection failed", err);
26613
+ }
26614
+ const latencyMs = await sendHostHeartbeat(this.identity, metrics);
26615
+ this.metrics.recordLatency(latencyMs);
26616
+ if (!this.reportedConnected) {
26617
+ this.reportedConnected = true;
26618
+ void reportProgress(
26619
+ { hostId: this.identity.hostId, hostToken: this.identity.hostToken },
26620
+ "connected",
26621
+ "host-agent connected"
26622
+ );
26623
+ }
26539
26624
  } catch (err) {
26540
26625
  log.trace("host-agent", "heartbeat failed", err);
26541
26626
  }
@@ -26642,8 +26727,14 @@ async function resolveHostIdentity(enrollToken) {
26642
26727
  const existing = loadHostIdentity();
26643
26728
  if (existing) return existing;
26644
26729
  if (!enrollToken) return null;
26730
+ await reportProgress({ enrollToken }, "redeeming", "redeeming enrollment token\u2026");
26645
26731
  const identity = await redeemEnrollToken(enrollToken);
26646
26732
  saveHostIdentity(identity);
26733
+ await reportProgress(
26734
+ { hostId: identity.hostId, hostToken: identity.hostToken },
26735
+ "enrolled",
26736
+ "host enrolled"
26737
+ );
26647
26738
  return identity;
26648
26739
  }
26649
26740
  async function hostAgent(args2 = []) {
@@ -26840,7 +26931,7 @@ function checkChokidar() {
26840
26931
  }
26841
26932
  async function doctor(args2 = []) {
26842
26933
  const json = args2.includes("--json");
26843
- const cliVersion = true ? "2.39.28" : "0.0.0-dev";
26934
+ const cliVersion = true ? "2.39.30" : "0.0.0-dev";
26844
26935
  const apiBase2 = resolveApiBaseUrl();
26845
26936
  const diagnosticId = (0, import_node_crypto8.randomUUID)();
26846
26937
  log.info("doctor", `run id=${diagnosticId} cli=${cliVersion}`);
@@ -27039,7 +27130,7 @@ async function completion(args2) {
27039
27130
  // src/commands/version.ts
27040
27131
  var import_picocolors13 = __toESM(require("picocolors"));
27041
27132
  function version2() {
27042
- const v = true ? "2.39.28" : "unknown";
27133
+ const v = true ? "2.39.30" : "unknown";
27043
27134
  console.log(`${import_picocolors13.default.bold("codeam-cli")} ${import_picocolors13.default.cyan(v)}`);
27044
27135
  }
27045
27136
 
@@ -27325,7 +27416,7 @@ function checkForUpdates() {
27325
27416
  if (process.env.CODEAM_DISABLE_UPDATE_CHECK === "1") return;
27326
27417
  if (process.env.CI) return;
27327
27418
  if (!process.stdout.isTTY) return;
27328
- const current = true ? "2.39.28" : null;
27419
+ const current = true ? "2.39.30" : null;
27329
27420
  if (!current) return;
27330
27421
  const cache = readCache();
27331
27422
  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.39.28",
3
+ "version": "2.39.30",
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",