@indigoai-us/hq-cli 5.115.6 → 5.116.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.
Files changed (55) hide show
  1. package/CHANGELOG.md +72 -11
  2. package/dist/command-catalog.generated.d.ts +162 -2
  3. package/dist/command-catalog.generated.js +205 -2
  4. package/dist/command-registration-plan.d.ts +6 -0
  5. package/dist/command-registration-plan.js +1 -0
  6. package/dist/commands/agent-enroll.d.ts +105 -0
  7. package/dist/commands/agent-enroll.js +273 -0
  8. package/dist/commands/agent-kit.d.ts +53 -0
  9. package/dist/commands/agent-kit.js +260 -0
  10. package/dist/commands/agent-mcp.d.ts +22 -0
  11. package/dist/commands/agent-mcp.js +104 -0
  12. package/dist/commands/agent-probe.d.ts +71 -0
  13. package/dist/commands/agent-probe.js +294 -0
  14. package/dist/commands/agent.d.ts +12 -0
  15. package/dist/commands/agent.js +23 -0
  16. package/dist/commands/agents.d.ts +27 -0
  17. package/dist/commands/agents.js +280 -6
  18. package/dist/commands/secrets.js +17 -5
  19. package/dist/lib/agent-kit/creds.d.ts +60 -0
  20. package/dist/lib/agent-kit/creds.js +123 -0
  21. package/dist/lib/agent-kit/kit-config.d.ts +29 -0
  22. package/dist/lib/agent-kit/kit-config.js +54 -0
  23. package/dist/lib/agent-kit/log.d.ts +17 -0
  24. package/dist/lib/agent-kit/log.js +46 -0
  25. package/dist/lib/agent-kit/mcp/jsonrpc.d.ts +84 -0
  26. package/dist/lib/agent-kit/mcp/jsonrpc.js +164 -0
  27. package/dist/lib/agent-kit/mcp/tools.d.ts +45 -0
  28. package/dist/lib/agent-kit/mcp/tools.js +280 -0
  29. package/dist/lib/agent-kit/paths.d.ts +42 -0
  30. package/dist/lib/agent-kit/paths.js +56 -0
  31. package/dist/lib/agent-kit/run/heartbeat.d.ts +52 -0
  32. package/dist/lib/agent-kit/run/heartbeat.js +97 -0
  33. package/dist/lib/agent-kit/run/inbox.d.ts +59 -0
  34. package/dist/lib/agent-kit/run/inbox.js +152 -0
  35. package/dist/lib/agent-kit/run/mesh-listener.d.ts +58 -0
  36. package/dist/lib/agent-kit/run/mesh-listener.js +193 -0
  37. package/dist/lib/agent-kit/run/sync.d.ts +33 -0
  38. package/dist/lib/agent-kit/run/sync.js +58 -0
  39. package/dist/lib/agent-kit/services.d.ts +21 -0
  40. package/dist/lib/agent-kit/services.js +46 -0
  41. package/dist/lib/agent-kit/skills.d.ts +18 -0
  42. package/dist/lib/agent-kit/skills.js +149 -0
  43. package/dist/lib/service-manager/index.d.ts +43 -0
  44. package/dist/lib/service-manager/index.js +114 -0
  45. package/dist/lib/service-manager/launchd.d.ts +23 -0
  46. package/dist/lib/service-manager/launchd.js +81 -0
  47. package/dist/lib/service-manager/systemd.d.ts +19 -0
  48. package/dist/lib/service-manager/systemd.js +72 -0
  49. package/dist/lib/service-manager/types.d.ts +32 -0
  50. package/dist/lib/service-manager/types.js +26 -0
  51. package/dist/utils/self-update.js +2 -30
  52. package/dist/utils/update-command-supervisor.cjs +194 -0
  53. package/dist/utils/version-gate.d.ts +18 -0
  54. package/dist/utils/version-gate.js +126 -7
  55. package/package.json +2 -2
@@ -534,16 +534,74 @@ export function inOwnProcessGroup(options) {
534
534
  }
535
535
  /** Tail of captured installer stderr kept to explain a failure. */
536
536
  const INSTALL_DETAIL_MAX_CHARS = 400;
