@indigoai-us/hq-cli 5.162.0 → 5.164.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 +26 -0
- package/dist/command-catalog.generated.d.ts +188 -0
- package/dist/command-catalog.generated.js +235 -0
- package/dist/command-registration-plan.d.ts +7 -0
- package/dist/command-registration-plan.js +1 -0
- package/dist/commands/agent-enroll.d.ts +6 -1
- package/dist/commands/agent-enroll.js +30 -3
- package/dist/commands/agents.js +15 -22
- package/dist/commands/bot.js +56 -6
- package/dist/commands/daemon.d.ts +53 -0
- package/dist/commands/daemon.js +554 -0
- package/dist/commands/lanes.js +7 -0
- package/dist/commands/meetings.js +18 -16
- package/dist/commands/mesh.js +9 -0
- package/dist/lib/daemon/catalog.d.ts +85 -0
- package/dist/lib/daemon/catalog.js +274 -0
- package/dist/lib/daemon/config.d.ts +26 -0
- package/dist/lib/daemon/config.js +85 -0
- package/dist/lib/daemon/handoff.d.ts +53 -0
- package/dist/lib/daemon/handoff.js +136 -0
- package/dist/lib/daemon/install.d.ts +76 -0
- package/dist/lib/daemon/install.js +299 -0
- package/dist/lib/daemon/legacy.d.ts +35 -0
- package/dist/lib/daemon/legacy.js +120 -0
- package/dist/lib/daemon/lock.d.ts +47 -0
- package/dist/lib/daemon/lock.js +138 -0
- package/dist/lib/daemon/machine-type.d.ts +48 -0
- package/dist/lib/daemon/machine-type.js +139 -0
- package/dist/lib/daemon/migrate.d.ts +61 -0
- package/dist/lib/daemon/migrate.js +238 -0
- package/dist/lib/daemon/paths.d.ts +22 -0
- package/dist/lib/daemon/paths.js +42 -0
- package/dist/lib/daemon/remote-control.d.ts +35 -0
- package/dist/lib/daemon/remote-control.js +47 -0
- package/dist/lib/daemon/requests.d.ts +24 -0
- package/dist/lib/daemon/requests.js +63 -0
- package/dist/lib/daemon/run.d.ts +41 -0
- package/dist/lib/daemon/run.js +319 -0
- package/dist/lib/daemon/supervisor.d.ts +87 -0
- package/dist/lib/daemon/supervisor.js +523 -0
- package/dist/lib/daemon/updater.d.ts +39 -0
- package/dist/lib/daemon/updater.js +103 -0
- package/dist/lib/flag-registry.d.ts +7 -1
- package/dist/lib/flag-registry.js +8 -0
- package/dist/lib/mesh/live/backoff.d.ts +2 -2
- package/dist/lib/mesh/live/backoff.js +16 -3
- package/dist/lib/mesh/live/daemon/run.d.ts +2 -2
- package/dist/lib/mesh/live/daemon/run.js +5 -2
- package/dist/lib/mesh/live/flush.d.ts +3 -2
- package/dist/lib/mesh/live/flush.js +34 -7
- package/dist/lib/mesh/live/session-events-client.d.ts +2 -0
- package/dist/lib/mesh/live/session-events-client.js +25 -0
- package/dist/lib/service-manager/systemd.d.ts +2 -2
- package/dist/lib/service-manager/systemd.js +1 -1
- package/dist/lib/service-manager/types.d.ts +2 -0
- package/dist/main.d.ts +6 -0
- package/dist/main.js +53 -13
- package/dist/sentry.js +5 -1
- package/dist/startup-registration.d.ts +3 -1
- package/dist/startup-registration.js +25 -4
- package/dist/utils/cognito-session.d.ts +4 -0
- package/dist/utils/cognito-session.js +11 -10
- package/dist/utils/install-tree-torn.d.ts +29 -10
- package/dist/utils/install-tree-torn.js +262 -29
- package/dist/utils/plan-gate-error.d.ts +27 -13
- package/dist/utils/plan-gate-error.js +133 -58
- package/dist/utils/self-update.d.ts +9 -0
- package/dist/utils/self-update.js +15 -2
- package/dist/utils/sentry-fingerprint.d.ts +7 -1
- package/dist/utils/sentry-fingerprint.js +95 -15
- package/package.json +1 -1
|
@@ -27,6 +27,8 @@ import { classifyEvalThrow, classifyModuleNotFound, installRewriteInProgress, In
|
|
|
27
27
|
* shadow each other.
|
|
28
28
|
*/
|
|
29
29
|
export const INSTALL_TREE_RECOVERY_GUARD_ENV = "HQ_INSTALL_TREE_RECOVERED";
|
|
30
|
+
/** Carries the writer-state observed by the parent into the guarded re-exec. */
|
|
31
|
+
export const INSTALL_TREE_RECOVERY_TREE_STATE_ENV = "HQ_INSTALL_TREE_RECOVERY_TREE_STATE";
|
|
30
32
|
/** Operator override for the settle wait's deadline. */
|
|
31
33
|
export const INSTALL_SETTLE_TIMEOUT_ENV = "HQ_INSTALL_SETTLE_TIMEOUT_MS";
|
|
32
34
|
/** Default settle deadline (ms). Overridable by {@link INSTALL_SETTLE_TIMEOUT_ENV}. */
|
|
@@ -36,7 +38,7 @@ export const DEFAULT_INSTALL_SETTLE_TIMEOUT_MS = 90_000;
|
|
|
36
38
|
* actually happens. It tells a human why the command paused and is the e2e's
|
|
37
39
|
* synchronization point; it is never a remedy and never sets an error exit code.
|
|
38
40
|
*/
|
|
39
|
-
export const INSTALL_TREE_WAIT_NOTICE = "hq: the hq-cli install is
|
|
41
|
+
export const INSTALL_TREE_WAIT_NOTICE = "hq: checking whether the hq-cli install tree is still changing before retrying…";
|
|
40
42
|
/** Resolve the settle deadline from the environment (0 = evaluate once; default on invalid). */
|
|
41
43
|
export function resolveSettleTimeoutMs(env) {
|
|
42
44
|
const raw = env[INSTALL_SETTLE_TIMEOUT_ENV];
|
|
@@ -121,10 +123,12 @@ export async function registerCommandsWithRecovery(args) {
|
|
|
121
123
|
classified,
|
|
122
124
|
packageRoot,
|
|
123
125
|
outcome: "guarded",
|
|
126
|
+
treeState: env[INSTALL_TREE_RECOVERY_TREE_STATE_ENV] === "stale" ? "stale" : "in_flight",
|
|
124
127
|
attempt: 2,
|
|
125
128
|
waitedMs: 0,
|
|
126
129
|
sawLock: false,
|
|
127
130
|
sawRetired: false,
|
|
131
|
+
sawTreeChange: false,
|
|
128
132
|
});
|
|
129
133
|
}
|
|
130
134
|
stderr.write(`${INSTALL_TREE_WAIT_NOTICE}\n`);
|
|
@@ -137,6 +141,7 @@ export async function registerCommandsWithRecovery(args) {
|
|
|
137
141
|
onLockExtended: () => stderr.write(`${INSTALL_TREE_LOCK_WAIT_NOTICE}\n`),
|
|
138
142
|
});
|
|
139
143
|
if (!result.settled) {
|
|
144
|
+
const treeState = result.staleTree === true ? "stale" : "in_flight";
|
|
140
145
|
throw new InstallTreeTornError({
|
|
141
146
|
cause: err,
|
|
142
147
|
classified,
|
|
@@ -145,12 +150,15 @@ export async function registerCommandsWithRecovery(args) {
|
|
|
145
150
|
// writer held the shared lock past its own ceiling — report 'lock-held'
|
|
146
151
|
// (its remedy tells the user to let the update finish, never to reinstall
|
|
147
152
|
// over it). Otherwise the tree genuinely never settled with no writer
|
|
148
|
-
// visible
|
|
149
|
-
|
|
153
|
+
// visible. The settle probe only uses `stale_tree` when it could observe
|
|
154
|
+
// an unchanged tree with no active writer or retired sibling.
|
|
155
|
+
outcome: result.lockHeldAtEnd ? "lock-held" : result.staleTree ? "stale_tree" : "unsettled",
|
|
156
|
+
treeState,
|
|
150
157
|
attempt: 1,
|
|
151
158
|
waitedMs: result.waitedMs,
|
|
152
159
|
sawLock: result.sawLock,
|
|
153
160
|
sawRetired: result.sawRetired,
|
|
161
|
+
sawTreeChange: result.sawTreeChange,
|
|
154
162
|
lockHeldAtEnd: result.lockHeldAtEnd,
|
|
155
163
|
lockedMs: result.lockedMs,
|
|
156
164
|
lockTool: result.lockTool,
|
|
@@ -158,17 +166,30 @@ export async function registerCommandsWithRecovery(args) {
|
|
|
158
166
|
}
|
|
159
167
|
const spawn = deps.spawn ?? spawnSync;
|
|
160
168
|
const execArgv = deps.execArgv ?? process.execArgv;
|
|
161
|
-
const
|
|
169
|
+
const treeState = result.sawLock || result.sawRetired || result.sawTreeChange
|
|
170
|
+
? "in_flight"
|
|
171
|
+
: "stale";
|
|
172
|
+
const child = spawn(process.execPath, [...execArgv, entryPath, ...argv.slice(2)], {
|
|
173
|
+
stdio: "inherit",
|
|
174
|
+
env: {
|
|
175
|
+
...env,
|
|
176
|
+
[INSTALL_TREE_RECOVERY_GUARD_ENV]: "1",
|
|
177
|
+
[INSTALL_TREE_RECOVERY_TREE_STATE_ENV]: treeState,
|
|
178
|
+
},
|
|
179
|
+
});
|
|
162
180
|
if (child.error) {
|
|
163
181
|
throw new InstallTreeTornError({
|
|
164
182
|
cause: err,
|
|
165
183
|
classified,
|
|
166
184
|
packageRoot,
|
|
167
185
|
outcome: "reexec-failed",
|
|
186
|
+
treeState,
|
|
168
187
|
attempt: 1,
|
|
169
188
|
waitedMs: result.waitedMs,
|
|
170
189
|
sawLock: result.sawLock,
|
|
171
190
|
sawRetired: result.sawRetired,
|
|
191
|
+
sawTreeChange: result.sawTreeChange,
|
|
192
|
+
lockTool: result.lockTool,
|
|
172
193
|
});
|
|
173
194
|
}
|
|
174
195
|
// A signal-killed child has status null; surface it as a failure exit rather
|
|
@@ -81,6 +81,10 @@ export declare const DEFAULT_VAULT_API_URL: string;
|
|
|
81
81
|
*/
|
|
82
82
|
export declare function resolveDefaultHqRoot(opts?: {
|
|
83
83
|
onMissing?: "throw" | "fallback";
|
|
84
|
+
/** Defaults to process.env, process.cwd() and os.homedir(). */
|
|
85
|
+
env?: NodeJS.ProcessEnv;
|
|
86
|
+
cwd?: string;
|
|
87
|
+
home?: string;
|
|
84
88
|
}): string;
|
|
85
89
|
export declare const DEFAULT_HQ_ROOT: string;
|
|
86
90
|
/**
|
|
@@ -102,22 +102,24 @@ export const DEFAULT_VAULT_API_URL = process.env.HQ_VAULT_API_URL ?? "https://hq
|
|
|
102
102
|
* Re-importable as a function for command-time resolution and tests.
|
|
103
103
|
*/
|
|
104
104
|
export function resolveDefaultHqRoot(opts = {}) {
|
|
105
|
+
const env = opts.env ?? process.env;
|
|
106
|
+
const home = opts.home ?? os.homedir();
|
|
105
107
|
// Tier 1: explicit override.
|
|
106
|
-
if (
|
|
107
|
-
return path.resolve(
|
|
108
|
+
if (env.HQ_ROOT)
|
|
109
|
+
return path.resolve(env.HQ_ROOT);
|
|
108
110
|
// Tier 2: working-tree walk-up — operate on the HQ tree you're inside.
|
|
109
|
-
let cur = path.resolve(process.cwd());
|
|
111
|
+
let cur = path.resolve(opts.cwd ?? process.cwd());
|
|
110
112
|
while (cur !== path.dirname(cur)) {
|
|
111
113
|
if (isHqRoot(cur))
|
|
112
114
|
return cur;
|
|
113
115
|
cur = path.dirname(cur);
|
|
114
116
|
}
|
|
115
117
|
// Tiers 3–4: the installer/menubar's canonical "where is HQ" records.
|
|
116
|
-
const fromConfig = readHqRootFromHqConfig(path.join(
|
|
118
|
+
const fromConfig = readHqRootFromHqConfig(path.join(home, ".hq"));
|
|
117
119
|
if (fromConfig)
|
|
118
120
|
return path.resolve(fromConfig);
|
|
119
121
|
// Tier 5: discover a valid HQ root in the well-known locations.
|
|
120
|
-
const discovered = discoverHqRootViaCoreYaml();
|
|
122
|
+
const discovered = discoverHqRootViaCoreYaml(home);
|
|
121
123
|
if (discovered)
|
|
122
124
|
return discovered;
|
|
123
125
|
// Tier 6: nothing resolved.
|
|
@@ -127,7 +129,7 @@ export function resolveDefaultHqRoot(opts = {}) {
|
|
|
127
129
|
"or install HQ via the installer/menubar so ~/.hq/menubar.json records " +
|
|
128
130
|
"its location.");
|
|
129
131
|
}
|
|
130
|
-
return path.join(
|
|
132
|
+
return path.join(home, "hq");
|
|
131
133
|
}
|
|
132
134
|
/**
|
|
133
135
|
* Read the configured HQ root from the installer/menubar records under
|
|
@@ -156,8 +158,7 @@ function readJsonStringField(file, field) {
|
|
|
156
158
|
}
|
|
157
159
|
}
|
|
158
160
|
/** Well-known parent locations an HQ tree commonly lives in (mirrors hq-sync). */
|
|
159
|
-
function hqDiscoveryCandidates() {
|
|
160
|
-
const home = os.homedir();
|
|
161
|
+
function hqDiscoveryCandidates(home = os.homedir()) {
|
|
161
162
|
return [
|
|
162
163
|
path.join(home, "HQ"),
|
|
163
164
|
path.join(home, "hq"),
|
|
@@ -172,8 +173,8 @@ function hqDiscoveryCandidates() {
|
|
|
172
173
|
* signature. First match wins; returns undefined if none qualify. Cheap — a
|
|
173
174
|
* few stats plus one small YAML parse on a hit.
|
|
174
175
|
*/
|
|
175
|
-
function discoverHqRootViaCoreYaml() {
|
|
176
|
-
return hqDiscoveryCandidates().find(isValidHqRootSignature);
|
|
176
|
+
function discoverHqRootViaCoreYaml(home) {
|
|
177
|
+
return hqDiscoveryCandidates(home).find(isValidHqRootSignature);
|
|
177
178
|
}
|
|
178
179
|
/**
|
|
179
180
|
* True iff `dir` carries a valid hq-core `core.yaml` — canonical
|
|
@@ -179,6 +179,8 @@ export interface InstallTreeFs {
|
|
|
179
179
|
existsSync(p: string): boolean;
|
|
180
180
|
statSync(p: string): {
|
|
181
181
|
isDirectory(): boolean;
|
|
182
|
+
size?: number;
|
|
183
|
+
mtimeMs?: number;
|
|
182
184
|
};
|
|
183
185
|
readFileSync(p: string, encoding: "utf-8"): string;
|
|
184
186
|
readdirSync(p: string): string[];
|
|
@@ -225,6 +227,8 @@ export interface WaitForInstallTreeSettledArgs {
|
|
|
225
227
|
isPidAlive?: (pid: number) => boolean;
|
|
226
228
|
pollMs?: number;
|
|
227
229
|
quietMs?: number;
|
|
230
|
+
/** Short observation window for an unchanged, lock-free broken tree. */
|
|
231
|
+
staleProbeMs?: number;
|
|
228
232
|
deadlineMs: number;
|
|
229
233
|
/**
|
|
230
234
|
* Ceiling on the CUMULATIVE time observed under a FRESH foreign lock held by a
|
|
@@ -256,7 +260,12 @@ export interface WaitForInstallTreeSettledResult {
|
|
|
256
260
|
lockedMs: number;
|
|
257
261
|
/** The bounded `tool` of the last fresh foreign lock seen, or "" if none. */
|
|
258
262
|
lockTool: string;
|
|
263
|
+
/** Whether a relevant install-tree path changed during this wait. */
|
|
264
|
+
sawTreeChange?: boolean;
|
|
265
|
+
/** True when the lock-free tree stayed unchanged for `staleProbeMs`. */
|
|
266
|
+
staleTree?: boolean;
|
|
259
267
|
}
|
|
268
|
+
export declare const DEFAULT_INSTALL_TREE_STALE_PROBE_MS = 1500;
|
|
260
269
|
/**
|
|
261
270
|
* Poll until the install tree has been continuously READY for `quietMs`, or the
|
|
262
271
|
* wait exhausts its bounds. READY means: (i) no FRESH update lock held by another
|
|
@@ -265,14 +274,14 @@ export interface WaitForInstallTreeSettledResult {
|
|
|
265
274
|
* `.<leaf>-<rand>` retired sibling remains beside it. Every filesystem error makes
|
|
266
275
|
* its condition "not ready" rather than throwing.
|
|
267
276
|
*
|
|
268
|
-
* The wait is LOCK-AWARE and bounded
|
|
269
|
-
*
|
|
270
|
-
*
|
|
277
|
+
* The wait is LOCK-AWARE and bounded, so a live cooperating writer (the desktop
|
|
278
|
+
* app, the box's update timer, another `hq`) cannot make a legitimately-in-progress
|
|
279
|
+
* reinstall look stale:
|
|
271
280
|
* - UNLOCKED time — wall time observed with no fresh foreign lock — is bounded
|
|
272
|
-
* by `deadlineMs`.
|
|
273
|
-
*
|
|
274
|
-
*
|
|
275
|
-
*
|
|
281
|
+
* by `deadlineMs`. If the target is still missing, no lock or retired sibling
|
|
282
|
+
* is present, and the observed tree stays unchanged for `staleProbeMs`, the
|
|
283
|
+
* wait returns `staleTree` early. Any observed tree change keeps the ordinary
|
|
284
|
+
* settle deadline in force. `deadlineMs === 0` evaluates readiness once.
|
|
276
285
|
* - LOCKED time — wall time observed under a fresh foreign lock held by a live
|
|
277
286
|
* pid — is bounded SEPARATELY by `lockWaitMs` (default the lock contract's own
|
|
278
287
|
* 10-minute staleness window) and does NOT consume the settle budget. When
|
|
@@ -285,7 +294,8 @@ export interface WaitForInstallTreeSettledResult {
|
|
|
285
294
|
* began, so a starved event loop only makes the wait coarser, never unbounded. A
|
|
286
295
|
* stale lock (dead pid, older than the staleness window, unparseable) or our own
|
|
287
296
|
* pid never extends anything. The whole wait is bounded (≤ `deadlineMs +
|
|
288
|
-
* lockWaitMs`),
|
|
297
|
+
* lockWaitMs`), and the unchanged-tree path normally returns after the much
|
|
298
|
+
* shorter `staleProbeMs` observation window.
|
|
289
299
|
*/
|
|
290
300
|
export declare function waitForInstallTreeSettled(args: WaitForInstallTreeSettledArgs): Promise<WaitForInstallTreeSettledResult>;
|
|
291
301
|
export interface InstallRewriteSignalArgs {
|
|
@@ -310,7 +320,8 @@ export interface InstallRewriteSignalArgs {
|
|
|
310
320
|
*/
|
|
311
321
|
export declare function installRewriteInProgress(args: InstallRewriteSignalArgs): boolean;
|
|
312
322
|
/** The recovery OUTCOME, which — not the dialect — decides capture. */
|
|
313
|
-
export type InstallTreeTornOutcome = "guarded" | "unsettled" | "reexec-failed" | "lock-held";
|
|
323
|
+
export type InstallTreeTornOutcome = "guarded" | "unsettled" | "reexec-failed" | "lock-held" | "stale_tree";
|
|
324
|
+
export type InstallTreeState = "in_flight" | "stale";
|
|
314
325
|
export type InstallTreeTornDiagnostics = {
|
|
315
326
|
dialect: ModuleErrorDialect;
|
|
316
327
|
code: ModuleNotFoundCode;
|
|
@@ -318,10 +329,12 @@ export type InstallTreeTornDiagnostics = {
|
|
|
318
329
|
importer: string;
|
|
319
330
|
packageRoot: string;
|
|
320
331
|
outcome: InstallTreeTornOutcome;
|
|
332
|
+
treeState: InstallTreeState;
|
|
321
333
|
attempt: 1 | 2;
|
|
322
334
|
waitedMs: number;
|
|
323
335
|
sawLock: boolean;
|
|
324
336
|
sawRetired: boolean;
|
|
337
|
+
sawTreeChange: boolean;
|
|
325
338
|
/** A fresh foreign lock was still held when the wait gave up (→ 'lock-held'). */
|
|
326
339
|
lockHeldAtEnd: boolean;
|
|
327
340
|
/** Cumulative wall time the wait spent under a fresh foreign lock. */
|
|
@@ -342,6 +355,8 @@ export interface InstallTreeTornInit {
|
|
|
342
355
|
waitedMs: number;
|
|
343
356
|
sawLock: boolean;
|
|
344
357
|
sawRetired: boolean;
|
|
358
|
+
sawTreeChange?: boolean;
|
|
359
|
+
treeState?: InstallTreeState;
|
|
345
360
|
/** Whether a fresh foreign lock was held when the wait gave up (default false). */
|
|
346
361
|
lockHeldAtEnd?: boolean;
|
|
347
362
|
/** Cumulative time under a fresh foreign lock (default 0). */
|
|
@@ -362,11 +377,13 @@ export declare class InstallTreeTornError extends Error {
|
|
|
362
377
|
}
|
|
363
378
|
export declare function isInstallTreeTornError(err: unknown): err is InstallTreeTornError;
|
|
364
379
|
/** The exact Sentry `contexts` payload for a captured torn-install failure. */
|
|
365
|
-
export declare function installTreeTornCaptureContext(err: InstallTreeTornError): {
|
|
380
|
+
export declare function installTreeTornCaptureContext(err: InstallTreeTornError, homeDirectory?: string): {
|
|
366
381
|
contexts: {
|
|
367
382
|
install_tree_torn: InstallTreeTornDiagnostics;
|
|
368
383
|
};
|
|
369
384
|
};
|
|
385
|
+
/** Replace a home-directory prefix without matching similarly-named sibling folders. */
|
|
386
|
+
export declare function scrubHomeDirectoryPrefix(value: string, homeDirectory?: string): string;
|
|
370
387
|
/**
|
|
371
388
|
* The fixed, input-free operator remedy. It names the likely writers (so a
|
|
372
389
|
* human knows this was not their command's fault) and the reinstall commands,
|
|
@@ -374,6 +391,8 @@ export declare function installTreeTornCaptureContext(err: InstallTreeTornError)
|
|
|
374
391
|
* appended by {@link installTreeTornStderrLine}.
|
|
375
392
|
*/
|
|
376
393
|
export declare const INSTALL_TREE_TORN_REMEDY: string;
|
|
394
|
+
/** Fixed repair guidance for an unchanged tree with no active updater signal. */
|
|
395
|
+
export declare const INSTALL_TREE_STALE_REMEDY: string;
|
|
377
396
|
/**
|
|
378
397
|
* The fixed, input-free remedy for the 'lock-held' outcome: a cooperating writer
|
|
379
398
|
* held the shared update lock past its own staleness ceiling. Telling the user to
|