@indigoai-us/hq-cli 5.309.0 → 5.310.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 +15 -0
- package/assets/scaffold/core/scripts/hq-dm-bind.sh +585 -0
- package/assets/scaffold/core/scripts/work-mesh-live-bind-trusted.sh +152 -0
- package/dist/command-catalog.generated.d.ts +30 -1
- package/dist/command-catalog.generated.js +37 -1
- package/dist/commands/agents.js +6 -0
- package/dist/commands/core-checkpoint-gate-setting.d.ts +0 -10
- package/dist/commands/core-checkpoint-gate-setting.js +3 -3
- package/dist/commands/core-dm-bind-native.d.ts +32 -0
- package/dist/commands/core-dm-bind-native.js +668 -0
- package/dist/commands/core-native-utilities.d.ts +2 -1
- package/dist/commands/core-native-utilities.js +4 -2
- package/dist/commands/core-ontology.d.ts +1 -9
- package/dist/commands/core-ontology.js +4 -4
- package/dist/commands/core-policy.d.ts +0 -3
- package/dist/commands/core-policy.js +3 -3
- package/dist/commands/core-work-mesh-bind-trusted-native.d.ts +32 -0
- package/dist/commands/core-work-mesh-bind-trusted-native.js +190 -0
- package/dist/commands/core.js +6 -0
- package/dist/commands/files.js +1 -1
- package/dist/commands/index-cmd.js +1 -1
- package/dist/commands/integrations-api.d.ts +9 -4
- package/dist/commands/integrations-api.js +9 -6
- package/dist/commands/integrations-manage.js +11 -3
- package/dist/commands/integrations.d.ts +8 -0
- package/dist/commands/integrations.js +83 -0
- package/dist/commands/lanes-checks.d.ts +0 -2
- package/dist/commands/lanes-checks.js +2 -2
- package/dist/commands/lanes.d.ts +0 -11
- package/dist/commands/lanes.js +6 -6
- package/dist/commands/mcp-registration.d.ts +5 -11
- package/dist/commands/mcp-registration.js +2 -6
- package/dist/lib/core-utils/sentry-report.d.ts +5 -11
- package/dist/lib/core-utils/sentry-report.js +2 -2
- package/dist/lib/db/local.d.ts +0 -5
- package/dist/lib/db/local.js +1 -1
- package/dist/lib/db/migrate.d.ts +0 -12
- package/dist/lib/db/migrate.js +5 -5
- package/dist/lib/db/sql.d.ts +0 -10
- package/dist/lib/db/sql.js +4 -8
- package/dist/lib/doctor/checks/codex-wiring.js +1 -33
- package/dist/lib/doctor/checks/grok-wiring.js +1 -34
- package/dist/lib/doctor/deep/effects.js +1 -3
- package/dist/lib/doctor/deep/sandbox.js +1 -3
- package/dist/lib/doctor/fixtures/schema.js +1 -3
- package/dist/lib/doctor/shared.d.ts +8 -0
- package/dist/lib/doctor/shared.js +38 -0
- package/dist/lib/lanes/autopilot.js +6 -4
- package/dist/lib/monitor.d.ts +2 -6
- package/dist/lib/monitor.js +5 -5
- package/dist/lib/report-publish.d.ts +1 -10
- package/dist/lib/report-publish.js +3 -9
- package/dist/lib/search-index/index.d.ts +7 -0
- package/dist/lib/search-index/index.js +21 -2
- package/dist/lib/search-index/qmd-exit-diagnostics.d.ts +5 -0
- package/dist/lib/search-index/qmd-exit-diagnostics.js +40 -0
- package/dist/lib/service-manager/launchd.d.ts +0 -7
- package/dist/lib/service-manager/launchd.js +2 -2
- package/dist/main.js +7 -2
- package/dist/startup-registration.js +9 -1
- package/dist/utils/client-health.d.ts +0 -4
- package/dist/utils/client-health.js +2 -2
- package/dist/utils/feedback-diagnostics.d.ts +2 -1
- package/dist/utils/feedback-log-bundle.d.ts +0 -25
- package/dist/utils/feedback-log-bundle.js +5 -5
- package/dist/utils/feedback-logs.d.ts +4 -38
- package/dist/utils/feedback-logs.js +7 -7
- package/dist/utils/feedback-screenshots.d.ts +0 -1
- package/dist/utils/feedback-screenshots.js +1 -1
- package/dist/utils/install-tree-torn.d.ts +19 -13
- package/dist/utils/install-tree-torn.js +77 -2
- package/dist/utils/local-files-overview.d.ts +0 -6
- package/dist/utils/local-files-overview.js +1 -1
- package/dist/utils/qmd-model-download-error.d.ts +0 -8
- package/dist/utils/qmd-model-download-error.js +1 -1
- package/dist/utils/qmd-query-document-error.d.ts +0 -6
- package/dist/utils/qmd-query-document-error.js +1 -1
- package/dist/utils/scaffold-fast-import-guard.d.ts +0 -1
- package/dist/utils/scaffold-fast-import-guard.js +1 -1
- package/dist/utils/scaffold-sibling-references.d.ts +3 -2
- package/dist/utils/sentry-fingerprint.js +17 -0
- package/dist/utils/version-gate.d.ts +4 -0
- package/dist/utils/version-gate.js +13 -0
- package/package.json +1 -1
|
@@ -3,7 +3,7 @@ import * as path from "node:path";
|
|
|
3
3
|
import { vaultApiFetch } from "./vault-api.js";
|
|
4
4
|
export const MAX_SCREENSHOTS = 5;
|
|
5
5
|
// Per-image ceiling. Screenshots are PNG/JPEG captures; 10 MB is generous.
|
|
6
|
-
|
|
6
|
+
const MAX_SCREENSHOT_BYTES = 10 * 1024 * 1024;
|
|
7
7
|
// Extension → content type. Must stay in sync with the server's allowed set
|
|
8
8
|
// (hq-pro feedback-screenshots.ts ALLOWED_CONTENT_TYPES).
|
|
9
9
|
const EXT_CONTENT_TYPE = {
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
* for a module-EVALUATION throw that carries NO real `code` (HQ-CLI-1T) — see
|
|
38
38
|
* {@link ModuleErrorDialect}.
|
|
39
39
|
*/
|
|
40
|
-
|
|
40
|
+
type ModuleNotFoundCode = "ERR_MODULE_NOT_FOUND" | "MODULE_NOT_FOUND" | "ENOENT" | "EVAL_THROW";
|
|
41
41
|
/**
|
|
42
42
|
* The closed set of resolution-failure dialects, verified on Node v22.23.1 and
|
|
43
43
|
* the observed Node 24 synchronous ESM loader shape (the @sentry/node
|
|
@@ -78,7 +78,7 @@ export type ModuleNotFoundCode = "ERR_MODULE_NOT_FOUND" | "MODULE_NOT_FOUND" | "
|
|
|
78
78
|
* - `unknown`: a module-not-found whose message did not parse; recovery
|
|
79
79
|
* still waits on the lock / retired-dir / quiet signals.
|
|
80
80
|
*/
|
|
81
|
-
|
|
81
|
+
type ModuleErrorDialect = "esm-path" | "esm-package" | "cjs-path" | "cjs-package" | "cjs-relative" | "esm-enoent" | "cjs-enoent" | "resolve-enoent" | "eval-throw" | "unknown";
|
|
82
82
|
/**
|
|
83
83
|
* The missing thing, re-resolvable by the readiness probe:
|
|
84
84
|
* - `path`: an absolute filesystem path (a `.js` file, or a package dir).
|
|
@@ -214,15 +214,6 @@ export interface InstallTreeFs {
|
|
|
214
214
|
* Any filesystem error reads as "not present" rather than throwing.
|
|
215
215
|
*/
|
|
216
216
|
export declare function installTargetPresent(target: ModuleErrorTarget, fs?: InstallTreeFs): boolean;
|
|
217
|
-
/**
|
|
218
|
-
* Default ceiling on the CUMULATIVE time the wait may spend observing a FRESH
|
|
219
|
-
* foreign lock held by a live pid. Equal to the shared update-lock contract's own
|
|
220
|
-
* staleness window ({@link UPDATE_LOCK_STALE_MS} — "no healthy npm install -g of
|
|
221
|
-
* this package runs 10 minutes"), so the wait trusts a live cooperating writer for
|
|
222
|
-
* exactly as long as the contract legitimises holding the lock, and never longer.
|
|
223
|
-
* Overridable by the seam (env HQ_INSTALL_SETTLE_LOCK_TIMEOUT_MS).
|
|
224
|
-
*/
|
|
225
|
-
export declare const DEFAULT_INSTALL_SETTLE_LOCK_WAIT_MS: number;
|
|
226
217
|
export interface WaitForInstallTreeSettledArgs {
|
|
227
218
|
target: ModuleErrorTarget;
|
|
228
219
|
/** The running install's package dir (from resolveRunningInstall), or null. */
|
|
@@ -276,8 +267,14 @@ export interface WaitForInstallTreeSettledResult {
|
|
|
276
267
|
retiredSiblingPresent?: boolean | null;
|
|
277
268
|
/** A stable partial retired-sibling scan ended the wait without claiming the tree is stale. */
|
|
278
269
|
retiredSiblingUnverified?: boolean;
|
|
270
|
+
retiredSiblingKind?: RetiredSiblingKind;
|
|
271
|
+
installTargetState?: InstallTargetState;
|
|
272
|
+
updaterPidAlive?: UpdaterPidAlive;
|
|
279
273
|
}
|
|
280
|
-
|
|
274
|
+
type RetiredSiblingKind = "npm_hash_retired" | "hq_staging" | "hq_backup" | "other" | "unknown";
|
|
275
|
+
type InstallTargetState = "missing" | "manifest_valid" | "manifest_invalid" | "unreadable" | "unknown";
|
|
276
|
+
type UpdaterPidAlive = "true" | "false" | "unknown";
|
|
277
|
+
type VersionGatePhase = "preflight" | "lock_acquired" | "installer_running" | "verifying" | "rollback" | "complete" | "failed" | "unknown";
|
|
281
278
|
/**
|
|
282
279
|
* Poll until the install tree has been continuously READY for `quietMs`, or the
|
|
283
280
|
* wait exhausts its bounds. READY means: (i) no FRESH update lock held by another
|
|
@@ -337,7 +334,7 @@ export interface InstallRewriteSignalArgs {
|
|
|
337
334
|
export declare function installRewriteInProgress(args: InstallRewriteSignalArgs): boolean;
|
|
338
335
|
/** The recovery OUTCOME, which — not the dialect — decides capture. */
|
|
339
336
|
export type InstallTreeTornOutcome = "guarded" | "unsettled" | "reexec-failed" | "lock-held" | "stale_tree" | "retired_unverified";
|
|
340
|
-
|
|
337
|
+
type InstallTreeState = "in_flight" | "stale";
|
|
341
338
|
export type InstallTreeTornDiagnostics = {
|
|
342
339
|
dialect: ModuleErrorDialect;
|
|
343
340
|
code: ModuleNotFoundCode;
|
|
@@ -362,6 +359,10 @@ export type InstallTreeTornDiagnostics = {
|
|
|
362
359
|
retiredSiblingPresent?: boolean;
|
|
363
360
|
/** The thrown error's `name` for the eval-throw dialect (HQ-CLI-1T), else "". */
|
|
364
361
|
errorName: string;
|
|
362
|
+
retiredSiblingKind: RetiredSiblingKind;
|
|
363
|
+
installTargetState: InstallTargetState;
|
|
364
|
+
versionGatePhase: VersionGatePhase;
|
|
365
|
+
updaterPidAlive: UpdaterPidAlive;
|
|
365
366
|
};
|
|
366
367
|
export interface InstallTreeTornInit {
|
|
367
368
|
/** The original loader error, carried unchanged as `cause`. */
|
|
@@ -383,6 +384,10 @@ export interface InstallTreeTornInit {
|
|
|
383
384
|
lockTool?: string;
|
|
384
385
|
/** Final retired sibling presence, or null when the scope directory was unreadable. */
|
|
385
386
|
retiredSiblingPresent?: boolean | null;
|
|
387
|
+
retiredSiblingKind?: RetiredSiblingKind;
|
|
388
|
+
installTargetState?: InstallTargetState;
|
|
389
|
+
versionGatePhase?: VersionGatePhase;
|
|
390
|
+
updaterPidAlive?: UpdaterPidAlive;
|
|
386
391
|
}
|
|
387
392
|
/**
|
|
388
393
|
* A torn-install failure that stays VISIBLE in Sentry with bounded, hq-derived
|
|
@@ -431,4 +436,5 @@ export declare const INSTALL_TREE_LOCK_HELD_REMEDY: string;
|
|
|
431
436
|
* {@link INSTALL_TREE_TORN_REMEDY} byte-for-byte.
|
|
432
437
|
*/
|
|
433
438
|
export declare function installTreeTornStderrLine(err: InstallTreeTornError): string;
|
|
439
|
+
export {};
|
|
434
440
|
//# sourceMappingURL=install-tree-torn.d.ts.map
|
|
@@ -609,7 +609,7 @@ export function installTargetPresent(target, fs = nodeInstallTreeFs) {
|
|
|
609
609
|
* exactly as long as the contract legitimises holding the lock, and never longer.
|
|
610
610
|
* Overridable by the seam (env HQ_INSTALL_SETTLE_LOCK_TIMEOUT_MS).
|
|
611
611
|
*/
|
|
612
|
-
|
|
612
|
+
const DEFAULT_INSTALL_SETTLE_LOCK_WAIT_MS = UPDATE_LOCK_STALE_MS;
|
|
613
613
|
/** The scope/leaf split of CLI_NAME, e.g. `@indigoai-us` / `hq-cli`. */
|
|
614
614
|
function cliNameParts() {
|
|
615
615
|
const slash = CLI_NAME.indexOf("/");
|
|
@@ -652,6 +652,68 @@ function retiredSiblingPresent(packageRoot, fs) {
|
|
|
652
652
|
const entries = fs.readdirSync(parent); // caller treats a throw as "not ready"
|
|
653
653
|
return entries.some((entry) => entry.startsWith(stagingPrefix));
|
|
654
654
|
}
|
|
655
|
+
/** Reduce an observed sibling basename to a closed kind without retaining it. */
|
|
656
|
+
function retiredSiblingKind(packageRoot, fs) {
|
|
657
|
+
if (!packageRoot)
|
|
658
|
+
return "unknown";
|
|
659
|
+
try {
|
|
660
|
+
const { leaf } = cliNameParts();
|
|
661
|
+
const prefix = "." + leaf + "-";
|
|
662
|
+
const siblings = fs
|
|
663
|
+
.readdirSync(path.dirname(packageRoot))
|
|
664
|
+
.filter((entry) => entry.startsWith(prefix))
|
|
665
|
+
.sort();
|
|
666
|
+
if (siblings.length === 0)
|
|
667
|
+
return "unknown";
|
|
668
|
+
const name = siblings[0];
|
|
669
|
+
if (/^\.hq-cli-staging-[A-Za-z0-9-]+$/.test(name))
|
|
670
|
+
return "hq_staging";
|
|
671
|
+
if (/^\.hq-cli-backup-[A-Za-z0-9-]+$/.test(name))
|
|
672
|
+
return "hq_backup";
|
|
673
|
+
if (/^\.hq-cli-[A-Za-z0-9]{8}$/.test(name))
|
|
674
|
+
return "npm_hash_retired";
|
|
675
|
+
return "other";
|
|
676
|
+
}
|
|
677
|
+
catch {
|
|
678
|
+
return "unknown";
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
/** Classify current target/manifest state without retaining paths or file contents. */
|
|
682
|
+
function installTargetState(target, packageRoot, fs) {
|
|
683
|
+
if (target.kind === "unknown")
|
|
684
|
+
return "unknown";
|
|
685
|
+
if (!installTargetPresent(target, fs))
|
|
686
|
+
return "missing";
|
|
687
|
+
if (!packageRoot)
|
|
688
|
+
return "unknown";
|
|
689
|
+
try {
|
|
690
|
+
const raw = fs.readFileSync(path.join(packageRoot, "package.json"), "utf-8");
|
|
691
|
+
let manifest;
|
|
692
|
+
try {
|
|
693
|
+
manifest = JSON.parse(raw);
|
|
694
|
+
}
|
|
695
|
+
catch {
|
|
696
|
+
return "manifest_invalid";
|
|
697
|
+
}
|
|
698
|
+
return manifest.name === CLI_NAME ? "manifest_valid" : "manifest_invalid";
|
|
699
|
+
}
|
|
700
|
+
catch (error) {
|
|
701
|
+
return error?.code === "ENOENT" ? "missing" : "unreadable";
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
/** Read only the lock's recorded owner PID; never retain or report the PID. */
|
|
705
|
+
function updaterPidAlive(lockPath, fs, isPidAlive) {
|
|
706
|
+
try {
|
|
707
|
+
const lock = JSON.parse(fs.readFileSync(lockPath, "utf-8"));
|
|
708
|
+
if (typeof lock.pid !== "number" || !Number.isSafeInteger(lock.pid) || lock.pid <= 0) {
|
|
709
|
+
return "unknown";
|
|
710
|
+
}
|
|
711
|
+
return isPidAlive(lock.pid) ? "true" : "false";
|
|
712
|
+
}
|
|
713
|
+
catch {
|
|
714
|
+
return "unknown";
|
|
715
|
+
}
|
|
716
|
+
}
|
|
655
717
|
/** Snapshot retired sibling trees so changes below their root stay observable. */
|
|
656
718
|
function retiredSiblingActivitySignature(packageRoot, fs) {
|
|
657
719
|
const { leaf } = cliNameParts();
|
|
@@ -851,7 +913,7 @@ function canProbeUnsettledRetiredSibling(snapshot) {
|
|
|
851
913
|
return snapshot.observed ||
|
|
852
914
|
(snapshot.nonRetiredObserved && snapshot.retiredSiblingPresent === true);
|
|
853
915
|
}
|
|
854
|
-
|
|
916
|
+
const DEFAULT_INSTALL_TREE_STALE_PROBE_MS = 1_500;
|
|
855
917
|
/**
|
|
856
918
|
* Poll until the install tree has been continuously READY for `quietMs`, or the
|
|
857
919
|
* wait exhausts its bounds. READY means: (i) no FRESH update lock held by another
|
|
@@ -986,6 +1048,9 @@ export async function waitForInstallTreeSettled(args) {
|
|
|
986
1048
|
sawTreeChange,
|
|
987
1049
|
staleTree: false,
|
|
988
1050
|
retiredSiblingPresent: retiredPresent,
|
|
1051
|
+
retiredSiblingKind: retiredSiblingKind(args.packageRoot, fs),
|
|
1052
|
+
installTargetState: installTargetState(args.target, args.packageRoot, fs),
|
|
1053
|
+
updaterPidAlive: updaterPidAlive(args.lockPath, fs, isPidAlive),
|
|
989
1054
|
};
|
|
990
1055
|
}
|
|
991
1056
|
if (!ready &&
|
|
@@ -1007,6 +1072,9 @@ export async function waitForInstallTreeSettled(args) {
|
|
|
1007
1072
|
staleTree: lastActivitySnapshot.observed,
|
|
1008
1073
|
retiredSiblingPresent: retiredPresent,
|
|
1009
1074
|
retiredSiblingUnverified: !lastActivitySnapshot.observed && lastActivitySnapshot.retiredSiblingPresent === true,
|
|
1075
|
+
retiredSiblingKind: retiredSiblingKind(args.packageRoot, fs),
|
|
1076
|
+
installTargetState: installTargetState(args.target, args.packageRoot, fs),
|
|
1077
|
+
updaterPidAlive: updaterPidAlive(args.lockPath, fs, isPidAlive),
|
|
1010
1078
|
};
|
|
1011
1079
|
}
|
|
1012
1080
|
// Give up when the TREE has had a full unlocked budget with no writer visible,
|
|
@@ -1029,6 +1097,9 @@ export async function waitForInstallTreeSettled(args) {
|
|
|
1029
1097
|
sawTreeChange,
|
|
1030
1098
|
staleTree: false,
|
|
1031
1099
|
retiredSiblingPresent: retiredPresent,
|
|
1100
|
+
retiredSiblingKind: retiredSiblingKind(args.packageRoot, fs),
|
|
1101
|
+
installTargetState: installTargetState(args.target, args.packageRoot, fs),
|
|
1102
|
+
updaterPidAlive: updaterPidAlive(args.lockPath, fs, isPidAlive),
|
|
1032
1103
|
};
|
|
1033
1104
|
}
|
|
1034
1105
|
// The first poll at which the wait has reached `deadlineMs` while a lock is
|
|
@@ -1129,6 +1200,10 @@ export class InstallTreeTornError extends Error {
|
|
|
1129
1200
|
lockTool: boundedDiagnosticValue(init.lockTool ?? "", LOCK_TOOL_BYTES),
|
|
1130
1201
|
node: process.version,
|
|
1131
1202
|
errorName: boundedDiagnosticValue(init.classified.errorName ?? "", ERRORNAME_BYTES),
|
|
1203
|
+
retiredSiblingKind: init.retiredSiblingKind ?? "unknown",
|
|
1204
|
+
installTargetState: init.installTargetState ?? "unknown",
|
|
1205
|
+
versionGatePhase: init.versionGatePhase ?? "unknown",
|
|
1206
|
+
updaterPidAlive: init.updaterPidAlive ?? "unknown",
|
|
1132
1207
|
};
|
|
1133
1208
|
if (typeof init.retiredSiblingPresent === "boolean") {
|
|
1134
1209
|
diagnostics.retiredSiblingPresent = init.retiredSiblingPresent;
|
|
@@ -26,12 +26,6 @@
|
|
|
26
26
|
* must never break a heartbeat, let alone a CLI command.
|
|
27
27
|
*/
|
|
28
28
|
import { type ClientHealthLocalFilesOverview } from "./client-health-contract.js";
|
|
29
|
-
/**
|
|
30
|
-
* Only the last N bytes of the sync log are read for error-line counting —
|
|
31
|
-
* this bounds memory/CPU regardless of how large the on-disk log has grown.
|
|
32
|
-
* Metadata (size/age) still reflects the FULL file via `fs.stat`.
|
|
33
|
-
*/
|
|
34
|
-
export declare const LOCAL_FILES_OVERVIEW_TAIL_READ_BYTES: number;
|
|
35
29
|
/**
|
|
36
30
|
* Count lines that look like an error marker in the last
|
|
37
31
|
* {@link LOCAL_FILES_OVERVIEW_TAIL_READ_BYTES} of `filePath`, capped at
|
|
@@ -36,7 +36,7 @@ import { CLIENT_HEALTH_MAX_ERROR_LINE_COUNT, CLIENT_HEALTH_MAX_FILE_AGE_SECONDS,
|
|
|
36
36
|
* this bounds memory/CPU regardless of how large the on-disk log has grown.
|
|
37
37
|
* Metadata (size/age) still reflects the FULL file via `fs.stat`.
|
|
38
38
|
*/
|
|
39
|
-
|
|
39
|
+
const LOCAL_FILES_OVERVIEW_TAIL_READ_BYTES = 2 * 1024 * 1024; // 2 MiB
|
|
40
40
|
/** Case-insensitive marker matched against each line (or JSON-lines `level`). */
|
|
41
41
|
const ERROR_MARKER = /error/i;
|
|
42
42
|
const JSON_LEVEL_ERROR = /"level"\s*:\s*"error"\s*(?=[,}])/i;
|
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The actionable remedy shown to the caller. Input-free — nothing is
|
|
3
|
-
* interpolated from the error, the argv, or qmd's output — so there is no
|
|
4
|
-
* injection surface. The disposition holds for every reading of a vanished
|
|
5
|
-
* `.ipull` partial (a concurrent finisher, a cache eviction, a partially-written
|
|
6
|
-
* file): it is transient and retryable, and re-running resumes the download.
|
|
7
|
-
*/
|
|
8
|
-
export declare const QMD_MODEL_DOWNLOAD_REMEDY: string;
|
|
9
1
|
/**
|
|
10
2
|
* True when `err` is a qmd interrupted-model-download failure: qmd's OWN captured
|
|
11
3
|
* streams carry BOTH an ENOENT-rename signature AND a `.ipull` partial-download
|
|
@@ -50,7 +50,7 @@ const MODEL_FETCH_READS = new Set(["query", "vsearch", "embed"]);
|
|
|
50
50
|
* `.ipull` partial (a concurrent finisher, a cache eviction, a partially-written
|
|
51
51
|
* file): it is transient and retryable, and re-running resumes the download.
|
|
52
52
|
*/
|
|
53
|
-
|
|
53
|
+
const QMD_MODEL_DOWNLOAD_REMEDY = "hq's local search couldn't finish downloading a model it needs: the partial " +
|
|
54
54
|
"download file disappeared before it could be saved, which usually means " +
|
|
55
55
|
"another hq or qmd process on this machine finished or cleared the same " +
|
|
56
56
|
"download. Re-run the command — the download resumes.";
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The actionable remedy shown to the caller. Input-free: nothing is interpolated
|
|
3
|
-
* from the error, the argv, or qmd's output, so there is no injection surface —
|
|
4
|
-
* matching the bounded-fingerprint discipline of its siblings.
|
|
5
|
-
*/
|
|
6
|
-
export declare const QMD_QUERY_DOCUMENT_REMEDY: string;
|
|
7
1
|
/**
|
|
8
2
|
* True when `err` carries, in qmd's OWN captured streams, a query-document
|
|
9
3
|
* syntax diagnostic — i.e. qmd's `query` parser rejected the positional as a
|
|
@@ -61,7 +61,7 @@ const QUERY_DOCUMENT_READS = new Set(["query"]);
|
|
|
61
61
|
* from the error, the argv, or qmd's output, so there is no injection surface —
|
|
62
62
|
* matching the bounded-fingerprint discipline of its siblings.
|
|
63
63
|
*/
|
|
64
|
-
|
|
64
|
+
const QMD_QUERY_DOCUMENT_REMEDY = "Hybrid search was given a multi-line query, and qmd read it as a structured " +
|
|
65
65
|
"query document whose lines must each begin with a lex:, vec:, hyde:, or " +
|
|
66
66
|
"intent: prefix. Put your search terms on a single line, or prefix every line, " +
|
|
67
67
|
"then run the command again.";
|
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
export declare function forbiddenFastPathModules(loadedModules: readonly string[], distDirectory: string, allowedCommandModules?: readonly string[]): string[];
|
|
2
1
|
export declare function assertFastPathStaysCommandGraphFree(loadedModules: readonly string[], distDirectory: string, allowedCommandModules?: readonly string[]): void;
|
|
3
2
|
//# sourceMappingURL=scaffold-fast-import-guard.d.ts.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
|
-
|
|
3
|
+
function forbiddenFastPathModules(loadedModules, distDirectory, allowedCommandModules = []) {
|
|
4
4
|
const dist = path.resolve(distDirectory);
|
|
5
5
|
const main = path.join(dist, "main.js");
|
|
6
6
|
const commands = `${path.join(dist, "commands")}${path.sep}`;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
type SiblingAssetReference = {
|
|
2
2
|
referringAsset: string;
|
|
3
3
|
targetAsset: string;
|
|
4
4
|
};
|
|
5
|
-
|
|
5
|
+
type HqRootAssetReference = {
|
|
6
6
|
referringAsset: string;
|
|
7
7
|
reference: string;
|
|
8
8
|
};
|
|
@@ -18,4 +18,5 @@ export type ScaffoldReferenceScan = {
|
|
|
18
18
|
*/
|
|
19
19
|
export declare function scanScaffoldSiblingReferences(scaffoldRoot: string): ScaffoldReferenceScan;
|
|
20
20
|
export declare function assertNoMissingScaffoldSiblings(scan: ScaffoldReferenceScan): void;
|
|
21
|
+
export {};
|
|
21
22
|
//# sourceMappingURL=scaffold-sibling-references.d.ts.map
|
|
@@ -250,6 +250,23 @@ export function installTreeTornSentryTagsFor(err) {
|
|
|
250
250
|
if (typeof diagnostics.retiredSiblingPresent === "boolean") {
|
|
251
251
|
tags.retired_sibling = diagnostics.retiredSiblingPresent ? "present" : "absent";
|
|
252
252
|
}
|
|
253
|
+
const retiredSiblingKind = diagnostics.retiredSiblingKind;
|
|
254
|
+
tags.retired_sibling_kind = typeof retiredSiblingKind === "string" &&
|
|
255
|
+
["npm_hash_retired", "hq_staging", "hq_backup", "other", "unknown"].includes(retiredSiblingKind)
|
|
256
|
+
? retiredSiblingKind
|
|
257
|
+
: "unknown";
|
|
258
|
+
const targetState = diagnostics.installTargetState;
|
|
259
|
+
tags.install_target_state = typeof targetState === "string" &&
|
|
260
|
+
["missing", "manifest_valid", "manifest_invalid", "unreadable", "unknown"].includes(targetState)
|
|
261
|
+
? targetState
|
|
262
|
+
: "unknown";
|
|
263
|
+
const gatePhase = diagnostics.versionGatePhase;
|
|
264
|
+
tags.version_gate_phase = typeof gatePhase === "string" &&
|
|
265
|
+
["preflight", "lock_acquired", "installer_running", "verifying", "rollback", "complete", "failed", "unknown"].includes(gatePhase)
|
|
266
|
+
? gatePhase
|
|
267
|
+
: "unknown";
|
|
268
|
+
const pidAlive = diagnostics.updaterPidAlive;
|
|
269
|
+
tags.updater_pid_alive = pidAlive === "true" || pidAlive === "false" ? pidAlive : "unknown";
|
|
253
270
|
if (recoveryOutcome)
|
|
254
271
|
tags.recovery_outcome = recoveryOutcome;
|
|
255
272
|
const lockTool = diagnostics.lockTool;
|
|
@@ -29,6 +29,9 @@
|
|
|
29
29
|
*/
|
|
30
30
|
import { buildSpawnPlan, quoteForWindowsShell } from "./windows-spawn.js";
|
|
31
31
|
import { type UpdateLockHandle } from "./update-lock.js";
|
|
32
|
+
export type VersionGatePhase = "preflight" | "lock_acquired" | "installer_running" | "verifying" | "rollback" | "complete" | "failed" | "unknown";
|
|
33
|
+
/** The last closed version-gate phase observed in this process. */
|
|
34
|
+
export declare function getVersionGatePhase(): VersionGatePhase;
|
|
32
35
|
/** Which package manager owns the running global install. */
|
|
33
36
|
export type InstallManager = "npm" | "pnpm" | "bun";
|
|
34
37
|
export interface VersionCheckResponse {
|
|
@@ -489,6 +492,7 @@ export declare function enforceVersionGate(onUpdateRecommended?: (decision: Vers
|
|
|
489
492
|
export declare function shouldSkipGate(argv: readonly string[]): boolean;
|
|
490
493
|
export declare const __test__: {
|
|
491
494
|
CLIENT_ID: string;
|
|
495
|
+
getVersionGatePhase: typeof getVersionGatePhase;
|
|
492
496
|
CONVERGENCE_TIMEOUT_MS: number;
|
|
493
497
|
DEFAULT_UPDATE_TIMEOUT_MS: number;
|
|
494
498
|
ENDPOINT_PATH: string;
|
|
@@ -45,6 +45,11 @@ const CLIENT_ID = "hq-cli";
|
|
|
45
45
|
const ENDPOINT_PATH = "/v1/client-version/check";
|
|
46
46
|
const FETCH_TIMEOUT_MS = 3_000;
|
|
47
47
|
const LATEST_PACKAGE_SPEC = `${CLI_NAME}@latest`;
|
|
48
|
+
let currentVersionGatePhase = "unknown";
|
|
49
|
+
/** The last closed version-gate phase observed in this process. */
|
|
50
|
+
export function getVersionGatePhase() {
|
|
51
|
+
return currentVersionGatePhase;
|
|
52
|
+
}
|
|
48
53
|
function isOptedOut() {
|
|
49
54
|
return process.env.HQ_NO_UPDATE_CHECK === "1";
|
|
50
55
|
}
|
|
@@ -1291,6 +1296,7 @@ export function checkUpdateConvergence(targetVersion, deps = {}) {
|
|
|
1291
1296
|
* gate returns and lets the user's command run on the current version.
|
|
1292
1297
|
*/
|
|
1293
1298
|
function enforceUpdateRequired(decision, deps = {}) {
|
|
1299
|
+
currentVersionGatePhase = "preflight";
|
|
1294
1300
|
const banner = chalk.red.bold(`✗ hq-cli ${CLI_VERSION} is below the minimum required version (${decision.minVersion}).`);
|
|
1295
1301
|
console.error(banner);
|
|
1296
1302
|
if (decision.message)
|
|
@@ -1341,6 +1347,7 @@ function enforceUpdateRequired(decision, deps = {}) {
|
|
|
1341
1347
|
console.error(chalk.dim(" Another hq updater is already installing (update lock held); continuing on the current version."));
|
|
1342
1348
|
return;
|
|
1343
1349
|
}
|
|
1350
|
+
currentVersionGatePhase = "lock_acquired";
|
|
1344
1351
|
let attempt;
|
|
1345
1352
|
try {
|
|
1346
1353
|
attempt = attemptRequiredUpdate(decision, deps, install);
|
|
@@ -1406,6 +1413,7 @@ function attemptRequiredUpdate(decision, deps, install) {
|
|
|
1406
1413
|
const updateEnv = pnpmUpdateEnv(install);
|
|
1407
1414
|
let result;
|
|
1408
1415
|
let noticeFlush = null;
|
|
1416
|
+
currentVersionGatePhase = "installer_running";
|
|
1409
1417
|
if (isManagedOutsideNpm) {
|
|
1410
1418
|
console.error(chalk.dim(` Detected a ${install.manager}-managed global install; updating with ${install.manager}`));
|
|
1411
1419
|
console.error(chalk.dim(` Running: ${primaryCmd} ${primaryArgs.join(" ")}`));
|
|
@@ -1475,6 +1483,7 @@ function attemptRequiredUpdate(decision, deps, install) {
|
|
|
1475
1483
|
result = sudoResult;
|
|
1476
1484
|
}
|
|
1477
1485
|
if (!result.ok) {
|
|
1486
|
+
currentVersionGatePhase = "failed";
|
|
1478
1487
|
console.error(chalk.red(`✗ Update failed${result.detail ? `: ${result.detail}` : ""}.`));
|
|
1479
1488
|
// If the npm prefix is still unwritable after the unprivileged and sudo
|
|
1480
1489
|
// attempts, explain the permission wall plainly instead of leaving exit 75
|
|
@@ -1524,11 +1533,14 @@ function attemptRequiredUpdate(decision, deps, install) {
|
|
|
1524
1533
|
// install shadowing the copy we just wrote is reported as the failed update
|
|
1525
1534
|
// it is rather than as a success the caller will keep retrying — see
|
|
1526
1535
|
// checkUpdateConvergence.
|
|
1536
|
+
currentVersionGatePhase = "verifying";
|
|
1527
1537
|
const converged = (deps.checkConvergence ?? checkUpdateConvergence)(decision.latestVersion);
|
|
1528
1538
|
if (converged === false) {
|
|
1539
|
+
currentVersionGatePhase = "failed";
|
|
1529
1540
|
console.error(chalk.red(`✗ Update did not take effect: \`hq\` on PATH still resolves a different build than ${decision.latestVersion}.`));
|
|
1530
1541
|
return 75;
|
|
1531
1542
|
}
|
|
1543
|
+
currentVersionGatePhase = "complete";
|
|
1532
1544
|
console.error(chalk.green(`✓ Updated to hq-cli ${decision.latestVersion}. Rerun your command.`));
|
|
1533
1545
|
return 0;
|
|
1534
1546
|
}
|
|
@@ -1585,6 +1597,7 @@ export function shouldSkipGate(argv) {
|
|
|
1585
1597
|
}
|
|
1586
1598
|
export const __test__ = {
|
|
1587
1599
|
CLIENT_ID,
|
|
1600
|
+
getVersionGatePhase,
|
|
1588
1601
|
CONVERGENCE_TIMEOUT_MS,
|
|
1589
1602
|
DEFAULT_UPDATE_TIMEOUT_MS,
|
|
1590
1603
|
ENDPOINT_PATH,
|