@indigoai-us/hq-cli 5.345.69 → 5.345.71
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 +5 -0
- package/dist/fast-core.d.ts +10 -1
- package/dist/fast-core.js +75 -14
- package/dist/index.js +77 -11
- package/dist/startup-registration.d.ts +2 -0
- package/dist/startup-registration.js +20 -0
- package/dist/utils/install-tree-torn.d.ts +1 -1
- package/dist/utils/sentry-fingerprint.js +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [5.345.71] — 2026-10-10
|
|
6
|
+
|
|
7
|
+
## [5.345.70] — 2026-10-10
|
|
8
|
+
- Native `hq core` fast-route imports now use the bounded torn-install recovery path. If an update temporarily removes a route module, the CLI waits for the package tree to settle and re-executes once; a module that remains missing prints the reinstall remedy.
|
|
9
|
+
|
|
5
10
|
## [5.345.69] — 2026-10-10
|
|
6
11
|
- Supported native core handlers now run by default without a flag snapshot. Explicit false values still turn them off; delegate and worker-skills remain default-off pending parity.
|
|
7
12
|
|
package/dist/fast-core.d.ts
CHANGED
|
@@ -7,17 +7,26 @@
|
|
|
7
7
|
* module. The registration in `core.ts` remains the complete fallback path.
|
|
8
8
|
*/
|
|
9
9
|
import { parseFastCore } from "./fast-scaffold.js";
|
|
10
|
+
type RecoveredModule<T> = {
|
|
11
|
+
module?: T;
|
|
12
|
+
reexecStatus?: number;
|
|
13
|
+
};
|
|
14
|
+
type RecoverFastImportOptions = {
|
|
15
|
+
reexecOnSettled?: boolean;
|
|
16
|
+
};
|
|
17
|
+
type RecoverFastImport = <T>(load: () => Promise<T>, options?: RecoverFastImportOptions) => Promise<RecoveredModule<T>>;
|
|
10
18
|
export declare function readStdinIfPiped(stream: NodeJS.ReadStream, maxBytes?: number): Promise<string>;
|
|
11
19
|
/**
|
|
12
20
|
* Run the fast-path core command named in `argv` and exit with its code.
|
|
13
21
|
* Only call when {@link isFastCoreRequest} is true; a non-match returns without
|
|
14
22
|
* doing anything.
|
|
15
23
|
*/
|
|
16
|
-
export declare function runFastCore(argv: readonly string[], parsed?: ReturnType<typeof parseFastCore
|
|
24
|
+
export declare function runFastCore(argv: readonly string[], parsed?: ReturnType<typeof parseFastCore>, recoverImport?: RecoverFastImport): Promise<void>;
|
|
17
25
|
/**
|
|
18
26
|
* Resolve valid explicit roots for the two measured hook routes without loading
|
|
19
27
|
* the general root resolver. Anything uncertain falls through to that resolver,
|
|
20
28
|
* which retains its diagnostics and cwd-discovery behavior.
|
|
21
29
|
*/
|
|
22
30
|
export declare function resolveExplicitFastLiveRoot(hqRootArg: string | undefined, env: NodeJS.ProcessEnv): string | undefined;
|
|
31
|
+
export {};
|
|
23
32
|
//# sourceMappingURL=fast-core.d.ts.map
|
package/dist/fast-core.js
CHANGED
|
@@ -33,7 +33,7 @@ export async function readStdinIfPiped(stream, maxBytes) {
|
|
|
33
33
|
* Only call when {@link isFastCoreRequest} is true; a non-match returns without
|
|
34
34
|
* doing anything.
|
|
35
35
|
*/
|
|
36
|
-
export async function runFastCore(argv, parsed = parseFastCore(argv)) {
|
|
36
|
+
export async function runFastCore(argv, parsed = parseFastCore(argv), recoverImport = async (load) => ({ module: await load() })) {
|
|
37
37
|
if (!parsed)
|
|
38
38
|
return;
|
|
39
39
|
const profile = wantsFastRouteProfile(process.env);
|
|
@@ -54,7 +54,14 @@ export async function runFastCore(argv, parsed = parseFastCore(argv)) {
|
|
|
54
54
|
familyEnabled = envOverride;
|
|
55
55
|
}
|
|
56
56
|
else {
|
|
57
|
-
const
|
|
57
|
+
const loaded = await recoverImport(() => import("./native-fast-route.js"));
|
|
58
|
+
if (loaded.reexecStatus !== undefined) {
|
|
59
|
+
process.exitCode = loaded.reexecStatus;
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (!loaded.module)
|
|
63
|
+
throw new Error("Native fast-route module did not load");
|
|
64
|
+
const { fastFlagEnabled, fastFlagEnabledDefaultOn } = loaded.module;
|
|
58
65
|
const readFamilyFlag = isDefaultOnFastFamily(entry.familyFlagKey)
|
|
59
66
|
? fastFlagEnabledDefaultOn
|
|
60
67
|
: fastFlagEnabled;
|
|
@@ -62,15 +69,28 @@ export async function runFastCore(argv, parsed = parseFastCore(argv)) {
|
|
|
62
69
|
}
|
|
63
70
|
}
|
|
64
71
|
if (entry.kind === "native" && entry.fallbackToCommandGraph && !familyEnabled) {
|
|
65
|
-
const
|
|
66
|
-
|
|
72
|
+
const loaded = await recoverImport(() => import("./main.js"));
|
|
73
|
+
if (loaded.reexecStatus !== undefined) {
|
|
74
|
+
process.exitCode = loaded.reexecStatus;
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (!loaded.module)
|
|
78
|
+
throw new Error("Main command graph did not load");
|
|
79
|
+
await loaded.module.runCli();
|
|
67
80
|
return;
|
|
68
81
|
}
|
|
69
82
|
familyFlagMs = profileElapsed(profileStart, profileMark());
|
|
70
83
|
const useShell = entry.kind === "native" && Boolean(entry.familyFlagKey) && !familyEnabled;
|
|
71
84
|
let worktreeScopeFixEnabled;
|
|
72
85
|
if (entry.name === "repo-run-registry") {
|
|
73
|
-
const
|
|
86
|
+
const loaded = await recoverImport(() => import("./commands/core-rrr-worktree-scope-fix-fast-flag.js"));
|
|
87
|
+
if (loaded.reexecStatus !== undefined) {
|
|
88
|
+
process.exitCode = loaded.reexecStatus;
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (!loaded.module)
|
|
92
|
+
throw new Error("Repo-run-registry flag module did not load");
|
|
93
|
+
const { coreRrrWorktreeScopeFixFastFlagEnabled } = loaded.module;
|
|
74
94
|
worktreeScopeFixEnabled = await coreRrrWorktreeScopeFixFastFlagEnabled(process.env);
|
|
75
95
|
}
|
|
76
96
|
const shouldInjectRoot = useShell
|
|
@@ -96,14 +116,27 @@ export async function runFastCore(argv, parsed = parseFastCore(argv)) {
|
|
|
96
116
|
hqRoot = shortcutRoot;
|
|
97
117
|
}
|
|
98
118
|
else {
|
|
99
|
-
const
|
|
119
|
+
const loaded = await recoverImport(() => import("./utils/hq-roots-core.js"));
|
|
120
|
+
if (loaded.reexecStatus !== undefined) {
|
|
121
|
+
process.exitCode = loaded.reexecStatus;
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (!loaded.module)
|
|
125
|
+
throw new Error("HQ root resolver did not load");
|
|
126
|
+
const { resolveLiveRoot } = loaded.module;
|
|
100
127
|
try {
|
|
101
128
|
hqRoot = resolveLiveRoot(parsed.hqRootArg ? { hqRoot: parsed.hqRootArg } : {});
|
|
102
129
|
}
|
|
103
130
|
catch {
|
|
104
131
|
if (entry.kind === "native") {
|
|
105
|
-
const
|
|
106
|
-
|
|
132
|
+
const loaded = await recoverImport(() => import("./main.js"));
|
|
133
|
+
if (loaded.reexecStatus !== undefined) {
|
|
134
|
+
process.exitCode = loaded.reexecStatus;
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (!loaded.module)
|
|
138
|
+
throw new Error("Main command graph did not load");
|
|
139
|
+
await loaded.module.runCli();
|
|
107
140
|
return;
|
|
108
141
|
}
|
|
109
142
|
hqRoot = undefined;
|
|
@@ -113,9 +146,22 @@ export async function runFastCore(argv, parsed = parseFastCore(argv)) {
|
|
|
113
146
|
rootResolutionMs = profileElapsed(rootResolutionStartedAt, profileMark());
|
|
114
147
|
if (entry.kind === "native" && !useShell) {
|
|
115
148
|
const handlerImportStartedAt = profileMark();
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
149
|
+
const errorHandlers = await recoverImport(() => import("./utils/fast-stream-errors.js"));
|
|
150
|
+
if (errorHandlers.reexecStatus !== undefined) {
|
|
151
|
+
process.exitCode = errorHandlers.reexecStatus;
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
if (!errorHandlers.module)
|
|
155
|
+
throw new Error("Fast-path stream error handlers did not load");
|
|
156
|
+
errorHandlers.module.installFastPathStreamErrorHandlers();
|
|
157
|
+
const loadedHandler = await recoverImport(() => import(entry.modulePath));
|
|
158
|
+
if (loadedHandler.reexecStatus !== undefined) {
|
|
159
|
+
process.exitCode = loadedHandler.reexecStatus;
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
if (!loadedHandler.module)
|
|
163
|
+
throw new Error("Native fast handler module did not load");
|
|
164
|
+
const handlerModule = loadedHandler.module;
|
|
119
165
|
const handler = handlerModule[entry.exportName];
|
|
120
166
|
if (typeof handler !== "function") {
|
|
121
167
|
throw new Error(`Native fast handler ${entry.exportName} was not exported`);
|
|
@@ -140,8 +186,16 @@ export async function runFastCore(argv, parsed = parseFastCore(argv)) {
|
|
|
140
186
|
if (!entry.fallbackToCommandGraph) {
|
|
141
187
|
throw new Error(`Native fast handler ${entry.exportName} requested an unavailable command-graph fallback`);
|
|
142
188
|
}
|
|
143
|
-
|
|
144
|
-
|
|
189
|
+
// The fast handler has already run (and may have consumed stdin). If a
|
|
190
|
+
// torn main import is detected now, report it without replaying the command.
|
|
191
|
+
const loaded = await recoverImport(() => import("./main.js"), { reexecOnSettled: false });
|
|
192
|
+
if (loaded.reexecStatus !== undefined) {
|
|
193
|
+
process.exitCode = loaded.reexecStatus;
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
if (!loaded.module)
|
|
197
|
+
throw new Error("Main command graph did not load");
|
|
198
|
+
await loaded.module.runCli();
|
|
145
199
|
return;
|
|
146
200
|
}
|
|
147
201
|
// Let Node drain handler output before terminating. The timeout guard's
|
|
@@ -167,7 +221,14 @@ export async function runFastCore(argv, parsed = parseFastCore(argv)) {
|
|
|
167
221
|
...(entry.name === "repo-run-registry" ? { HQ_FLAG_CORE_RRR_WORKTREE_SCOPE_FIX: String(worktreeScopeFixEnabled) } : {}),
|
|
168
222
|
};
|
|
169
223
|
// Keep the rollback runner off native commands' import path.
|
|
170
|
-
const
|
|
224
|
+
const runner = await recoverImport(() => import("./utils/run-bundled-script.js"));
|
|
225
|
+
if (runner.reexecStatus !== undefined) {
|
|
226
|
+
process.exitCode = runner.reexecStatus;
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
if (!runner.module)
|
|
230
|
+
throw new Error("Bundled-script runner did not load");
|
|
231
|
+
const { runBundledScript } = runner.module;
|
|
171
232
|
const { code } = runBundledScript({
|
|
172
233
|
asset,
|
|
173
234
|
args: parsed.args,
|
package/dist/index.js
CHANGED
|
@@ -59,6 +59,80 @@ async function runMainWithInstallTreeRecovery() {
|
|
|
59
59
|
throw error;
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* Fast-route imports happen before main.ts command registration, so the
|
|
64
|
+
* registration recovery seam cannot see a torn fast module unless each import
|
|
65
|
+
* is run through the same bounded recovery boundary. Imports before handler
|
|
66
|
+
* execution may re-exec after a settled repair; the post-handler fallback passes
|
|
67
|
+
* reexecOnSettled=false so a torn main import is reported without replaying the
|
|
68
|
+
* handler or losing the stdin it already consumed.
|
|
69
|
+
*/
|
|
70
|
+
const recoverFastImport = async (load, options = {}) => {
|
|
71
|
+
try {
|
|
72
|
+
return { module: await load() };
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// Keep the clean hook path free of startup-registration's update-lock and
|
|
76
|
+
// package-root imports. Only a failed import pays for the recovery seam.
|
|
77
|
+
const { registerCommandsWithRecovery } = await import("./startup-registration.js");
|
|
78
|
+
let module;
|
|
79
|
+
try {
|
|
80
|
+
const result = await registerCommandsWithRecovery({
|
|
81
|
+
register: async () => {
|
|
82
|
+
module = await load();
|
|
83
|
+
},
|
|
84
|
+
argv: process.argv,
|
|
85
|
+
env: process.env,
|
|
86
|
+
entryPath: fileURLToPath(import.meta.url),
|
|
87
|
+
stderr: process.stderr,
|
|
88
|
+
reexecOnSettled: options.reexecOnSettled,
|
|
89
|
+
});
|
|
90
|
+
return result.reexecStatus === undefined
|
|
91
|
+
? { module }
|
|
92
|
+
: { reexecStatus: result.reexecStatus };
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
// Fast-route imports precede main.ts/Sentry initialization. When recovery
|
|
96
|
+
// cannot repair a still-absent module, report it here as the registration
|
|
97
|
+
// path does instead of relying on the later unhandled-rejection boundary.
|
|
98
|
+
const installTree = await import("./utils/install-tree-torn.js");
|
|
99
|
+
if (!installTree.isInstallTreeTornError(error))
|
|
100
|
+
throw error;
|
|
101
|
+
process.stderr.write(`${installTree.installTreeTornStderrLine(error)}\n`);
|
|
102
|
+
const sentry = await import("./sentry.js");
|
|
103
|
+
sentry.initSentry();
|
|
104
|
+
sentry.Sentry.captureException(error, installTree.installTreeTornCaptureContext(error));
|
|
105
|
+
sentry.finishSentrySession();
|
|
106
|
+
await sentry.Sentry.flush(2_000);
|
|
107
|
+
return { reexecStatus: 1 };
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
async function runFastCoreWithRecovery(parsed) {
|
|
112
|
+
const loaded = await recoverFastImport(() => import("./fast-core.js"));
|
|
113
|
+
if (loaded.reexecStatus !== undefined) {
|
|
114
|
+
process.exitCode = loaded.reexecStatus;
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
if (!loaded.module)
|
|
118
|
+
throw new Error("Fast core module did not load");
|
|
119
|
+
await loaded.module.runFastCore(process.argv, parsed, recoverFastImport);
|
|
120
|
+
}
|
|
121
|
+
async function runNativeFastRouteWithRecovery(parsed) {
|
|
122
|
+
const loaded = await recoverFastImport(() => import("./native-fast-route.js"));
|
|
123
|
+
if (loaded.reexecStatus !== undefined) {
|
|
124
|
+
process.exitCode = loaded.reexecStatus;
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
if (!loaded.module)
|
|
128
|
+
throw new Error("Native fast-route module did not load");
|
|
129
|
+
if (await loaded.module.nativeFastRouteEnabled(process.argv)) {
|
|
130
|
+
await runFastCoreWithRecovery(parsed);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
await runMainWithInstallTreeRecovery();
|
|
134
|
+
}
|
|
135
|
+
}
|
|
62
136
|
function dispatchCliEntry() {
|
|
63
137
|
const versionRequest = isVersionRequest(process.argv);
|
|
64
138
|
const helpRequest = !versionRequest && isCommandHelpRequest(process.argv);
|
|
@@ -99,21 +173,13 @@ function dispatchCliEntry() {
|
|
|
99
173
|
// An explicit enabled override has already decided the global gate. Skip the
|
|
100
174
|
// snapshot reader import on hot native plumbing paths; runFastCore still
|
|
101
175
|
// checks the command-family flag before choosing native or shell rollback.
|
|
102
|
-
void
|
|
176
|
+
void runFastCoreWithRecovery(parsedFastCore);
|
|
103
177
|
}
|
|
104
178
|
else if (parsedFastCore?.entry.kind === "native") {
|
|
105
179
|
// Native routes are default-off. Resolve the company-scoped persisted flag
|
|
106
180
|
// without loading the command graph; a miss or stale snapshot takes the
|
|
107
181
|
// existing full-graph route.
|
|
108
|
-
void
|
|
109
|
-
if (await nativeFastRouteEnabled(process.argv)) {
|
|
110
|
-
const { runFastCore } = await import("./fast-core.js");
|
|
111
|
-
await runFastCore(process.argv, parsedFastCore);
|
|
112
|
-
}
|
|
113
|
-
else {
|
|
114
|
-
await runMainWithInstallTreeRecovery();
|
|
115
|
-
}
|
|
116
|
-
});
|
|
182
|
+
void runNativeFastRouteWithRecovery(parsedFastCore);
|
|
117
183
|
}
|
|
118
184
|
else if (parseSecretsEnvFastRequest(process.argv)) {
|
|
119
185
|
const request = parseSecretsEnvFastRequest(process.argv);
|
|
@@ -138,7 +204,7 @@ function dispatchCliEntry() {
|
|
|
138
204
|
// Hot relocated plumbing (`hq core hq-session`, `hq core checkpoint-stop-gate`)
|
|
139
205
|
// dispatches straight to the bundled-script runner, so it never pays for the
|
|
140
206
|
// ~60-module command graph main.ts imports. See src/fast-scaffold.ts.
|
|
141
|
-
void
|
|
207
|
+
void runFastCoreWithRecovery(parsedFastCore);
|
|
142
208
|
}
|
|
143
209
|
else {
|
|
144
210
|
// Keep the command lifecycle detached from module evaluation, as it was
|
|
@@ -88,6 +88,8 @@ export interface RegisterCommandsWithRecoveryArgs {
|
|
|
88
88
|
/** The RUNNING install's own `dist/index.js` — the safe re-exec target. */
|
|
89
89
|
entryPath: string;
|
|
90
90
|
stderr: Pick<typeof process.stderr, "write">;
|
|
91
|
+
/** False when command execution already happened and must not be replayed. */
|
|
92
|
+
reexecOnSettled?: boolean;
|
|
91
93
|
deps?: RegisterRecoveryDeps;
|
|
92
94
|
}
|
|
93
95
|
export interface RegisterCommandsWithRecoveryResult {
|
|
@@ -179,6 +179,26 @@ export async function registerCommandsWithRecovery(args) {
|
|
|
179
179
|
const treeState = result.sawLock || result.sawRetired || result.sawTreeChange
|
|
180
180
|
? "in_flight"
|
|
181
181
|
: "stale";
|
|
182
|
+
if (args.reexecOnSettled === false) {
|
|
183
|
+
throw new InstallTreeTornError({
|
|
184
|
+
cause: err,
|
|
185
|
+
classified,
|
|
186
|
+
packageRoot,
|
|
187
|
+
versionGatePhase: readVersionGatePhase(deps),
|
|
188
|
+
outcome: "replay-unsafe",
|
|
189
|
+
treeState,
|
|
190
|
+
attempt: 1,
|
|
191
|
+
waitedMs: result.waitedMs,
|
|
192
|
+
sawLock: result.sawLock,
|
|
193
|
+
sawRetired: result.sawRetired,
|
|
194
|
+
retiredSiblingPresent: result.retiredSiblingPresent,
|
|
195
|
+
retiredSiblingKind: result.retiredSiblingKind,
|
|
196
|
+
installTargetState: result.installTargetState,
|
|
197
|
+
updaterPidAlive: result.updaterPidAlive,
|
|
198
|
+
sawTreeChange: result.sawTreeChange,
|
|
199
|
+
lockTool: result.lockTool,
|
|
200
|
+
});
|
|
201
|
+
}
|
|
182
202
|
const child = spawn(process.execPath, [...execArgv, entryPath, ...argv.slice(2)], {
|
|
183
203
|
stdio: "inherit",
|
|
184
204
|
env: {
|
|
@@ -336,7 +336,7 @@ export interface InstallRewriteSignalArgs {
|
|
|
336
336
|
*/
|
|
337
337
|
export declare function installRewriteInProgress(args: InstallRewriteSignalArgs): boolean;
|
|
338
338
|
/** The recovery OUTCOME, which — not the dialect — decides capture. */
|
|
339
|
-
export type InstallTreeTornOutcome = "guarded" | "unsettled" | "reexec-failed" | "lock-held" | "stale_tree" | "retired_unverified";
|
|
339
|
+
export type InstallTreeTornOutcome = "guarded" | "replay-unsafe" | "unsettled" | "reexec-failed" | "lock-held" | "stale_tree" | "retired_unverified";
|
|
340
340
|
type InstallTreeState = "in_flight" | "stale";
|
|
341
341
|
export type InstallTreeTornDiagnostics = {
|
|
342
342
|
dialect: ModuleErrorDialect;
|