@indigoai-us/hq-cli 5.345.3 → 5.345.4
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
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [5.345.4] — 2026-10-05
|
|
6
|
+
- Run a pinned or newer managed hq-cloud sync runner ahead of the staged sync bundle, so daemon sync no longer stays on the hq-cloud version bundled with the CLI after an update or pin (hq-cli#1547).
|
|
7
|
+
- Avoid dispatching the global hook when the working directory is inside an HQ root.
|
|
8
|
+
|
|
5
9
|
## [5.345.3] — 2026-10-05
|
|
6
10
|
- Retry a blocker freshness lane-inbox fallback after a transient append failure.
|
|
7
11
|
- Scope bundled-script stdin checks to changes in their workflow, build inputs, CLI sources, tests, or shell assets.
|
|
@@ -23,6 +23,11 @@ export type ManagedHqCloud = PickedHqCloud & {
|
|
|
23
23
|
export declare function managedHqCloudDir(daemonRoot: string): string;
|
|
24
24
|
/** The package folder inside the updater's install prefix. */
|
|
25
25
|
export declare function managedHqCloudRoot(daemonRoot: string): string;
|
|
26
|
+
/**
|
|
27
|
+
* True only when both versions are valid semver and `a` is older than `b`.
|
|
28
|
+
* Prereleases count, so 6.18.33-alpha.1 is older than 6.18.33.
|
|
29
|
+
*/
|
|
30
|
+
export declare function hqCloudVersionOlder(a: string, b: string): boolean;
|
|
26
31
|
/**
|
|
27
32
|
* `usable` says whether a copy has what the daemon runs; a newer managed copy
|
|
28
33
|
* that fails it (an interrupted or damaged install) is passed over.
|
|
@@ -10,6 +10,7 @@ import { spawnSync } from "node:child_process";
|
|
|
10
10
|
import * as fs from "node:fs";
|
|
11
11
|
import { createRequire } from "node:module";
|
|
12
12
|
import * as path from "node:path";
|
|
13
|
+
import { lt as semverLt, valid as validSemver } from "semver";
|
|
13
14
|
import { readDaemonConfig } from "./config.js";
|
|
14
15
|
import { buildSpawnPlan } from "../../utils/windows-spawn.js";
|
|
15
16
|
export const HQ_CLOUD_PACKAGE = "@indigoai-us/hq-cloud";
|
|
@@ -31,6 +32,15 @@ function newer(a, b) {
|
|
|
31
32
|
}
|
|
32
33
|
return false;
|
|
33
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* True only when both versions are valid semver and `a` is older than `b`.
|
|
37
|
+
* Prereleases count, so 6.18.33-alpha.1 is older than 6.18.33.
|
|
38
|
+
*/
|
|
39
|
+
export function hqCloudVersionOlder(a, b) {
|
|
40
|
+
const va = validSemver(a.trim());
|
|
41
|
+
const vb = validSemver(b.trim());
|
|
42
|
+
return !!va && !!vb && semverLt(va, vb);
|
|
43
|
+
}
|
|
34
44
|
/**
|
|
35
45
|
* `usable` says whether a copy has what the daemon runs; a newer managed copy
|
|
36
46
|
* that fails it (an interrupted or damaged install) is passed over.
|
package/dist/lib/daemon/run.d.ts
CHANGED
|
@@ -59,7 +59,10 @@ export type SyncRunnerSource = {
|
|
|
59
59
|
kind: "unavailable";
|
|
60
60
|
runnerPath: null;
|
|
61
61
|
};
|
|
62
|
-
export declare function syncRunnerSourceForPlan(stagedRunnerPath: string | null, hqCloud: PickedHqCloud | null, legacyBundledRunnerPath?: string | null
|
|
62
|
+
export declare function syncRunnerSourceForPlan(stagedRunnerPath: string | null, hqCloud: PickedHqCloud | null, legacyBundledRunnerPath?: string | null, staged?: {
|
|
63
|
+
version: string | null;
|
|
64
|
+
pinnedVersion?: string | null;
|
|
65
|
+
}): SyncRunnerSource;
|
|
63
66
|
/** Whether every executable in the first service plan remains usable after the CLI package is replaced. */
|
|
64
67
|
export declare function daemonPlanCanReleasePackageUseLease(plans: readonly UnitPlan[], replaceablePackageRoots: readonly string[], stagingFailed: boolean, hqBootstrapReady?: boolean): boolean;
|
|
65
68
|
export declare function daemonHqChildCommand(nodeBinary: string, hqBinary: string, args: readonly string[], bootstrapPath: string | null, packagePrefix: string | null): [string, string[]];
|
package/dist/lib/daemon/run.js
CHANGED
|
@@ -37,7 +37,7 @@ import { startHqdServer } from "./server.js";
|
|
|
37
37
|
import { heartbeatFile, startHeartbeat } from "./heartbeat.js";
|
|
38
38
|
import { createProtocolTail, ensureProtocolFifo, openProtocolWriter } from "./protocol-tail.js";
|
|
39
39
|
import { takeControlRequests } from "./requests.js";
|
|
40
|
-
import { currentHqCloud, currentHqCloudAfterLeaseRelease, hqCloudRunners } from "./hq-cloud.js";
|
|
40
|
+
import { currentHqCloud, currentHqCloudAfterLeaseRelease, hqCloudRunners, hqCloudVersionOlder } from "./hq-cloud.js";
|
|
41
41
|
import { stageSyncRunnerBundle } from "./sync-runner-bundle.js";
|
|
42
42
|
import { stageHqChildBootstrap } from "./hq-child-bootstrap-stage.js";
|
|
43
43
|
import { runnerSessionName } from "./remote-control.js";
|
|
@@ -76,7 +76,21 @@ export function handoffForPackageUpdateRequest(requested, restartDaemon) {
|
|
|
76
76
|
const LEGACY_PROBE_TTL_MS = 60_000;
|
|
77
77
|
/** A flag read that has not answered by then counts as off. */
|
|
78
78
|
const HANDOFF_FLAG_TIMEOUT_MS = 10_000;
|
|
79
|
-
export function syncRunnerSourceForPlan(stagedRunnerPath, hqCloud, legacyBundledRunnerPath = null) {
|
|
79
|
+
export function syncRunnerSourceForPlan(stagedRunnerPath, hqCloud, legacyBundledRunnerPath = null, staged = { version: null }) {
|
|
80
|
+
// A managed copy lives in the daemon directory, so it survives a CLI
|
|
81
|
+
// replacement. Before lease release pickHqCloud returns it only when it is
|
|
82
|
+
// pinned or newer than the bundled copy; after release it is the only
|
|
83
|
+
// eligible package, so it must not displace a newer staged bundle unless it
|
|
84
|
+
// is the pinned version. The staged bundle is built from the bundled copy and
|
|
85
|
+
// otherwise only stands in for that copy (hq-cli#1547).
|
|
86
|
+
const managedYieldsToStaged = stagedRunnerPath !== null &&
|
|
87
|
+
hqCloud?.source === "managed" &&
|
|
88
|
+
hqCloud.version !== staged.pinnedVersion &&
|
|
89
|
+
staged.version !== null &&
|
|
90
|
+
hqCloudVersionOlder(hqCloud.version, staged.version);
|
|
91
|
+
if (hqCloud?.source === "managed" && !managedYieldsToStaged) {
|
|
92
|
+
return { kind: "managed-full-package", runnerPath: path.join(hqCloud.root, "dist", "bin", "sync-runner.js") };
|
|
93
|
+
}
|
|
80
94
|
if (stagedRunnerPath)
|
|
81
95
|
return { kind: "staged-sync", runnerPath: stagedRunnerPath };
|
|
82
96
|
if (!hqCloud) {
|
|
@@ -86,10 +100,7 @@ export function syncRunnerSourceForPlan(stagedRunnerPath, hqCloud, legacyBundled
|
|
|
86
100
|
}
|
|
87
101
|
// The caller has already selected a usable full package; preserve the
|
|
88
102
|
// conventional path here, including in tests with virtual package roots.
|
|
89
|
-
|
|
90
|
-
return hqCloud.source === "managed"
|
|
91
|
-
? { kind: "managed-full-package", runnerPath }
|
|
92
|
-
: { kind: "bundled-full-package", runnerPath };
|
|
103
|
+
return { kind: "bundled-full-package", runnerPath: path.join(hqCloud.root, "dist", "bin", "sync-runner.js") };
|
|
93
104
|
}
|
|
94
105
|
function isPathWithin(root, candidate) {
|
|
95
106
|
if (!root || !path.isAbsolute(candidate))
|
|
@@ -559,7 +570,7 @@ export async function runDaemon(opts) {
|
|
|
559
570
|
legacySyncRunnerPath = resolveSyncRunnerPath();
|
|
560
571
|
legacySyncRunnerPathChecked = true;
|
|
561
572
|
}
|
|
562
|
-
const syncSource = syncRunnerSourceForPlan(staged?.runnerPath ?? null, hqCloud, packageUseLeaseReleased ? null : legacySyncRunnerPath);
|
|
573
|
+
const syncSource = syncRunnerSourceForPlan(staged?.runnerPath ?? null, hqCloud, packageUseLeaseReleased ? null : legacySyncRunnerPath, { version: staged?.metadata.version ?? null, pinnedVersion: config.hqCloudVersion ?? null });
|
|
563
574
|
if (!syncSource.runnerPath && !warnedMissingSyncRunner) {
|
|
564
575
|
warnedMissingSyncRunner = true;
|
|
565
576
|
log("warn", "the hq-cloud sync runner was not found in this CLI install; sync is unavailable");
|
|
@@ -49,7 +49,7 @@ export function hqSkillsSource(hqRoot) {
|
|
|
49
49
|
}
|
|
50
50
|
/** The exact hook command registered for one event (absolute path, quoted). */
|
|
51
51
|
export function hookCommand(hqRoot, event, target = 'master') {
|
|
52
|
-
const skipInsideRoot = `if [ -d "\${CLAUDE_PROJECT_DIR:-}" ] && [ "\${CLAUDE_PROJECT_DIR}" -ef ${shellQuote(hqRoot)} ]; then exit 0; fi; `;
|
|
52
|
+
const skipInsideRoot = `if [ -d "\${CLAUDE_PROJECT_DIR:-}" ] && [ "\${CLAUDE_PROJECT_DIR}" -ef ${shellQuote(hqRoot)} ]; then exit 0; fi; cwd=$(pwd -P 2>/dev/null || pwd); while [ -n "$cwd" ]; do if [ -f "$cwd/core/core.yaml" ] && [ -f "$cwd/companies/manifest.yaml" ]; then exit 0; fi; [ "$cwd" = / ] && break; cwd=\${cwd%/*}; [ -n "$cwd" ] || cwd=/; done; `;
|
|
53
53
|
if (target === 'shim')
|
|
54
54
|
return `${skipInsideRoot}exec /bin/sh ${shellQuote(shimPath(hqRoot))} ${shellQuote(event)} --runtime claude`;
|
|
55
55
|
return `${skipInsideRoot}BASH_ENV=/dev/null exec bash ${shellQuote(masterHookPath(hqRoot))} ${shellQuote(event)}`;
|