537
+ /** Give a package-manager update ample time without letting it wedge every CLI call. */
538
+ const DEFAULT_UPDATE_TIMEOUT_MS = 30 * 60 * 1_000;
539
+ /** Node clamps longer timer delays to 1ms, so accepting them would invert the deadline. */
540
+ const MAX_UPDATE_TIMEOUT_MS = 2_147_483_647;
541
+ /** Let a well-behaved updater stop before force-killing the rest of its group. */
542
+ const UPDATE_TERMINATE_GRACE_MS = 1_000;
543
+ /** A successful group kill must still yield a close event so the supervisor reaps its child. */
544
+ const UPDATE_REAP_GRACE_MS = 1_000;
545
+ function updateTimeoutMs(env = process.env) {
546
+ const value = env.HQ_UPDATE_TIMEOUT_MS;
547
+ if (value === undefined || value.trim() === "")
548
+ return DEFAULT_UPDATE_TIMEOUT_MS;
549
+ const parsed = Number(value);
550
+ if (Number.isInteger(parsed) && parsed > 0 && parsed <= MAX_UPDATE_TIMEOUT_MS) {
551
+ return parsed;
552
+ }
553
+ // Falling back rather than clamping is deliberate: a clamp would silently
554
+ // substitute a materially different deadline. The warning tells operators
555
+ // their requested value was rejected before any update begins.
556
+ console.error(chalk.yellow(`⚠ Ignoring HQ_UPDATE_TIMEOUT_MS=${JSON.stringify(value)}: expected a positive integer no greater than ${MAX_UPDATE_TIMEOUT_MS}ms; using ${DEFAULT_UPDATE_TIMEOUT_MS}ms.`));
557
+ return DEFAULT_UPDATE_TIMEOUT_MS;
558
+ }
559
+ /**
560
+ * The actual installer runs in a small asynchronous supervisor, rather than
561
+ * directly under `spawnSync`. `spawnSync({ timeout })` only signals its direct
562
+ * child. That is unsafe here because the installer deliberately runs in its
563
+ * own process group: the leader can exit while its descendants keep the global
564
+ * store and registry connection alive. The CommonJS file is copied next to the
565
+ * compiled module for production and is directly runnable from source tests.
566
+ */
567
+ const UPDATE_COMMAND_SUPERVISOR = fileURLToPath(new URL("./update-command-supervisor.cjs", import.meta.url));
568
+ function readSupervisorSpawnError(errorMarker) {
569
+ try {
570
+ const value = JSON.parse(readFileSync(errorMarker, "utf-8"));
571
+ const code = typeof value.code === "string" ? value.code : undefined;
572
+ const detail = typeof value.detail === "string" ? value.detail : undefined;
573
+ return code === undefined && detail === undefined ? undefined : { code, detail };
574
+ }
575
+ catch {
576
+ return undefined;
577
+ }
578
+ }
537
579
  export function openInstallOutput(verbose, isTty = process.stdout.isTTY === true && process.stderr.isTTY === true) {
538
580
  if (verbose && isTty) {
581
+ const dir = mkdtempSync(path.join(os.tmpdir(), "hq-cli-install-"));
582
+ const timeoutMarker = path.join(dir, "timed-out");
583
+ const errorMarker = path.join(dir, "spawn-error.json");
539
584
  return {
540
585
  stdio: ["ignore", "inherit", "inherit"],
541
586
  stderrTail: () => "",
542
- dispose: () => { },
587
+ timeoutMarker,
588
+ errorMarker,
589
+ timedOut: () => existsSync(timeoutMarker),
590
+ spawnError: () => readSupervisorSpawnError(errorMarker),
591
+ dispose: () => {
592
+ try {
593
+ rmSync(dir, { recursive: true, force: true });
594
+ }
595
+ catch {
596
+ // Best-effort cleanup of the supervisor's timeout marker.
597
+ }
598
+ },
543
599
  };
544
600
  }
545
601
  const dir = mkdtempSync(path.join(os.tmpdir(), "hq-cli-install-"));
546
602
  const errPath = path.join(dir, "stderr.log");
603
+ const timeoutPath = path.join(dir, "timed-out");
604
+ const errorPath = path.join(dir, "spawn-error.json");
547
605
  const out = openSync(path.join(dir, "stdout.log"), "a");
548
606
  const err = openSync(errPath, "a");
549
607
  return {
@@ -556,6 +614,10 @@ export function openInstallOutput(verbose, isTty = process.stdout.isTTY === true
556
614
  return "";
557
615
  }
558
616
  },
617
+ timeoutMarker: timeoutPath,
618
+ errorMarker: errorPath,
619
+ timedOut: () => existsSync(timeoutPath),
620
+ spawnError: () => readSupervisorSpawnError(errorPath),
559
621
  dispose: () => {
560
622
  for (const fd of [out, err]) {
561
623
  try {
@@ -574,13 +636,28 @@ export function openInstallOutput(verbose, isTty = process.stdout.isTTY === true
574
636
  },
575
637
  };
576
638
  }
577
- export function runUpdateCommand(cmd, args, env) {
578
- const output = openInstallOutput(true);
639
+ export function runSupervisedUpdateCommand(cmd, args, output, env) {
579
640
  try {
580
641
  const plan = buildSpawnPlan(cmd, args);
581
- const result = spawnSync(plan.cmd, plan.args, inOwnProcessGroup({
642
+ const timeoutMs = updateTimeoutMs(env);
643
+ // The outer sync spawn waits only for our supervisor. The supervisor owns
644
+ // the wall-clock deadline, sends TERM to the updater's whole process group,
645
+ // waits briefly, sends KILL to survivors, and waits for its direct child so
646
+ // the completed result leaves no zombie behind.
647
+ const result = spawnSync(process.execPath, [
648
+ UPDATE_COMMAND_SUPERVISOR,
649
+ JSON.stringify({
650
+ cmd: plan.cmd,
651
+ args: plan.args,
652
+ shell: plan.shell,
653
+ timeoutMs,
654
+ graceMs: UPDATE_TERMINATE_GRACE_MS,
655
+ reapMs: UPDATE_REAP_GRACE_MS,
656
+ timeoutMarker: output.timeoutMarker,
657
+ errorMarker: output.errorMarker,
658
+ }),
659
+ ], inOwnProcessGroup({
582
660
  stdio: output.stdio,
583
- shell: plan.shell,
584
661
  ...(env ? { env } : {}),
585
662
  }));
586
663
  // spawnSync reports a missing executable via `error`, not a throw.
@@ -588,6 +665,26 @@ export function runUpdateCommand(cmd, args, env) {
588
665
  const code = result.error.code;
589
666
  return { ok: false, code, detail: result.error.message };
590
667
  }
668
+ if (output.timedOut()) {
669
+ const command = [cmd, ...args].join(" ");
670
+ const supervisorError = output.spawnError();
671
+ const detail = [
672
+ `timed out after ${timeoutMs}ms: ${command}`,
673
+ supervisorError?.detail,
674
+ ]
675
+ .filter(Boolean)
676
+ .join("; ");
677
+ console.error(chalk.red(`✗ Update ${detail}.`));
678
+ return { ok: false, code: "ETIMEDOUT", detail };
679
+ }
680
+ const spawnedError = output.spawnError();
681
+ if (spawnedError) {
682
+ return {
683
+ ok: false,
684
+ code: spawnedError.code,
685
+ detail: spawnedError.detail ?? "failed to start updater",
686
+ };
687
+ }
591
688
  if (result.status !== 0) {
592
689
  // When output went to a file rather than the user's terminal, its tail is
593
690
  // the only explanation anyone will ever see for this failure.
@@ -606,6 +703,12 @@ export function runUpdateCommand(cmd, args, env) {
606
703
  detail: err instanceof Error ? err.message : String(err),
607
704
  };
608
705
  }
706
+ }
707
+ export function runUpdateCommand(cmd, args, env) {
708
+ const output = openInstallOutput(true);
709
+ try {
710
+ return runSupervisedUpdateCommand(cmd, args, output, env);
711
+ }
609
712
  finally {
610
713
  output.dispose();
611
714
  }
@@ -796,6 +899,9 @@ export function checkUpdateConvergence(targetVersion, deps = {}) {
796
899
  * Exit codes:
797
900
  * 0 — update succeeded; user must rerun their command
798
901
  * 75 — update failed (EX_TEMPFAIL; common for sudo/EACCES on system npm)
902
+ *
903
+ * A timed-out updater is the exception: it has been forcibly reaped, so the
904
+ * gate returns and lets the user's command run on the current version.
799
905
  */
800
906
  function enforceUpdateRequired(decision, deps = {}) {
801
907
  const banner = chalk.red.bold(`✗ hq-cli ${decision.currentVersion} is below the minimum required version (${decision.minVersion}).`);
@@ -858,6 +964,8 @@ function enforceUpdateRequired(decision, deps = {}) {
858
964
  // unexpected throw from the install attempt.
859
965
  lock.release();
860
966
  }
967
+ if (exitCode === "continue")
968
+ return;
861
969
  process.exit(exitCode);
862
970
  }
863
971
  /**
@@ -923,7 +1031,7 @@ function attemptRequiredUpdate(decision, deps, install) {
923
1031
  // artifacts and retry the install ONCE. Guarded on `cleaned.length > 0` so a
924
1032
  // plain EACCES on an otherwise-healthy prefix falls straight through to the
925
1033
  // sudo retry below without a redundant reinstall attempt.
926
- if (!result.ok && prefix) {
1034
+ if (!result.ok && result.code !== "ETIMEDOUT" && prefix) {
927
1035
  const cleaner = deps.cleanStale ?? cleanStalePartialInstall;
928
1036
  const cleaned = cleaner(prefix);
929
1037
  if (cleaned.length > 0) {
@@ -943,7 +1051,7 @@ function attemptRequiredUpdate(decision, deps, install) {
943
1051
  // Never for pnpm or Bun: elevation changes the package manager's global home,
944
1052
  // leaving the user's shim untouched while reporting success. A failed
945
1053
  // non-npm update must surface instead.
946
- if (!result.ok && primaryCmd && !isManagedOutsideNpm) {
1054
+ if (!result.ok && result.code !== "ETIMEDOUT" && primaryCmd && !isManagedOutsideNpm) {
947
1055
  console.error(chalk.dim(` Update failed unprivileged; retrying with: sudo -n ${primaryCmd} ${primaryArgs.join(" ")}`));
948
1056
  const sudoResult = performUpdateCommand("sudo", ["-n", primaryCmd, ...primaryArgs], runner);
949
1057
  if (sudoResult.ok)
@@ -973,6 +1081,15 @@ function attemptRequiredUpdate(decision, deps, install) {
973
1081
  if (isManagedOutsideNpm && result.code === "ENOENT" && command) {
974
1082
  console.error(chalk.dim(` (hq-pro suggests \`${command}\` — that is for npm-managed installs; use it only if you have switched this install to npm.)`));
975
1083
  }
1084
+ // The self-update is allowed to fail, but a deadline expiry must not turn
1085
+ // into a second, indefinite denial of the user's command. The supervisor
1086
+ // either reaped the group or recorded why the OS denied that signal; in
1087
+ // neither case did an update land. Continue on the current CLI rather than
1088
+ // pretending it did or aborting the command that caused the gate to run.
1089
+ if (result.code === "ETIMEDOUT") {
1090
+ console.error(chalk.dim(" Continuing on the current hq-cli version."));
1091
+ return "continue";
1092
+ }
976
1093
  return 75;
977
1094
  }
978
1095
  // Read-your-writes: a "successful" install into the npm prefix does not
@@ -1028,6 +1145,7 @@ export function shouldSkipGate(argv) {
1028
1145
  export const __test__ = {
1029
1146
  CLIENT_ID,
1030
1147
  CONVERGENCE_TIMEOUT_MS,
1148
+ DEFAULT_UPDATE_TIMEOUT_MS,
1031
1149
  ENDPOINT_PATH,
1032
1150
  FETCH_TIMEOUT_MS,
1033
1151
  checkUpdateConvergence,
@@ -1058,5 +1176,6 @@ export const __test__ = {
1058
1176
  resolveRunningInstall,
1059
1177
  resolveRunningManager,
1060
1178
  resolveRunningPrefix,
1179
+ updateTimeoutMs,
1061
1180
  };
1062
1181
  //# sourceMappingURL=version-gate.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indigoai-us/hq-cli",
3
- "version": "5.115.6",
3
+ "version": "5.116.0",
4
4
  "description": "HQ by Indigo management CLI \u2014 modules and cloud sync",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -34,7 +34,7 @@
34
34
  "dependencies": {
35
35
  "@aws-sdk/client-iot-data-plane": "^3.1096.0",
36
36
  "@aws-sdk/client-s3": "^3.1049.0",
37
- "@indigoai-us/hq-cloud": "~6.16.41",
37
+ "@indigoai-us/hq-cloud": "~6.16.48",
38
38
  "@indigoai-us/hq-flags-client": "^0.1.2",
39
39
  "@sentry/node": "^10.49.0",
40
40
  "@tobilu/qmd": "2.5.3",