@integrity-labs/agt-cli 0.28.1139 → 0.28.1141

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/dist/bin/agt.js CHANGED
@@ -40,7 +40,7 @@ import {
40
40
  success,
41
41
  table,
42
42
  warn
43
- } from "../chunk-PI2WUKWD.js";
43
+ } from "../chunk-6WHFJSXT.js";
44
44
  import {
45
45
  getProjectDir,
46
46
  isSessionResumeDisabled,
@@ -5474,7 +5474,7 @@ import { execFileSync, execSync } from "child_process";
5474
5474
  import { existsSync as existsSync11, realpathSync as realpathSync2 } from "fs";
5475
5475
  import chalk18 from "chalk";
5476
5476
  import ora16 from "ora";
5477
- var cliVersion = true ? "0.28.1139" : "dev";
5477
+ var cliVersion = true ? "0.28.1141" : "dev";
5478
5478
  async function fetchLatestVersion() {
5479
5479
  const host2 = getHost();
5480
5480
  if (!host2) return null;
@@ -6734,7 +6734,7 @@ function handleError(err) {
6734
6734
  }
6735
6735
 
6736
6736
  // src/bin/agt.ts
6737
- var cliVersion2 = true ? "0.28.1139" : "dev";
6737
+ var cliVersion2 = true ? "0.28.1141" : "dev";
6738
6738
  var program = new Command();
6739
6739
  program.name("agt").description("Augmented CLI \u2014 agent provisioning and management").version(cliVersion2).option("--json", "Emit machine-readable JSON output (suppress spinners and colors)").option("--skip-update-check", "Skip the automatic update check on startup");
6740
6740
  program.hook("preAction", async (thisCommand, actionCommand) => {
@@ -8076,7 +8076,7 @@ function exchangeFailureKind(err) {
8076
8076
  }
8077
8077
 
8078
8078
  // src/lib/api-client.ts
8079
- var agtCliVersion = true ? "0.28.1139" : "dev";
8079
+ var agtCliVersion = true ? "0.28.1141" : "dev";
8080
8080
  var lastConfigHash = null;
8081
8081
  function setConfigHash(hash) {
8082
8082
  lastConfigHash = hash && hash.length > 0 ? hash : null;
@@ -13227,4 +13227,4 @@ export {
13227
13227
  managerInstallSystemUnitCommand,
13228
13228
  managerUninstallSystemUnitCommand
13229
13229
  };
13230
- //# sourceMappingURL=chunk-PI2WUKWD.js.map
13230
+ //# sourceMappingURL=chunk-6WHFJSXT.js.map
@@ -76,7 +76,7 @@ import {
76
76
  setConfigHash,
77
77
  shellGuard,
78
78
  tripClass
79
- } from "../chunk-PI2WUKWD.js";
79
+ } from "../chunk-6WHFJSXT.js";
80
80
  import {
81
81
  getProjectDir as getProjectDir2,
82
82
  getReadyTasks,
@@ -343,6 +343,8 @@ var HostNodeConvergence = class {
343
343
  }
344
344
  converged = /* @__PURE__ */ new Set();
345
345
  abandoned = /* @__PURE__ */ new Set();
346
+ /** Pins this platform cannot install at all; see {@link recordUnsupported}. */
347
+ unsupported = /* @__PURE__ */ new Set();
346
348
  failures = /* @__PURE__ */ new Map();
347
349
  retryAfter = /* @__PURE__ */ new Map();
348
350
  /**
@@ -383,6 +385,7 @@ var HostNodeConvergence = class {
383
385
  this.lastDesired = pin;
384
386
  this.converged.clear();
385
387
  this.abandoned.clear();
388
+ this.unsupported.clear();
386
389
  this.failures.clear();
387
390
  this.retryAfter.clear();
388
391
  }
@@ -393,6 +396,7 @@ var HostNodeConvergence = class {
393
396
  stateOf(pin, now = Date.now()) {
394
397
  if (this.converged.has(pin)) return "converged";
395
398
  if (this.abandoned.has(pin)) return "abandoned";
399
+ if (this.unsupported.has(pin)) return "unsupported";
396
400
  if ((this.retryAfter.get(pin) ?? 0) > now) return "cooling";
397
401
  return "ready";
398
402
  }
@@ -447,6 +451,20 @@ var HostNodeConvergence = class {
447
451
  this.failures.delete(pin);
448
452
  this.retryAfter.delete(pin);
449
453
  }
454
+ /**
455
+ * The pin cannot be installed on this platform (no `dnf`). Terminal, so it
456
+ * stops further attempts exactly as success does — hammering an impossible
457
+ * install every poll is worse than stopping — but it reports as itself.
458
+ *
459
+ * Carries the same staleness guard as {@link recordSuccess}: a late call for
460
+ * a superseded pin must not write a conclusion about the pin now wanted.
461
+ */
462
+ recordUnsupported(pin) {
463
+ if (this.lastDesired !== null && pin !== this.lastDesired) return;
464
+ this.unsupported.add(pin);
465
+ this.failures.delete(pin);
466
+ this.retryAfter.delete(pin);
467
+ }
450
468
  /**
451
469
  * An attempt failed. Returns the resulting state so the caller can log the
452
470
  * right thing: 'cooling' means it will retry, 'abandoned' means it will not.
@@ -488,6 +506,58 @@ var HostNodeConvergence = class {
488
506
  }
489
507
  };
490
508
 
509
+ // src/lib/host-node-report.ts
510
+ var HOST_NODE_PROBE_INTERVAL_MS = 15 * 60 * 1e3;
511
+ function shouldReprobeHostNode(lastObservedAt, now) {
512
+ if (lastObservedAt === 0) return true;
513
+ return now - lastObservedAt >= HOST_NODE_PROBE_INTERVAL_MS;
514
+ }
515
+ function normalizeReportedNodeVersion(raw) {
516
+ if (raw == null) return null;
517
+ const v = raw.trim().replace(/^v/, "");
518
+ return HOST_NODE_PIN_VERSION_RE.test(v) ? v : null;
519
+ }
520
+ function parsePathNodeVersion(stdout) {
521
+ const m = /^v?(\d+\.\d+\.\d+)$/.exec(stdout.trim());
522
+ return m ? normalizeReportedNodeVersion(m[1]) : null;
523
+ }
524
+ function parseRpmNodeVersion(stdout) {
525
+ return normalizeReportedNodeVersion(stdout.trim());
526
+ }
527
+ var RPM_NODE_QUERY_ARGV = ["-q", "--qf", "%{VERSION}", "nodejs"];
528
+ var PATH_NODE_QUERY_ARGV = ["--version"];
529
+ async function probeOrNull(probe) {
530
+ try {
531
+ return await probe();
532
+ } catch {
533
+ return null;
534
+ }
535
+ }
536
+ async function probeHostNode(run) {
537
+ const pathVersion = await probeOrNull(async () => {
538
+ const probe = await run("node", [...PATH_NODE_QUERY_ARGV]);
539
+ return probe.code === 0 ? parsePathNodeVersion(probe.stdout) : null;
540
+ });
541
+ const rpmVersion = await probeOrNull(async () => {
542
+ const rpm = await run("rpm", [...RPM_NODE_QUERY_ARGV]);
543
+ return rpm.code === 0 ? parseRpmNodeVersion(rpm.stdout) : null;
544
+ });
545
+ return { pathVersion, rpmVersion };
546
+ }
547
+ function heartbeatHostNodeFields(observation, pin, pinState) {
548
+ if (!observation) return {};
549
+ return {
550
+ host_node_version: normalizeReportedNodeVersion(observation.pathVersion),
551
+ host_node_rpm_version: normalizeReportedNodeVersion(observation.rpmVersion),
552
+ // A state without a pin would be meaningless, so the two are resolved
553
+ // together: no pin in force means no state, whatever the converger's
554
+ // internal maps happen to still hold for an older pin.
555
+ host_node_pin: pin,
556
+ host_node_pin_state: pin ? pinState : null,
557
+ host_node_observed: true
558
+ };
559
+ }
560
+
491
561
  // src/lib/manager-worker.ts
492
562
  import { execFileSync as syncExecFile } from "child_process";
493
563
  import { join as join43, dirname as dirname11, delimiter as pathDelimiter } from "path";
@@ -15416,7 +15486,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
15416
15486
  var lastVersionCheckAt = 0;
15417
15487
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
15418
15488
  var lastResponsivenessProbeAt = 0;
15419
- var agtCliVersion = true ? "0.28.1139" : "dev";
15489
+ var agtCliVersion = true ? "0.28.1141" : "dev";
15420
15490
  function resolveBrewPath(execFileSync3) {
15421
15491
  try {
15422
15492
  const out = execFileSync3("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -15597,8 +15667,22 @@ function recordHostNodeFailure(pin, reason) {
15597
15667
  async function diskNodeVersion() {
15598
15668
  const probe = await runAsync("node", ["--version"], { timeout: 15e3 });
15599
15669
  if (probe.code !== 0) return null;
15600
- const m = /^v?(\d+\.\d+\.\d+)/.exec(probe.stdout.trim());
15601
- return m?.[1] ?? null;
15670
+ return parsePathNodeVersion(probe.stdout);
15671
+ }
15672
+ var hostNodeObservation = null;
15673
+ var hostNodeObservedAt = 0;
15674
+ function invalidateHostNodeObservation(pathVersion) {
15675
+ hostNodeObservedAt = 0;
15676
+ if (pathVersion !== void 0 && hostNodeObservation) {
15677
+ hostNodeObservation = { ...hostNodeObservation, pathVersion };
15678
+ }
15679
+ }
15680
+ async function refreshHostNodeObservation(now = Date.now()) {
15681
+ if (!shouldReprobeHostNode(hostNodeObservedAt, now)) return;
15682
+ hostNodeObservedAt = now;
15683
+ hostNodeObservation = await probeHostNode(
15684
+ (cmd, args) => runAsync(cmd, args, { timeout: 15e3 })
15685
+ );
15602
15686
  }
15603
15687
  async function ensureHostNodeVersion(rawPin) {
15604
15688
  const pin = normalizeHostNodePinVersion(rawPin);
@@ -15628,7 +15712,7 @@ async function ensureHostNodeVersion(rawPin) {
15628
15712
  hostNodeNoDnfLogged = true;
15629
15713
  log(`[host-node] 'dnf' not found - cannot converge host Node onto ${pin} on this platform (leaving ${current ?? "unknown"} in place)`);
15630
15714
  }
15631
- hostNodeConvergence.recordSuccess(pin);
15715
+ hostNodeConvergence.recordUnsupported(pin);
15632
15716
  return;
15633
15717
  }
15634
15718
  const asRoot = typeof process.getuid === "function" && process.getuid() === 0;
@@ -15649,6 +15733,7 @@ async function ensureHostNodeVersion(rawPin) {
15649
15733
  }
15650
15734
  log(`[host-node] host Node is now ${pin} on disk. Running processes keep the previous binary until they restart - this does NOT restart the manager (ENG-11617).`);
15651
15735
  hostNodeConvergence.recordSuccess(pin);
15736
+ invalidateHostNodeObservation(after);
15652
15737
  } catch (err) {
15653
15738
  recordHostNodeFailure(pin, `install attempt threw: ${isolationImageErrDetail(err)}`);
15654
15739
  } finally {
@@ -17317,6 +17402,8 @@ async function pollCycleInner() {
17317
17402
  } else {
17318
17403
  lastManagedSettingsStatus = null;
17319
17404
  }
17405
+ await refreshHostNodeObservation();
17406
+ const hostNodePin = hostNodeConvergence.desiredPin();
17320
17407
  const hbResp = await api.post("/host/heartbeat", {
17321
17408
  host_id: hostId,
17322
17409
  framework_version: cachedFrameworkVersion ?? void 0,
@@ -17330,6 +17417,17 @@ async function pollCycleInner() {
17330
17417
  // rather than as two fields a future edit could separate. See
17331
17418
  // `heartbeatRuntimeAuthFields`.
17332
17419
  ...heartbeatRuntimeAuthFields(runtimeAuthProbeVerdict),
17420
+ // ENG-11639: host Node, and how the pin stands with this host. Spread as
17421
+ // ONE unit for the same reason as the line above — the API stamps a
17422
+ // freshness timestamp on receipt, so the values and their
17423
+ // `host_node_observed` marker must never be editable apart. Omitted
17424
+ // entirely until the first measurement, because ABSENCE MEANS NO SIGNAL
17425
+ // here and must not be readable as either patched or unpatched.
17426
+ ...heartbeatHostNodeFields(
17427
+ hostNodeObservation,
17428
+ hostNodePin,
17429
+ hostNodePin ? hostNodeConvergence.stateOf(hostNodePin) : null
17430
+ ),
17333
17431
  agent_diagnostics: agentDiagnostics,
17334
17432
  hostname: tailscaleHostname,
17335
17433
  os_username: osUsername,