@indigoai-us/hq-cli 5.309.0 → 5.309.1
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 +13 -0
- package/dist/command-catalog.generated.d.ts +4 -1
- package/dist/command-catalog.generated.js +5 -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-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/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
|
@@ -76,6 +76,10 @@ export class QmdExitError extends Error {
|
|
|
76
76
|
stdout;
|
|
77
77
|
stderr;
|
|
78
78
|
name = 'QmdExitError';
|
|
79
|
+
/** Safe route labels for telemetry; raw qmd streams remain local to the error. */
|
|
80
|
+
entryPath;
|
|
81
|
+
/** Present only when the invoked executable is the bundled qmd. */
|
|
82
|
+
qmdVersion;
|
|
79
83
|
constructor(message, args, status, stdout, stderr) {
|
|
80
84
|
super(message);
|
|
81
85
|
this.args = args;
|
|
@@ -1239,6 +1243,21 @@ function finishRunQmd(result, bin, args, attribution) {
|
|
|
1239
1243
|
}
|
|
1240
1244
|
throw exitError;
|
|
1241
1245
|
}
|
|
1246
|
+
function finishRunQmdWithDiagnostics(result, bin, args, options, attribution) {
|
|
1247
|
+
try {
|
|
1248
|
+
return finishRunQmd(result, bin, args, attribution);
|
|
1249
|
+
}
|
|
1250
|
+
catch (error) {
|
|
1251
|
+
if (error instanceof QmdExitError) {
|
|
1252
|
+
if (args[0] === 'update' && options.entryPath)
|
|
1253
|
+
error.entryPath = options.entryPath;
|
|
1254
|
+
const bundledBins = [packageLocalBin(), packageLocalNodeEntry()].filter((candidate) => Boolean(candidate));
|
|
1255
|
+
if (bundledBins.includes(bin))
|
|
1256
|
+
error.qmdVersion = resolveQmdVersion();
|
|
1257
|
+
}
|
|
1258
|
+
throw error;
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1242
1261
|
/** Run qmd with captured output and typed failures. */
|
|
1243
1262
|
export function runQmd(args, options = {}) {
|
|
1244
1263
|
// Legacy string-bin runner seam (tests and callers that inject a fake process):
|
|
@@ -1246,7 +1265,7 @@ export function runQmd(args, options = {}) {
|
|
|
1246
1265
|
if (options.runner) {
|
|
1247
1266
|
const bin = options.bin ?? resolveQmdBin({ env: options.env });
|
|
1248
1267
|
const result = options.runner(bin, args, { cwd: options.cwd, env: options.env });
|
|
1249
|
-
return
|
|
1268
|
+
return finishRunQmdWithDiagnostics(result, bin, args, options);
|
|
1250
1269
|
}
|
|
1251
1270
|
// Default path: resolve the full invocation (shell-free Node launcher preferred)
|
|
1252
1271
|
// and spawn it through the shared Windows-aware plan. An explicit string bin is
|
|
@@ -1279,7 +1298,7 @@ export function runQmd(args, options = {}) {
|
|
|
1279
1298
|
// genuinely missing qmd — instead of blaming the launcher for all three
|
|
1280
1299
|
// (HQ-CLI-1A). `command` is the process actually spawned; `launcher` is qmd's
|
|
1281
1300
|
// file identity.
|
|
1282
|
-
return
|
|
1301
|
+
return finishRunQmdWithDiagnostics(result, launcher, args, options, {
|
|
1283
1302
|
command: invocation.command,
|
|
1284
1303
|
launcher,
|
|
1285
1304
|
cwd: options.cwd,
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { QmdExitError } from "./index.js";
|
|
2
|
+
const QMD_ERROR_CODE_TOKENS = [
|
|
3
|
+
"SQLITE_BUSY",
|
|
4
|
+
"SQLITE_CORRUPT",
|
|
5
|
+
"SQLITE_FULL",
|
|
6
|
+
"ENOSPC",
|
|
7
|
+
"EACCES",
|
|
8
|
+
"EPERM",
|
|
9
|
+
"ENOENT",
|
|
10
|
+
"ECONNRESET",
|
|
11
|
+
"ETIMEDOUT",
|
|
12
|
+
];
|
|
13
|
+
const QMD_UPDATE_ENTRY_PATHS = new Set(["index-sync"]);
|
|
14
|
+
/** Bounded Sentry tags for qmd exits; never return captured streams or full argv. */
|
|
15
|
+
export function qmdExitCaptureContext(err) {
|
|
16
|
+
if (!(err instanceof QmdExitError))
|
|
17
|
+
return undefined;
|
|
18
|
+
const output = err.stderr + "\n" + err.stdout;
|
|
19
|
+
const errorCode = QMD_ERROR_CODE_TOKENS.find((token) => new RegExp("(^|[^A-Za-z0-9_])" + token + "(?=$|[^A-Za-z0-9_])").test(output)) ?? "none";
|
|
20
|
+
const firstArg = err.args[0];
|
|
21
|
+
const subcommand = typeof firstArg === "string" && /^[a-z][a-z0-9_-]{0,31}$/.test(firstArg)
|
|
22
|
+
? firstArg
|
|
23
|
+
: "unknown";
|
|
24
|
+
const exitStatus = typeof err.status === "number" && Number.isSafeInteger(err.status)
|
|
25
|
+
? String(err.status)
|
|
26
|
+
: "unknown";
|
|
27
|
+
const tags = {
|
|
28
|
+
qmd_error_code: errorCode,
|
|
29
|
+
qmd_subcommand: subcommand,
|
|
30
|
+
qmd_exit_status: exitStatus,
|
|
31
|
+
};
|
|
32
|
+
if (typeof err.qmdVersion === "string" && /^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/.test(err.qmdVersion)) {
|
|
33
|
+
tags.qmd_version = err.qmdVersion;
|
|
34
|
+
}
|
|
35
|
+
if (typeof err.entryPath === "string" && QMD_UPDATE_ENTRY_PATHS.has(err.entryPath)) {
|
|
36
|
+
tags.qmd_entry_path = err.entryPath;
|
|
37
|
+
}
|
|
38
|
+
return { tags };
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=qmd-exit-diagnostics.js.map
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
* touch launchctl.
|
|
8
8
|
*/
|
|
9
9
|
import { type ServiceHostPaths, type ServiceSpec } from "./types.js";
|
|
10
|
-
export declare const LAUNCHD_PATH_ENV = "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin";
|
|
11
10
|
export declare function launchAgentsDir(home: string): string;
|
|
12
11
|
export declare function launchdPlistPath(home: string, spec: Pick<ServiceSpec, "label">): string;
|
|
13
12
|
export declare function renderLaunchdPlist(spec: ServiceSpec, host: ServiceHostPaths): string;
|
|
@@ -27,12 +26,6 @@ export declare const LAUNCHD_BOOTSTRAP_BACKOFF_MS: readonly number[];
|
|
|
27
26
|
export declare const LAUNCHD_BOOTSTRAP_ATTEMPTS: number;
|
|
28
27
|
export declare const defaultRunCommand: RunCommand;
|
|
29
28
|
export declare function launchdServiceTarget(plistPath: string, uid?: number): string;
|
|
30
|
-
/** launchd returns EIO (5) when bootstrap races the previous job's exit. */
|
|
31
|
-
export declare function isRetryableLaunchdBootstrap(r: {
|
|
32
|
-
status: number | null;
|
|
33
|
-
stdout: string;
|
|
34
|
-
stderr: string;
|
|
35
|
-
}): boolean;
|
|
36
29
|
/** ProgramArguments from a LaunchAgent plist. */
|
|
37
30
|
export declare function launchdPlistProgramArguments(plist: string): string[];
|
|
38
31
|
/**
|
|
@@ -10,7 +10,7 @@ import { spawnSync } from "node:child_process";
|
|
|
10
10
|
import * as os from "node:os";
|
|
11
11
|
import * as path from "node:path";
|
|
12
12
|
import { escapeXml, } from "./types.js";
|
|
13
|
-
|
|
13
|
+
const LAUNCHD_PATH_ENV = "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin";
|
|
14
14
|
export function launchAgentsDir(home) {
|
|
15
15
|
return path.join(home, "Library", "LaunchAgents");
|
|
16
16
|
}
|
|
@@ -89,7 +89,7 @@ function bootstrapErrorText(r) {
|
|
|
89
89
|
return `${r.stderr} ${r.stdout} exit ${r.status ?? ""}`;
|
|
90
90
|
}
|
|
91
91
|
/** launchd returns EIO (5) when bootstrap races the previous job's exit. */
|
|
92
|
-
|
|
92
|
+
function isRetryableLaunchdBootstrap(r) {
|
|
93
93
|
if (r.status === 0)
|
|
94
94
|
return false;
|
|
95
95
|
return /Input\/output error|\bEIO\b|Bootstrap failed: 5|Boot-strap failed: 5/i.test(bootstrapErrorText(r));
|
package/dist/main.js
CHANGED
|
@@ -24,6 +24,7 @@ import { qmdStoreUnopenableMessage } from "./utils/qmd-store-unopenable-error.js
|
|
|
24
24
|
import { qmdQueryDocumentMessage } from "./utils/qmd-query-document-error.js";
|
|
25
25
|
import { qmdModelDownloadMessage } from "./utils/qmd-model-download-error.js";
|
|
26
26
|
import { qmdWorkdirMissingMessage } from "./utils/qmd-workdir-missing-error.js";
|
|
27
|
+
import { qmdExitCaptureContext } from "./lib/search-index/qmd-exit-diagnostics.js";
|
|
27
28
|
import { hqStateWriteErrorMessage } from "./utils/hq-state-write-error.js";
|
|
28
29
|
import { checkpointStampWriteCaptureContext, checkpointStampWriteMessage, } from "./utils/checkpoint-stamp-write-error.js";
|
|
29
30
|
import { incompleteInstallMessage, incompleteInstallCaptureContext, } from "./utils/incomplete-install-error.js";
|
|
@@ -872,9 +873,13 @@ export async function handleTopLevelError(err, deps = defaultTopLevelErrorDepend
|
|
|
872
873
|
// Every other error captures bare, exactly as before.
|
|
873
874
|
const spawnContext = qmdSpawnFailureCaptureContext(err);
|
|
874
875
|
const installContext = incompleteInstallCaptureContext(err);
|
|
875
|
-
|
|
876
|
+
const qmdContext = qmdExitCaptureContext(err);
|
|
877
|
+
if (spawnContext || installContext || qmdContext) {
|
|
876
878
|
deps.sentry.captureException(err, {
|
|
877
|
-
|
|
879
|
+
...qmdContext,
|
|
880
|
+
...((spawnContext || installContext)
|
|
881
|
+
? { contexts: { ...spawnContext?.contexts, ...installContext } }
|
|
882
|
+
: {}),
|
|
878
883
|
});
|
|
879
884
|
}
|
|
880
885
|
else {
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* spawns, waits, or a real install tree.
|
|
18
18
|
*/
|
|
19
19
|
import { spawnSync } from "node:child_process";
|
|
20
|
-
import { resolveRunningInstall } from "./utils/version-gate.js";
|
|
20
|
+
import { getVersionGatePhase, resolveRunningInstall } from "./utils/version-gate.js";
|
|
21
21
|
import { stringDerivedPackageRoot } from "./utils/hq-roots.js";
|
|
22
22
|
import { updateLockPath, UPDATE_LOCK_STALE_MS } from "./utils/update-lock.js";
|
|
23
23
|
import { classifyEvalThrow, classifyModuleNotFound, installRewriteInProgress, InstallTreeTornError, waitForInstallTreeSettled, } from "./utils/install-tree-torn.js";
|
|
@@ -122,6 +122,7 @@ export async function registerCommandsWithRecovery(args) {
|
|
|
122
122
|
cause: err,
|
|
123
123
|
classified,
|
|
124
124
|
packageRoot,
|
|
125
|
+
versionGatePhase: getVersionGatePhase(),
|
|
125
126
|
outcome: "guarded",
|
|
126
127
|
treeState: env[INSTALL_TREE_RECOVERY_TREE_STATE_ENV] === "stale" ? "stale" : "in_flight",
|
|
127
128
|
attempt: 2,
|
|
@@ -165,6 +166,9 @@ export async function registerCommandsWithRecovery(args) {
|
|
|
165
166
|
sawLock: result.sawLock,
|
|
166
167
|
sawRetired: result.sawRetired,
|
|
167
168
|
retiredSiblingPresent: result.retiredSiblingPresent,
|
|
169
|
+
retiredSiblingKind: result.retiredSiblingKind,
|
|
170
|
+
installTargetState: result.installTargetState,
|
|
171
|
+
updaterPidAlive: result.updaterPidAlive,
|
|
168
172
|
sawTreeChange: result.sawTreeChange,
|
|
169
173
|
lockHeldAtEnd: result.lockHeldAtEnd,
|
|
170
174
|
lockedMs: result.lockedMs,
|
|
@@ -189,6 +193,7 @@ export async function registerCommandsWithRecovery(args) {
|
|
|
189
193
|
cause: err,
|
|
190
194
|
classified,
|
|
191
195
|
packageRoot,
|
|
196
|
+
versionGatePhase: getVersionGatePhase(),
|
|
192
197
|
outcome: "reexec-failed",
|
|
193
198
|
treeState,
|
|
194
199
|
attempt: 1,
|
|
@@ -196,6 +201,9 @@ export async function registerCommandsWithRecovery(args) {
|
|
|
196
201
|
sawLock: result.sawLock,
|
|
197
202
|
sawRetired: result.sawRetired,
|
|
198
203
|
retiredSiblingPresent: result.retiredSiblingPresent,
|
|
204
|
+
retiredSiblingKind: result.retiredSiblingKind,
|
|
205
|
+
installTargetState: result.installTargetState,
|
|
206
|
+
updaterPidAlive: result.updaterPidAlive,
|
|
199
207
|
sawTreeChange: result.sawTreeChange,
|
|
200
208
|
lockTool: result.lockTool,
|
|
201
209
|
});
|
|
@@ -33,8 +33,6 @@
|
|
|
33
33
|
import { isExpiring, loadCachedTokens } from "./cognito-session.js";
|
|
34
34
|
import { type VersionInfo } from "./feedback-versions.js";
|
|
35
35
|
import { type ClientHealthArch, type ClientHealthHeartbeat, type ClientHealthLocalFilesOverview, type ClientHealthPlatform } from "./client-health-contract.js";
|
|
36
|
-
/** Same bound as CLI telemetry: a heartbeat may never stall a command. */
|
|
37
|
-
export declare const CLIENT_HEALTH_TIMEOUT_MS = 1200;
|
|
38
36
|
export declare const CLIENT_HEALTH_STATE_FILE = "cli-client-health.json";
|
|
39
37
|
/**
|
|
40
38
|
* Single-flight marker for the journal observation below. Held only for the
|
|
@@ -66,8 +64,6 @@ export declare const CLIENT_HEALTH_OBSERVATION_FILE = "cli-client-health.observa
|
|
|
66
64
|
* always re-observe — they have opened the store anyway, so it costs nothing.
|
|
67
65
|
*/
|
|
68
66
|
export declare const JOURNAL_OBSERVATION_TTL_MS: number;
|
|
69
|
-
/** An observation lock older than this is treated as abandoned. */
|
|
70
|
-
export declare const JOURNAL_OBSERVATION_LOCK_STALE_MS: number;
|
|
71
67
|
export interface CliClientHealthState {
|
|
72
68
|
/** Stable random installation identity — regenerated only if invalid. */
|
|
73
69
|
installationId: string;
|
|
@@ -40,7 +40,7 @@ import { collectVersions } from "./feedback-versions.js";
|
|
|
40
40
|
import { CLIENT_HEALTH_CONTRACT_VERSION, parseClientHealthHeartbeat, } from "./client-health-contract.js";
|
|
41
41
|
import { collectLocalFilesOverview } from "./local-files-overview.js";
|
|
42
42
|
/** Same bound as CLI telemetry: a heartbeat may never stall a command. */
|
|
43
|
-
|
|
43
|
+
const CLIENT_HEALTH_TIMEOUT_MS = 1_200;
|
|
44
44
|
export const CLIENT_HEALTH_STATE_FILE = "cli-client-health.json";
|
|
45
45
|
/**
|
|
46
46
|
* Single-flight marker for the journal observation below. Held only for the
|
|
@@ -73,7 +73,7 @@ export const CLIENT_HEALTH_OBSERVATION_FILE = "cli-client-health.observation.jso
|
|
|
73
73
|
*/
|
|
74
74
|
export const JOURNAL_OBSERVATION_TTL_MS = 15 * 60_000;
|
|
75
75
|
/** An observation lock older than this is treated as abandoned. */
|
|
76
|
-
|
|
76
|
+
const JOURNAL_OBSERVATION_LOCK_STALE_MS = 5 * 60_000;
|
|
77
77
|
/**
|
|
78
78
|
* Pre-filter mirror of the contract's SemVer shape (the contract keeps its
|
|
79
79
|
* validators private). A version failing this is DROPPED individually so one
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type VersionInfo } from "./feedback-versions.js";
|
|
2
2
|
import type { FeedbackLogsBlob } from "./feedback-logs.js";
|
|
3
|
-
|
|
3
|
+
interface GitContext {
|
|
4
4
|
branch: string | null;
|
|
5
5
|
head: string | null;
|
|
6
6
|
dirty: boolean;
|
|
@@ -37,4 +37,5 @@ export interface DiagnosticsBlob {
|
|
|
37
37
|
}
|
|
38
38
|
export declare function sanitizeArgv(argv: string[]): string[];
|
|
39
39
|
export declare function collectDiagnostics(): DiagnosticsBlob;
|
|
40
|
+
export {};
|
|
40
41
|
//# sourceMappingURL=feedback-diagnostics.d.ts.map
|
|
@@ -61,29 +61,6 @@ import { vaultApiFetch } from "./vault-api.js";
|
|
|
61
61
|
* stay in step or the CLI will build bundles the server will not accept.
|
|
62
62
|
*/
|
|
63
63
|
export declare const LOG_BUNDLE_MAX_BYTES: number;
|
|
64
|
-
/**
|
|
65
|
-
* What the presign request declares, and what the server must answer with.
|
|
66
|
-
* Mirrors `LOG_BUNDLE_FORMAT_ZIP` / `LOG_BUNDLE_ZIP_CONTENT_TYPE` in the hq-pro
|
|
67
|
-
* handler `feedback-log-bundles.ts`.
|
|
68
|
-
*/
|
|
69
|
-
export declare const LOG_BUNDLE_FORMAT = "zip";
|
|
70
|
-
export declare const LOG_BUNDLE_CONTENT_TYPE = "application/zip";
|
|
71
|
-
/**
|
|
72
|
-
* Headroom between the size we stop feeding at and the hard cap.
|
|
73
|
-
*
|
|
74
|
-
* deflate reports compressed bytes only as its internal buffer flushes, so the
|
|
75
|
-
* running total lags the bytes actually consumed. The lag is bounded by that
|
|
76
|
-
* buffer (tens of KiB); a 1 MiB margin covers it with three orders of magnitude
|
|
77
|
-
* to spare, and the final size is asserted against the real cap regardless.
|
|
78
|
-
*/
|
|
79
|
-
export declare const LOG_BUNDLE_SAFETY_MARGIN_BYTES: number;
|
|
80
|
-
/**
|
|
81
|
-
* Per-file raw ceiling. The desktop logger rotates at 32 MiB (hq-desktop-core
|
|
82
|
-
* `logfile.rs`), so this admits a full generation with headroom while stopping
|
|
83
|
-
* one pathological file from consuming the whole bundle. Files above it are
|
|
84
|
-
* read from the TAIL — the end of a log is what explains a failure.
|
|
85
|
-
*/
|
|
86
|
-
export declare const LOG_BUNDLE_MAX_FILE_RAW_BYTES: number;
|
|
87
64
|
export interface LogBundleResult {
|
|
88
65
|
/** The zipped NDJSON bytes, ready to PUT. */
|
|
89
66
|
bytes: Buffer;
|
|
@@ -150,8 +127,6 @@ export declare const ZIP32_UNCOMPRESSED_STOP_BYTES: number;
|
|
|
150
127
|
*/
|
|
151
128
|
export declare function entryCeiling(requested?: number): number;
|
|
152
129
|
export declare function crc32Fallback(buf: Buffer, seed?: number): number;
|
|
153
|
-
/** Exported for the test that proves the fallback agrees with the native one. */
|
|
154
|
-
export declare function crc32(buf: Buffer, seed?: number): number;
|
|
155
130
|
/**
|
|
156
131
|
* Bytes of ZIP framing around one entry: local header, central directory
|
|
157
132
|
* header, end-of-central-directory. The name is stored twice.
|
|
@@ -71,8 +71,8 @@ export const LOG_BUNDLE_MAX_BYTES = 50 * 1024 * 1024;
|
|
|
71
71
|
* Mirrors `LOG_BUNDLE_FORMAT_ZIP` / `LOG_BUNDLE_ZIP_CONTENT_TYPE` in the hq-pro
|
|
72
72
|
* handler `feedback-log-bundles.ts`.
|
|
73
73
|
*/
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
const LOG_BUNDLE_FORMAT = "zip";
|
|
75
|
+
const LOG_BUNDLE_CONTENT_TYPE = "application/zip";
|
|
76
76
|
/**
|
|
77
77
|
* Headroom between the size we stop feeding at and the hard cap.
|
|
78
78
|
*
|
|
@@ -81,14 +81,14 @@ export const LOG_BUNDLE_CONTENT_TYPE = "application/zip";
|
|
|
81
81
|
* buffer (tens of KiB); a 1 MiB margin covers it with three orders of magnitude
|
|
82
82
|
* to spare, and the final size is asserted against the real cap regardless.
|
|
83
83
|
*/
|
|
84
|
-
|
|
84
|
+
const LOG_BUNDLE_SAFETY_MARGIN_BYTES = 1024 * 1024;
|
|
85
85
|
/**
|
|
86
86
|
* Per-file raw ceiling. The desktop logger rotates at 32 MiB (hq-desktop-core
|
|
87
87
|
* `logfile.rs`), so this admits a full generation with headroom while stopping
|
|
88
88
|
* one pathological file from consuming the whole bundle. Files above it are
|
|
89
89
|
* read from the TAIL — the end of a log is what explains a failure.
|
|
90
90
|
*/
|
|
91
|
-
|
|
91
|
+
const LOG_BUNDLE_MAX_FILE_RAW_BYTES = 64 * 1024 * 1024;
|
|
92
92
|
/** Read slice size. Bounds peak memory; unrelated to the output chunking. */
|
|
93
93
|
const READ_SLICE_BYTES = 4 * 1024 * 1024;
|
|
94
94
|
/** Target raw text per emitted chunk record. Keeps NDJSON lines manageable. */
|
|
@@ -217,7 +217,7 @@ export function crc32Fallback(buf, seed = 0) {
|
|
|
217
217
|
return (~crc >>> 0) >>> 0;
|
|
218
218
|
}
|
|
219
219
|
/** Exported for the test that proves the fallback agrees with the native one. */
|
|
220
|
-
|
|
220
|
+
function crc32(buf, seed = 0) {
|
|
221
221
|
const native = zlib.crc32;
|
|
222
222
|
return typeof native === "function" ? native(buf, seed) : crc32Fallback(buf, seed);
|
|
223
223
|
}
|
|
@@ -84,13 +84,6 @@ export declare const FEEDBACK_LOGS_MIN_USEFUL_BYTES = 1024;
|
|
|
84
84
|
* evidence. The overflow count preserves the signal without the bytes.
|
|
85
85
|
*/
|
|
86
86
|
export declare const FEEDBACK_MAX_SKIPPED_ENTRIES = 25;
|
|
87
|
-
/** Subdirectory of `~/.hq` whose log files are eligible. Not recursive. */
|
|
88
|
-
export declare const FEEDBACK_LOGS_DIRNAME = "logs";
|
|
89
|
-
/**
|
|
90
|
-
* Eligible extensions inside `~/.hq/logs/`. `.jsonl` is included because the
|
|
91
|
-
* CLI's MCP registry audit trail is JSON-lines, not plain text.
|
|
92
|
-
*/
|
|
93
|
-
export declare const FEEDBACK_LOG_EXTENSIONS: readonly string[];
|
|
94
87
|
/**
|
|
95
88
|
* True for a log filename, including a ROTATED generation.
|
|
96
89
|
*
|
|
@@ -103,26 +96,6 @@ export declare const FEEDBACK_LOG_EXTENSIONS: readonly string[];
|
|
|
103
96
|
export declare function isEligibleLogName(name: string): boolean;
|
|
104
97
|
/** Per-state-file ceiling. These are status documents, not streams. */
|
|
105
98
|
export declare const FEEDBACK_STATE_MAX_FILE_BYTES: number;
|
|
106
|
-
/**
|
|
107
|
-
* Ceiling on all state snapshots combined. Deliberately a small slice of the
|
|
108
|
-
* overall budget: state is dense but finite, and the rest belongs to logs.
|
|
109
|
-
*/
|
|
110
|
-
export declare const FEEDBACK_STATE_MAX_TOTAL_BYTES: number;
|
|
111
|
-
/**
|
|
112
|
-
* State files at the `~/.hq` ROOT that are eligible, by EXACT name.
|
|
113
|
-
*
|
|
114
|
-
* The root is where `cognito-tokens.json`, `deploy-passwords.json`, and
|
|
115
|
-
* `secrets-cache/` live, so it is never globbed. Adding a name here is a
|
|
116
|
-
* deliberate act: confirm the file carries no credential before listing it.
|
|
117
|
-
*/
|
|
118
|
-
export declare const FEEDBACK_STATE_FILENAMES: readonly string[];
|
|
119
|
-
/**
|
|
120
|
-
* Per-company sync journal LOCATORS (`sync-journal.<slug>.json`) are also
|
|
121
|
-
* eligible. Matched by an exact prefix AND a `.json` suffix, which is what
|
|
122
|
-
* excludes the multi-hundred-megabyte `…json.last-good` snapshots sitting
|
|
123
|
-
* beside them. No credential file at the root carries this prefix.
|
|
124
|
-
*/
|
|
125
|
-
export declare const FEEDBACK_STATE_JOURNAL_PREFIX = "sync-journal.";
|
|
126
99
|
/**
|
|
127
100
|
* `~/.hq/locks/` — held operation locks and background-work claims.
|
|
128
101
|
*
|
|
@@ -141,8 +114,6 @@ export declare const FEEDBACK_LOCKS_DIRNAME = "locks";
|
|
|
141
114
|
* bounds that walk carries.
|
|
142
115
|
*/
|
|
143
116
|
export declare const FEEDBACK_JOBS_DIRNAME = "jobs";
|
|
144
|
-
/** Per-entry ceiling for a lock file. Their value is the name plus a little context. */
|
|
145
|
-
export declare const FEEDBACK_LOCK_MAX_FILE_BYTES = 512;
|
|
146
117
|
/**
|
|
147
118
|
* Bounds on the `jobs/` walk. Depth 3 reaches
|
|
148
119
|
* `jobs/probes/<job>/last-attempt.json`; the file cap stops a pathological
|
|
@@ -152,15 +123,9 @@ export declare const FEEDBACK_JOBS_MAX_DEPTH = 3;
|
|
|
152
123
|
export declare const FEEDBACK_JOBS_MAX_FILES = 20;
|
|
153
124
|
/** Ceiling on how many lock entries are collected. */
|
|
154
125
|
export declare const FEEDBACK_LOCKS_MAX_FILES = 20;
|
|
155
|
-
/**
|
|
156
|
-
* Log files that live at the root of `~/.hq` rather than under `logs/`.
|
|
157
|
-
* Listed explicitly — the root directory holds credentials and must never be
|
|
158
|
-
* enumerated by pattern.
|
|
159
|
-
*/
|
|
160
|
-
export declare const FEEDBACK_ROOT_LOG_FILENAMES: readonly string[];
|
|
161
126
|
/** Why a discovered log file contributed nothing to the submission. */
|
|
162
|
-
|
|
163
|
-
|
|
127
|
+
type SkipReason = "budget" | "empty" | "unreadable";
|
|
128
|
+
interface LogTail {
|
|
164
129
|
/** Path relative to `~/.hq`, e.g. `logs/hq-sync.log`. Never absolute. */
|
|
165
130
|
name: string;
|
|
166
131
|
/** Full on-disk size, even when only a tail was included. */
|
|
@@ -177,7 +142,7 @@ export interface LogTail {
|
|
|
177
142
|
tail: string;
|
|
178
143
|
}
|
|
179
144
|
/** A small JSON status document included whole (or head-truncated). */
|
|
180
|
-
|
|
145
|
+
interface StateSnapshot {
|
|
181
146
|
/** Path relative to `~/.hq`, e.g. `sync-progress.json`. Never absolute. */
|
|
182
147
|
name: string;
|
|
183
148
|
sizeBytes: number;
|
|
@@ -331,4 +296,5 @@ export interface CollectFeedbackLogsOptions {
|
|
|
331
296
|
* omit the field entirely rather than attach an empty object. Never throws.
|
|
332
297
|
*/
|
|
333
298
|
export declare function collectFeedbackLogs(opts?: CollectFeedbackLogsOptions): FeedbackLogsBlob | undefined;
|
|
299
|
+
export {};
|
|
334
300
|
//# sourceMappingURL=feedback-logs.d.ts.map
|
|
@@ -89,12 +89,12 @@ export const FEEDBACK_LOGS_MIN_USEFUL_BYTES = 1024;
|
|
|
89
89
|
*/
|
|
90
90
|
export const FEEDBACK_MAX_SKIPPED_ENTRIES = 25;
|
|
91
91
|
/** Subdirectory of `~/.hq` whose log files are eligible. Not recursive. */
|
|
92
|
-
|
|
92
|
+
const FEEDBACK_LOGS_DIRNAME = "logs";
|
|
93
93
|
/**
|
|
94
94
|
* Eligible extensions inside `~/.hq/logs/`. `.jsonl` is included because the
|
|
95
95
|
* CLI's MCP registry audit trail is JSON-lines, not plain text.
|
|
96
96
|
*/
|
|
97
|
-
|
|
97
|
+
const FEEDBACK_LOG_EXTENSIONS = [".log", ".jsonl"];
|
|
98
98
|
/**
|
|
99
99
|
* True for a log filename, including a ROTATED generation.
|
|
100
100
|
*
|
|
@@ -120,7 +120,7 @@ export const FEEDBACK_STATE_MAX_FILE_BYTES = 4 * 1024;
|
|
|
120
120
|
* Ceiling on all state snapshots combined. Deliberately a small slice of the
|
|
121
121
|
* overall budget: state is dense but finite, and the rest belongs to logs.
|
|
122
122
|
*/
|
|
123
|
-
|
|
123
|
+
const FEEDBACK_STATE_MAX_TOTAL_BYTES = 12 * 1024;
|
|
124
124
|
/**
|
|
125
125
|
* State files at the `~/.hq` ROOT that are eligible, by EXACT name.
|
|
126
126
|
*
|
|
@@ -128,7 +128,7 @@ export const FEEDBACK_STATE_MAX_TOTAL_BYTES = 12 * 1024;
|
|
|
128
128
|
* `secrets-cache/` live, so it is never globbed. Adding a name here is a
|
|
129
129
|
* deliberate act: confirm the file carries no credential before listing it.
|
|
130
130
|
*/
|
|
131
|
-
|
|
131
|
+
const FEEDBACK_STATE_FILENAMES = [
|
|
132
132
|
// In-flight sync: phase, file counts, conflict count, current file.
|
|
133
133
|
"sync-progress.json",
|
|
134
134
|
// Sync contract version the installation last wrote.
|
|
@@ -150,7 +150,7 @@ export const FEEDBACK_STATE_FILENAMES = [
|
|
|
150
150
|
* excludes the multi-hundred-megabyte `…json.last-good` snapshots sitting
|
|
151
151
|
* beside them. No credential file at the root carries this prefix.
|
|
152
152
|
*/
|
|
153
|
-
|
|
153
|
+
const FEEDBACK_STATE_JOURNAL_PREFIX = "sync-journal.";
|
|
154
154
|
/**
|
|
155
155
|
* `~/.hq/locks/` — held operation locks and background-work claims.
|
|
156
156
|
*
|
|
@@ -170,7 +170,7 @@ export const FEEDBACK_LOCKS_DIRNAME = "locks";
|
|
|
170
170
|
*/
|
|
171
171
|
export const FEEDBACK_JOBS_DIRNAME = "jobs";
|
|
172
172
|
/** Per-entry ceiling for a lock file. Their value is the name plus a little context. */
|
|
173
|
-
|
|
173
|
+
const FEEDBACK_LOCK_MAX_FILE_BYTES = 512;
|
|
174
174
|
/**
|
|
175
175
|
* Bounds on the `jobs/` walk. Depth 3 reaches
|
|
176
176
|
* `jobs/probes/<job>/last-attempt.json`; the file cap stops a pathological
|
|
@@ -185,7 +185,7 @@ export const FEEDBACK_LOCKS_MAX_FILES = 20;
|
|
|
185
185
|
* Listed explicitly — the root directory holds credentials and must never be
|
|
186
186
|
* enumerated by pattern.
|
|
187
187
|
*/
|
|
188
|
-
|
|
188
|
+
const FEEDBACK_ROOT_LOG_FILENAMES = [
|
|
189
189
|
"boot-sync.log",
|
|
190
190
|
"boot-capture.log",
|
|
191
191
|
];
|
|
@@ -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
|