@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.
- package/CHANGELOG.md +72 -11
- package/dist/command-catalog.generated.d.ts +162 -2
- package/dist/command-catalog.generated.js +205 -2
- package/dist/command-registration-plan.d.ts +6 -0
- package/dist/command-registration-plan.js +1 -0
- package/dist/commands/agent-enroll.d.ts +105 -0
- package/dist/commands/agent-enroll.js +273 -0
- package/dist/commands/agent-kit.d.ts +53 -0
- package/dist/commands/agent-kit.js +260 -0
- package/dist/commands/agent-mcp.d.ts +22 -0
- package/dist/commands/agent-mcp.js +104 -0
- package/dist/commands/agent-probe.d.ts +71 -0
- package/dist/commands/agent-probe.js +294 -0
- package/dist/commands/agent.d.ts +12 -0
- package/dist/commands/agent.js +23 -0
- package/dist/commands/agents.d.ts +27 -0
- package/dist/commands/agents.js +280 -6
- package/dist/commands/secrets.js +17 -5
- package/dist/lib/agent-kit/creds.d.ts +60 -0
- package/dist/lib/agent-kit/creds.js +123 -0
- package/dist/lib/agent-kit/kit-config.d.ts +29 -0
- package/dist/lib/agent-kit/kit-config.js +54 -0
- package/dist/lib/agent-kit/log.d.ts +17 -0
- package/dist/lib/agent-kit/log.js +46 -0
- package/dist/lib/agent-kit/mcp/jsonrpc.d.ts +84 -0
- package/dist/lib/agent-kit/mcp/jsonrpc.js +164 -0
- package/dist/lib/agent-kit/mcp/tools.d.ts +45 -0
- package/dist/lib/agent-kit/mcp/tools.js +280 -0
- package/dist/lib/agent-kit/paths.d.ts +42 -0
- package/dist/lib/agent-kit/paths.js +56 -0
- package/dist/lib/agent-kit/run/heartbeat.d.ts +52 -0
- package/dist/lib/agent-kit/run/heartbeat.js +97 -0
- package/dist/lib/agent-kit/run/inbox.d.ts +59 -0
- package/dist/lib/agent-kit/run/inbox.js +152 -0
- package/dist/lib/agent-kit/run/mesh-listener.d.ts +58 -0
- package/dist/lib/agent-kit/run/mesh-listener.js +193 -0
- package/dist/lib/agent-kit/run/sync.d.ts +33 -0
- package/dist/lib/agent-kit/run/sync.js +58 -0
- package/dist/lib/agent-kit/services.d.ts +21 -0
- package/dist/lib/agent-kit/services.js +46 -0
- package/dist/lib/agent-kit/skills.d.ts +18 -0
- package/dist/lib/agent-kit/skills.js +149 -0
- package/dist/lib/service-manager/index.d.ts +43 -0
- package/dist/lib/service-manager/index.js +114 -0
- package/dist/lib/service-manager/launchd.d.ts +23 -0
- package/dist/lib/service-manager/launchd.js +81 -0
- package/dist/lib/service-manager/systemd.d.ts +19 -0
- package/dist/lib/service-manager/systemd.js +72 -0
- package/dist/lib/service-manager/types.d.ts +32 -0
- package/dist/lib/service-manager/types.js +26 -0
- package/dist/utils/self-update.js +2 -30
- package/dist/utils/update-command-supervisor.cjs +194 -0
- package/dist/utils/version-gate.d.ts +18 -0
- package/dist/utils/version-gate.js +126 -7
- 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
|
-
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
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",
|