@indigoai-us/hq-cli 5.303.0 → 5.305.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/dist/command-catalog.generated.d.ts +1 -1
- package/dist/command-catalog.generated.js +3 -1
- package/dist/command-registration-plan.js +8 -0
- package/dist/commands/lanes.d.ts +2 -0
- package/dist/commands/lanes.js +34 -6
- package/dist/commands/mcp-status.js +1 -0
- package/dist/commands/members.js +0 -1
- package/dist/commands/people.d.ts +3 -0
- package/dist/commands/people.js +27 -3
- package/dist/index.js +6 -1
- package/dist/lazy-commands.js +5 -2
- package/dist/lib/aws-sdk-node-warning-output.d.ts +12 -0
- package/dist/lib/aws-sdk-node-warning-output.js +22 -0
- package/dist/lib/company-errors-sdk-warning-flag.d.ts +5 -0
- package/dist/lib/company-errors-sdk-warning-flag.js +8 -0
- package/dist/lib/doctor/checks/work-context.js +2 -2
- package/dist/lib/doctor/deep/executor.d.ts +4 -10
- package/dist/lib/doctor/deep/executor.js +1 -1
- package/dist/lib/doctor/deep/parity.d.ts +2 -29
- package/dist/lib/doctor/deep/parity.js +7 -7
- package/dist/lib/doctor/deep/sandbox.d.ts +7 -25
- package/dist/lib/doctor/deep/sandbox.js +4 -4
- package/dist/lib/lanes/keep-alive.js +13 -2
- package/dist/lib/mesh/live/daemon/doctor.js +2 -2
- package/dist/lib/mesh/live/flush.d.ts +2 -2
- package/dist/lib/mesh/live/flush.js +49 -29
- package/dist/lib/mesh/live/spool.d.ts +6 -0
- package/dist/lib/mesh/live/spool.js +203 -4
- package/dist/lib/work-context/bind-best.js +13 -1
- package/dist/main.js +16 -3
- package/dist/utils/self-update.d.ts +4 -0
- package/dist/utils/self-update.js +46 -9
- package/dist/utils/version-check.d.ts +7 -0
- package/dist/utils/version-check.js +30 -1
- package/dist/utils/version-gate.d.ts +28 -10
- package/dist/utils/version-gate.js +159 -29
- package/dist/utils/version-report.d.ts +2 -1
- package/dist/utils/version-report.js +8 -1
- package/package.json +1 -1
- package/dist/lib/work-context/index.d.ts +0 -26
- package/dist/lib/work-context/index.js +0 -22
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [5.305.0] — 2026-10-01
|
|
6
|
+
|
|
7
|
+
- Work Mesh project discovery skips projects whose matching local lifecycle state is archived or retired.
|
|
8
|
+
|
|
9
|
+
- `hq lanes show` now treats a newer terminal primary mirror as authoritative when an Outpost owner snapshot is behind, avoiding a misleading refresh warning while preserving warnings for nonterminal conflicts.
|
|
10
|
+
- Self-updates detect an unwritable npm global prefix before install, show the command to repair that install, and report the handled permission state as a rate-limited warning.
|
|
11
|
+
- Keep Work Mesh held events unique by event ID across retries, including interrupted overflow recovery.
|
|
12
|
+
- Added `hq mcp list` as an alias for the existing MCP status command. `hq version` can show cached CLI update guidance on stderr when the default-off `cli.version-update-nudge` flag is enabled. Bare `hq --version` prints only the installed CLI semver and starts no flag or network work.
|
|
13
|
+
- Keep-alive no longer lets a newer terminal lane or a same-profile lane under a distinct lane senior supersede a running lane.
|
|
14
|
+
- With `cli.company-errors-sdk-warning` enabled, `hq people` uses shared active-company guidance when local metadata is missing, and files commands show the AWS SDK Node-version notice only with verbose diagnostics.
|
|
15
|
+
- Remove the unrelated secret-sharing hint from active member listings.
|
|
16
|
+
|
|
17
|
+
## [5.304.0] — 2026-10-01
|
|
18
|
+
- `hq lanes show` skips owner refresh only when a failed Outpost dispatch explicitly recorded `pickup_persisted: false` and no owner heartbeat; it reports `remote_refresh: "skipped_undispatched"` in that case.
|
|
19
|
+
|
|
5
20
|
## [5.303.0] — 2026-10-01
|
|
6
21
|
- Fixed bundled shell scripts failing on Windows when Git Bash is installed but `bash` is not on `PATH`.
|
|
7
22
|
- The bundled token usage script now reads Claude sessions from `CLAUDE_CONFIG_DIR/projects` and the standard Claude project directories, including nested session folders (subagent transcripts are skipped), when no project directory is given.
|
|
@@ -3622,7 +3622,7 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
3622
3622
|
readonly subcommands: readonly [{
|
|
3623
3623
|
readonly name: "status";
|
|
3624
3624
|
readonly description: "Show installed MCP packs/servers and their health in BOTH Claude and Codex (PARTIAL/DRIFT flagged; secrets redacted)";
|
|
3625
|
-
readonly aliases: readonly [];
|
|
3625
|
+
readonly aliases: readonly ["list"];
|
|
3626
3626
|
readonly hidden: false;
|
|
3627
3627
|
readonly usage: "[options]";
|
|
3628
3628
|
readonly arguments: readonly [];
|
|
@@ -4674,7 +4674,9 @@ export const COMMAND_CATALOG = [
|
|
|
4674
4674
|
{
|
|
4675
4675
|
"name": "status",
|
|
4676
4676
|
"description": "Show installed MCP packs/servers and their health in BOTH Claude and Codex (PARTIAL/DRIFT flagged; secrets redacted)",
|
|
4677
|
-
"aliases": [
|
|
4677
|
+
"aliases": [
|
|
4678
|
+
"list"
|
|
4679
|
+
],
|
|
4678
4680
|
"hidden": false,
|
|
4679
4681
|
"usage": "[options]",
|
|
4680
4682
|
"arguments": [],
|
|
@@ -85,6 +85,7 @@ export const REGISTRATION_PLAN = [
|
|
|
85
85
|
];
|
|
86
86
|
async function executePlan(program, steps) {
|
|
87
87
|
const parents = new Map([["program", program]]);
|
|
88
|
+
let filesRuntimeConfigured = false;
|
|
88
89
|
for (const step of steps) {
|
|
89
90
|
if (step.type === "group") {
|
|
90
91
|
const group = program.command(step.name, step.hidden ? { hidden: true } : {});
|
|
@@ -94,6 +95,13 @@ async function executePlan(program, steps) {
|
|
|
94
95
|
const parent = parents.get(step.parent);
|
|
95
96
|
if (!parent)
|
|
96
97
|
throw new Error(`Missing command parent '${step.parent}' for '${step.exportName}'`);
|
|
98
|
+
if (step.root === "files" && !filesRuntimeConfigured) {
|
|
99
|
+
// The SDK emits its Node support notice during module evaluation. Set
|
|
100
|
+
// its narrow suppression switch before loading any files command module.
|
|
101
|
+
const { configureAwsSdkNodeVersionWarningOutput } = await import("./lib/aws-sdk-node-warning-output.js");
|
|
102
|
+
configureAwsSdkNodeVersionWarningOutput();
|
|
103
|
+
filesRuntimeConfigured = true;
|
|
104
|
+
}
|
|
97
105
|
const imported = await import(step.module);
|
|
98
106
|
const register = imported[step.exportName];
|
|
99
107
|
if (!register)
|
package/dist/commands/lanes.d.ts
CHANGED
|
@@ -132,6 +132,8 @@ export declare function parseMaxRuntimeSeconds(raw: unknown, workerId: string):
|
|
|
132
132
|
export declare const PICKUP_TERMINAL_REASONS: readonly ["account_not_logged_in", "unknown_account", "account_disabled", "model_unsupported"];
|
|
133
133
|
export declare const DISPATCH_TERMINAL_REASON: "outpost_dispatch_failed";
|
|
134
134
|
export type PickupTerminalReason = (typeof PICKUP_TERMINAL_REASONS)[number];
|
|
135
|
+
/** Only explicit non-persistence evidence can prove a failed dispatch has no owner snapshot. */
|
|
136
|
+
export declare function isUndispatchedOutpostMirror(lane: LaneRecord): boolean;
|
|
135
137
|
/**
|
|
136
138
|
* The CAS updater a pickup failure applies. A lane that is already terminal
|
|
137
139
|
* for any reason (a cancel that landed after the caller's last read, an
|
package/dist/commands/lanes.js
CHANGED
|
@@ -1284,9 +1284,7 @@ async function refreshRemoteLaneMirrors(ctx, hqRoot, records, parentLaneIds, opt
|
|
|
1284
1284
|
((parentLaneIds === undefined || (lane.senior.kind === "lane" &&
|
|
1285
1285
|
lane.senior.id !== undefined &&
|
|
1286
1286
|
parentLaneIds.has(lane.senior.id))) || options.laneIds?.has(lane.lane_id) === true) &&
|
|
1287
|
-
!(
|
|
1288
|
-
lane.terminal_reason === DISPATCH_TERMINAL_REASON &&
|
|
1289
|
-
lane.last_host_report_at === undefined) &&
|
|
1287
|
+
!isUndispatchedOutpostMirror(lane) &&
|
|
1290
1288
|
(options.includeTerminalMirrors === true || !isTerminalLaneState(lane.state)))
|
|
1291
1289
|
.sort((a, b) => {
|
|
1292
1290
|
const terminalOrder = Number(isTerminalLaneState(a.state)) - Number(isTerminalLaneState(b.state));
|
|
@@ -2524,6 +2522,14 @@ export const PICKUP_TERMINAL_REASONS = [
|
|
|
2524
2522
|
"model_unsupported",
|
|
2525
2523
|
];
|
|
2526
2524
|
export const DISPATCH_TERMINAL_REASON = "outpost_dispatch_failed";
|
|
2525
|
+
/** Only explicit non-persistence evidence can prove a failed dispatch has no owner snapshot. */
|
|
2526
|
+
export function isUndispatchedOutpostMirror(lane) {
|
|
2527
|
+
return lane.host.kind === "outpost" &&
|
|
2528
|
+
isTerminalLaneState(lane.state) &&
|
|
2529
|
+
lane.terminal_reason === DISPATCH_TERMINAL_REASON &&
|
|
2530
|
+
lane.terminal_details?.pickup_persisted === false &&
|
|
2531
|
+
lane.last_host_report_at == null;
|
|
2532
|
+
}
|
|
2527
2533
|
function parsePickupTerminalReason(value) {
|
|
2528
2534
|
if (PICKUP_TERMINAL_REASONS.includes(value)) {
|
|
2529
2535
|
return value;
|
|
@@ -3647,6 +3653,9 @@ async function dispatchCreatedOutpostLane(ctx, input) {
|
|
|
3647
3653
|
command_id: dispatched.commandId,
|
|
3648
3654
|
exit_code: dispatched.exitCode,
|
|
3649
3655
|
status: dispatched.status,
|
|
3656
|
+
...(remote?.pickup_persisted === false || ownerError.details.pickup_persisted === false
|
|
3657
|
+
? { pickup_persisted: false }
|
|
3658
|
+
: {}),
|
|
3650
3659
|
owner_error: ownerError,
|
|
3651
3660
|
});
|
|
3652
3661
|
await emitLaneTerminalStateSoft(ctx, failed);
|
|
@@ -5038,13 +5047,17 @@ async function runOutpostIdentityBackfillTarget(ctx, opts) {
|
|
|
5038
5047
|
}
|
|
5039
5048
|
async function refreshOutpostLaneCopiesForShow(ctx, lane, localLanes) {
|
|
5040
5049
|
if (lane.host.kind !== "outpost" || laneRunsHere(lane, reconcileHostFromCtx(ctx))) {
|
|
5041
|
-
return lane;
|
|
5050
|
+
return { lane };
|
|
5051
|
+
}
|
|
5052
|
+
if (isUndispatchedOutpostMirror(lane)) {
|
|
5053
|
+
return { lane, remoteRefresh: "skipped_undispatched" };
|
|
5042
5054
|
}
|
|
5043
5055
|
const outpostId = lane.host.id;
|
|
5044
5056
|
const descendantIds = localLanes
|
|
5045
5057
|
.filter((candidate) => candidate.lane_id !== lane.lane_id &&
|
|
5046
5058
|
candidate.host.kind === "outpost" && candidate.host.id === outpostId &&
|
|
5047
5059
|
walkHitsLane(ctx.hqRoot(), candidate.senior, lane.lane_id, ctx))
|
|
5060
|
+
.filter((candidate) => !isUndispatchedOutpostMirror(candidate))
|
|
5048
5061
|
.map((candidate) => candidate.lane_id);
|
|
5049
5062
|
const refresh = async (laneId) => {
|
|
5050
5063
|
try {
|
|
@@ -5052,6 +5065,19 @@ async function refreshOutpostLaneCopiesForShow(ctx, lane, localLanes) {
|
|
|
5052
5065
|
return syncLaneRecordFromOwner(ctx.hqRoot(), laneId, outpostId, ownerLane, undefined, { allowNonTerminal: true });
|
|
5053
5066
|
}
|
|
5054
5067
|
catch (err) {
|
|
5068
|
+
if (isLanesError(err) && err.error === "outpost_lane_sync_conflict") {
|
|
5069
|
+
const ownerVersion = err.details.owner_version;
|
|
5070
|
+
const primaryVersion = err.details.primary_version;
|
|
5071
|
+
if (typeof ownerVersion === "number" &&
|
|
5072
|
+
typeof primaryVersion === "number" &&
|
|
5073
|
+
ownerVersion < primaryVersion) {
|
|
5074
|
+
const currentPrimary = readLane(ctx.hqRoot(), laneId);
|
|
5075
|
+
if (currentPrimary.version === primaryVersion &&
|
|
5076
|
+
isTerminalLaneState(currentPrimary.state)) {
|
|
5077
|
+
return currentPrimary;
|
|
5078
|
+
}
|
|
5079
|
+
}
|
|
5080
|
+
}
|
|
5055
5081
|
const detail = isLanesError(err)
|
|
5056
5082
|
? `${err.error}: ${err.message}`
|
|
5057
5083
|
: `outpost_lane_sync_unavailable: ${dispatchErrorMessage(err)}`;
|
|
@@ -5061,13 +5087,14 @@ async function refreshOutpostLaneCopiesForShow(ctx, lane, localLanes) {
|
|
|
5061
5087
|
};
|
|
5062
5088
|
const refreshed = await mapWithConcurrency([lane.lane_id, ...descendantIds], OUTPOST_HIRE_SUMMARY_CONCURRENCY, refresh);
|
|
5063
5089
|
const refreshedLane = refreshed[0] ?? null;
|
|
5064
|
-
return refreshedLane ?? lane;
|
|
5090
|
+
return { lane: refreshedLane ?? lane };
|
|
5065
5091
|
}
|
|
5066
5092
|
async function runShow(ctx, laneId, hasPendingQuestionForLane) {
|
|
5067
5093
|
const hqRoot = ctx.hqRoot();
|
|
5068
5094
|
const initialLane = readLane(hqRoot, laneId);
|
|
5069
5095
|
const initialLanes = loadAllLanes(hqRoot);
|
|
5070
|
-
const
|
|
5096
|
+
const showRefresh = await refreshOutpostLaneCopiesForShow(ctx, initialLane, initialLanes);
|
|
5097
|
+
const storedLane = showRefresh.lane;
|
|
5071
5098
|
const allLanes = loadAllLanes(hqRoot);
|
|
5072
5099
|
const hire = await laneHirePresentation(ctx, storedLane, allLanes);
|
|
5073
5100
|
const remoteQueued = storedLane.state === "queued" &&
|
|
@@ -5152,6 +5179,7 @@ async function runShow(ctx, laneId, hasPendingQuestionForLane) {
|
|
|
5152
5179
|
stalled_reason: lane.stalled_reason ?? null,
|
|
5153
5180
|
ask_delivery_failed: askDeliveryFailed,
|
|
5154
5181
|
delivery,
|
|
5182
|
+
...(showRefresh.remoteRefresh === undefined ? {} : { remote_refresh: showRefresh.remoteRefresh }),
|
|
5155
5183
|
provider_session_id: lane.provider_session_id ?? null,
|
|
5156
5184
|
provider_session_id_absent: lane.provider_session_id_absent ?? null,
|
|
5157
5185
|
envelope_repair_mode: lane.envelope_repair?.mode ?? null,
|
|
@@ -374,6 +374,7 @@ export function registerMcpCommand(program) {
|
|
|
374
374
|
.description('MCP pack observability (read-only) across Claude + Codex');
|
|
375
375
|
mcp
|
|
376
376
|
.command('status')
|
|
377
|
+
.alias('list')
|
|
377
378
|
.description('Show installed MCP packs/servers and their health in BOTH Claude and Codex (PARTIAL/DRIFT flagged; secrets redacted)')
|
|
378
379
|
.option('--json', 'Emit the structured report as JSON (for CI / scripting)')
|
|
379
380
|
.action((opts) => {
|
package/dist/commands/members.js
CHANGED
|
@@ -672,7 +672,6 @@ export function registerMembersCommand(program) {
|
|
|
672
672
|
const name = m.personName ?? m.personSlug ?? "";
|
|
673
673
|
console.log([email.padEnd(emailW), m.role.padEnd(roleW), name].join(" "));
|
|
674
674
|
}
|
|
675
|
-
console.log(chalk.gray("Share a secret with a member: hq secrets share <path> --with <email>"));
|
|
676
675
|
}
|
|
677
676
|
catch (err) {
|
|
678
677
|
if (err instanceof InviteHttpError) {
|
|
@@ -17,6 +17,9 @@ import { resolveNameToEmail, type PersonRecord } from "../utils/people.js";
|
|
|
17
17
|
export type FetchPeopleRoster = (hqRoot: string, companySlug: string) => Promise<PersonRecord[]>;
|
|
18
18
|
interface PeopleCommandDeps {
|
|
19
19
|
fetchRoster?: FetchPeopleRoster;
|
|
20
|
+
/** Test seam for the shared active-membership ambiguity resolver. */
|
|
21
|
+
resolveMissingManifestMemberships?: () => Promise<void>;
|
|
22
|
+
companySelectionGuidanceEnabled?: () => boolean;
|
|
20
23
|
}
|
|
21
24
|
interface PeopleLookupOpts {
|
|
22
25
|
localOnly?: boolean;
|
package/dist/commands/people.js
CHANGED
|
@@ -19,6 +19,7 @@ import { findHqRoot } from "../utils/manifest.js";
|
|
|
19
19
|
import { manifestPath } from "./cloud-provision.js";
|
|
20
20
|
import { ensureCognitoToken } from "../utils/cognito-session.js";
|
|
21
21
|
import { getCompanyUid } from "../utils/vault-api.js";
|
|
22
|
+
import { companyErrorsSdkWarningEnabled } from "../lib/company-errors-sdk-warning-flag.js";
|
|
22
23
|
import { listActiveMembers } from "./members.js";
|
|
23
24
|
import { assertSafeCompanySlug, listCompanyPeople, searchPeople, resolveNameToEmail, companyPeopleDir, } from "../utils/people.js";
|
|
24
25
|
function slugify(value) {
|
|
@@ -108,6 +109,29 @@ export function resolveCompanySlug(hqRoot, explicit) {
|
|
|
108
109
|
throw new Error("Multiple companies found — re-run with --company <slug> to pick one:\n" +
|
|
109
110
|
slugs.map((s) => ` --company ${s}`).join("\n"));
|
|
110
111
|
}
|
|
112
|
+
async function resolvePeopleCompanySlug(hqRoot, explicit, localOnly, deps) {
|
|
113
|
+
try {
|
|
114
|
+
return resolveCompanySlug(hqRoot, explicit);
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
118
|
+
const ambiguous = message.includes("companies/manifest.yaml not found") ||
|
|
119
|
+
message.startsWith("Multiple companies found");
|
|
120
|
+
const enabled = (deps.companySelectionGuidanceEnabled ?? companyErrorsSdkWarningEnabled)();
|
|
121
|
+
if (!explicit && !localOnly && enabled && ambiguous) {
|
|
122
|
+
const resolveMemberships = deps.resolveMissingManifestMemberships ??
|
|
123
|
+
(async () => {
|
|
124
|
+
const token = await ensureCognitoToken();
|
|
125
|
+
await getCompanyUid(token, undefined);
|
|
126
|
+
});
|
|
127
|
+
// The shared resolver throws members-style guidance for multiple active
|
|
128
|
+
// companies. A single membership cannot provide a local slug without a
|
|
129
|
+
// manifest, so preserve the existing missing-manifest error in that case.
|
|
130
|
+
await resolveMemberships();
|
|
131
|
+
}
|
|
132
|
+
throw error;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
111
135
|
/** Resolve the HQ root: explicit override (tests) → cwd walk-up. */
|
|
112
136
|
function resolveHqRoot(opts) {
|
|
113
137
|
return opts.hqRoot ?? findHqRoot();
|
|
@@ -192,7 +216,7 @@ export function registerPeopleCommand(program, deps = {}) {
|
|
|
192
216
|
try {
|
|
193
217
|
const scope = people.opts();
|
|
194
218
|
const hqRoot = resolveHqRoot(scope);
|
|
195
|
-
const slug =
|
|
219
|
+
const slug = await resolvePeopleCompanySlug(hqRoot, scope.company, opts.localOnly, deps);
|
|
196
220
|
let records = listCompanyPeople(hqRoot, slug);
|
|
197
221
|
if (!opts.localOnly) {
|
|
198
222
|
const roster = await tryFetchRoster(deps.fetchRoster ?? fetchMembershipRoster, hqRoot, slug);
|
|
@@ -223,7 +247,7 @@ export function registerPeopleCommand(program, deps = {}) {
|
|
|
223
247
|
try {
|
|
224
248
|
const scope = people.opts();
|
|
225
249
|
const hqRoot = resolveHqRoot(scope);
|
|
226
|
-
const slug =
|
|
250
|
+
const slug = await resolvePeopleCompanySlug(hqRoot, scope.company, opts.localOnly, deps);
|
|
227
251
|
const matches = await searchPeopleWithRosterFallback({
|
|
228
252
|
hqRoot,
|
|
229
253
|
slug,
|
|
@@ -255,7 +279,7 @@ export function registerPeopleCommand(program, deps = {}) {
|
|
|
255
279
|
try {
|
|
256
280
|
const scope = people.opts();
|
|
257
281
|
const hqRoot = resolveHqRoot(scope);
|
|
258
|
-
const slug =
|
|
282
|
+
const slug = await resolvePeopleCompanySlug(hqRoot, scope.company, opts.localOnly, deps);
|
|
259
283
|
const result = await resolvePersonWithRosterFallback({
|
|
260
284
|
hqRoot,
|
|
261
285
|
slug,
|
package/dist/index.js
CHANGED
|
@@ -22,9 +22,14 @@ if (isVersionRequest(process.argv)) {
|
|
|
22
22
|
// compare). `hq version` and `--json` print the labelled HQ/CLI report.
|
|
23
23
|
// Lazy so this path still skips the command graph.
|
|
24
24
|
if (wantsLabelledVersionReport(process.argv)) {
|
|
25
|
-
void import("./
|
|
25
|
+
void import("./lib/flag-registry.js").then(({ kickFlagRegistryReadiness }) => {
|
|
26
|
+
kickFlagRegistryReadiness();
|
|
27
|
+
return import("./utils/version-report.js").then(({ runVersionCommand }) => runVersionCommand(process.argv));
|
|
28
|
+
});
|
|
26
29
|
}
|
|
27
30
|
else {
|
|
31
|
+
// Keep the benchmarked updater fast route limited to the installed semver.
|
|
32
|
+
// Registry checks and flag resolution belong to the regular command path.
|
|
28
33
|
void import("./cli-version.js").then(({ CLI_VERSION }) => {
|
|
29
34
|
process.stdout.write(`${CLI_VERSION}\n`);
|
|
30
35
|
});
|
package/dist/lazy-commands.js
CHANGED
|
@@ -55,8 +55,11 @@ function commandForToken(token) {
|
|
|
55
55
|
* preserve the complete help and unknown-command recovery surfaces.
|
|
56
56
|
*/
|
|
57
57
|
export function findLazyCommand(argv) {
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
let commandIndex = 2;
|
|
59
|
+
while (argv[commandIndex] === "--verbose")
|
|
60
|
+
commandIndex += 1;
|
|
61
|
+
const first = argv[commandIndex];
|
|
62
|
+
const token = first === "help" ? argv[commandIndex + 1] : first;
|
|
60
63
|
if (typeof token !== "string" || token.startsWith("-"))
|
|
61
64
|
return null;
|
|
62
65
|
const command = commandForToken(token);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare const AWS_SDK_NODE_VERSION_WARNING_ENV = "AWS_SDK_JS_NODE_VERSION_SUPPORT_WARNING_DISABLED";
|
|
2
|
+
export interface AwsSdkWarningOutputOptions {
|
|
3
|
+
argv?: readonly string[];
|
|
4
|
+
env?: NodeJS.ProcessEnv;
|
|
5
|
+
enabled?: boolean;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Configure the AWS SDK's documented, warning-specific switch before loading
|
|
9
|
+
* the files command and its SDK clients. No other SDK warnings are affected.
|
|
10
|
+
*/
|
|
11
|
+
export declare function configureAwsSdkNodeVersionWarningOutput(options?: AwsSdkWarningOutputOptions): void;
|
|
12
|
+
//# sourceMappingURL=aws-sdk-node-warning-output.d.ts.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { companyErrorsSdkWarningEnabled } from "./company-errors-sdk-warning-flag.js";
|
|
2
|
+
export const AWS_SDK_NODE_VERSION_WARNING_ENV = "AWS_SDK_JS_NODE_VERSION_SUPPORT_WARNING_DISABLED";
|
|
3
|
+
/**
|
|
4
|
+
* Configure the AWS SDK's documented, warning-specific switch before loading
|
|
5
|
+
* the files command and its SDK clients. No other SDK warnings are affected.
|
|
6
|
+
*/
|
|
7
|
+
export function configureAwsSdkNodeVersionWarningOutput(options = {}) {
|
|
8
|
+
const env = options.env ?? process.env;
|
|
9
|
+
const argv = options.argv ?? process.argv;
|
|
10
|
+
const enabled = options.enabled ?? companyErrorsSdkWarningEnabled();
|
|
11
|
+
if (!enabled)
|
|
12
|
+
return;
|
|
13
|
+
const verbose = argv.includes("--verbose");
|
|
14
|
+
const debug = /^(1|true|yes)$/i.test(env.HQ_DEBUG?.trim() ?? "");
|
|
15
|
+
if (verbose || debug) {
|
|
16
|
+
delete env[AWS_SDK_NODE_VERSION_WARNING_ENV];
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
env[AWS_SDK_NODE_VERSION_WARNING_ENV] = "true";
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=aws-sdk-node-warning-output.js.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare const COMPANY_ERRORS_SDK_WARNING_FLAG = "cli.company-errors-sdk-warning";
|
|
2
|
+
export declare const COMPANY_ERRORS_SDK_WARNING_FALLBACK = false;
|
|
3
|
+
/** One default-off rollout gate for the two US-163 CLI compatibility fixes. */
|
|
4
|
+
export declare function companyErrorsSdkWarningEnabled(): boolean;
|
|
5
|
+
//# sourceMappingURL=company-errors-sdk-warning-flag.d.ts.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { resolveProcessFlagGate } from "./flag-registry.js";
|
|
2
|
+
export const COMPANY_ERRORS_SDK_WARNING_FLAG = "cli.company-errors-sdk-warning";
|
|
3
|
+
export const COMPANY_ERRORS_SDK_WARNING_FALLBACK = false;
|
|
4
|
+
/** One default-off rollout gate for the two US-163 CLI compatibility fixes. */
|
|
5
|
+
export function companyErrorsSdkWarningEnabled() {
|
|
6
|
+
return resolveProcessFlagGate(COMPANY_ERRORS_SDK_WARNING_FLAG, {}, () => COMPANY_ERRORS_SDK_WARNING_FALLBACK);
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=company-errors-sdk-warning-flag.js.map
|
|
@@ -10,7 +10,7 @@ import { readDeviceConfig, } from "../../work-context/config.js";
|
|
|
10
10
|
import { outboxStats, listOutboxOperations } from "../../work-context/outbox.js";
|
|
11
11
|
import { outboxQuarantineStatusEnabled } from "../../work-context/outbox-quarantine-status-flag.js";
|
|
12
12
|
import { workContextRoot, workContextSessionsDir } from "../../work-context/paths.js";
|
|
13
|
-
import { countJsonlLines, deadLetterNonEmpty, } from "../../mesh/live/spool.js";
|
|
13
|
+
import { countDistinctHeldEvents, countJsonlLines, deadLetterNonEmpty, } from "../../mesh/live/spool.js";
|
|
14
14
|
import { workMeshDeadLetterPath, workMeshHeldPath, workMeshRoot, workMeshSpoolPath, } from "../../mesh/live/paths.js";
|
|
15
15
|
import { collectDaemonDoctor, formatDaemonDoctor, } from "../../mesh/live/daemon/doctor.js";
|
|
16
16
|
export const WORK_CONTEXT_FAMILY_ID = "work-context";
|
|
@@ -139,7 +139,7 @@ function runChecks(deps) {
|
|
|
139
139
|
const heldPath = workMeshHeldPath(meshRoot);
|
|
140
140
|
const deadLetterPath = workMeshDeadLetterPath(meshRoot);
|
|
141
141
|
const spoolDepth = countJsonlLines(spoolPath);
|
|
142
|
-
const heldDepth =
|
|
142
|
+
const heldDepth = countDistinctHeldEvents(heldPath);
|
|
143
143
|
results.push({
|
|
144
144
|
status: "PASS",
|
|
145
145
|
checkId: "work-context.spool-depth",
|
|
@@ -55,7 +55,7 @@ export declare const DEFAULT_PER_CASE_TIMEOUT_MS = 10000;
|
|
|
55
55
|
*/
|
|
56
56
|
export declare const SANDBOX_COPY_RELPATHS: readonly string[];
|
|
57
57
|
/** One gate execution's inputs. */
|
|
58
|
-
|
|
58
|
+
interface GateRunInput {
|
|
59
59
|
/** Absolute path to the sandboxed `hook-gate.sh`. */
|
|
60
60
|
gatePath: string;
|
|
61
61
|
/** Absolute path to the sandboxed hook script the gate delegates to. */
|
|
@@ -72,7 +72,7 @@ export interface GateRunInput {
|
|
|
72
72
|
timeoutMs: number;
|
|
73
73
|
}
|
|
74
74
|
/** One gate execution's observed outcome. */
|
|
75
|
-
|
|
75
|
+
interface GateRunResult {
|
|
76
76
|
/** True when the execution exceeded {@link GateRunInput.timeoutMs}. */
|
|
77
77
|
timedOut: boolean;
|
|
78
78
|
/** Gate exit code, or null when it did not exit normally (timeout/signal). */
|
|
@@ -81,14 +81,7 @@ export interface GateRunResult {
|
|
|
81
81
|
stderr: string;
|
|
82
82
|
}
|
|
83
83
|
/** Executes one gate invocation. Injectable so the aggregation is unit-testable. */
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* The real runner: spawn `bash hook-gate.sh <id> <script>` with the payload on
|
|
87
|
-
* stdin, `HQ_HOOK_PROFILE` set, and a hard timeout. A timeout is detected by the
|
|
88
|
-
* `ETIMEDOUT` error code spawnSync reports; the process is killed with SIGKILL so
|
|
89
|
-
* a hook cannot ignore the bound.
|
|
90
|
-
*/
|
|
91
|
-
export declare const defaultGateRunner: GateRunner;
|
|
84
|
+
type GateRunner = (input: GateRunInput) => GateRunResult;
|
|
92
85
|
/** Options for {@link runDeepGuardTests}. All are injectable for testing. */
|
|
93
86
|
export interface DeepTestOptions {
|
|
94
87
|
/** Per-case time bound. Default: {@link DEFAULT_PER_CASE_TIMEOUT_MS}. */
|
|
@@ -109,4 +102,5 @@ export declare function cleanupAllDeepSandboxes(): void;
|
|
|
109
102
|
* an empty list — creating no sandbox — when there is nothing to run.
|
|
110
103
|
*/
|
|
111
104
|
export declare function runDeepGuardTests(context: CheckContext, options?: DeepTestOptions): Promise<CheckResult[]>;
|
|
105
|
+
export {};
|
|
112
106
|
//# sourceMappingURL=executor.d.ts.map
|
|
@@ -74,7 +74,7 @@ export const SANDBOX_COPY_RELPATHS = [
|
|
|
74
74
|
* `ETIMEDOUT` error code spawnSync reports; the process is killed with SIGKILL so
|
|
75
75
|
* a hook cannot ignore the bound.
|
|
76
76
|
*/
|
|
77
|
-
|
|
77
|
+
const defaultGateRunner = (input) => {
|
|
78
78
|
const result = spawnSync("bash", [input.gatePath, input.hookId, input.hookScriptPath], {
|
|
79
79
|
input: input.payload,
|
|
80
80
|
cwd: input.cwd,
|
|
@@ -46,14 +46,6 @@ import { type PlatformId } from "../payload-shapes.js";
|
|
|
46
46
|
export declare const PARITY_FAMILY_ID = "deep-parity";
|
|
47
47
|
/** Human title of the parity family. */
|
|
48
48
|
export declare const PARITY_FAMILY_TITLE = "Cross-platform parity replay";
|
|
49
|
-
/** Default per-case, per-platform time bound. */
|
|
50
|
-
export declare const DEFAULT_PARITY_TIMEOUT_MS = 10000;
|
|
51
|
-
/** The gate profile the replay runs each hook under. Strict is the broadest
|
|
52
|
-
* allowlist, so profile gating never suppresses the hook whose LOGIC is being
|
|
53
|
-
* compared across platforms — profile-specific behaviour is US-008's concern. */
|
|
54
|
-
export declare const PARITY_GATE_PROFILE = "strict";
|
|
55
|
-
/** The session id stamped onto every replay payload. */
|
|
56
|
-
export declare const PARITY_SESSION_ID = "hq-doctor-parity-replay";
|
|
57
49
|
/** One platform replay's inputs. */
|
|
58
50
|
export interface PlatformRunInput {
|
|
59
51
|
/** Which platform's adapter path to exercise. */
|
|
@@ -85,27 +77,7 @@ export interface PlatformRunOutput {
|
|
|
85
77
|
/** Executes one platform replay. Injectable so the comparison is unit-testable. */
|
|
86
78
|
export type PlatformRunner = (input: PlatformRunInput) => PlatformRunOutput;
|
|
87
79
|
/** The real runner set, keyed by platform. */
|
|
88
|
-
|
|
89
|
-
/** The real Claude runner: the canonical script through the Claude gate. */
|
|
90
|
-
export declare const defaultClaudeRunner: PlatformRunner;
|
|
91
|
-
/** The real Codex runner: the duplicated mirror through the Codex gate. */
|
|
92
|
-
export declare const defaultCodexRunner: PlatformRunner;
|
|
93
|
-
/**
|
|
94
|
-
* The real Grok runner. Grok shares the canonical `.claude/hooks/` scripts with
|
|
95
|
-
* Claude — it has no mirror of its own — and its adapter's whole job is to read
|
|
96
|
-
* the camelCase payload, map the tool alias back to canonical, build the Claude
|
|
97
|
-
* JSON, and pipe it to the same `hook-gate.sh`. We reproduce that routing here
|
|
98
|
-
* (via {@link toGrokPayload} + {@link grokToSnakePayload}) rather than spawn the
|
|
99
|
-
* adapter directly, for two reasons: the live adapter runs a whole tool
|
|
100
|
-
* *sequence* where the replay needs one isolated hook, and its user-global
|
|
101
|
-
* bridge cannot be simulated from a project-local run (US-005 keeps bridge
|
|
102
|
-
* presence a wiring check). The Grok block protocol still applies — the gate's
|
|
103
|
-
* non-zero exit is what the adapter would translate into a stdout `deny`
|
|
104
|
-
* ({@link interpretGrokBlock} in verdictFor recognises both).
|
|
105
|
-
*/
|
|
106
|
-
export declare const defaultGrokRunner: PlatformRunner;
|
|
107
|
-
/** The default real runner set wired to each platform's real adapter path. */
|
|
108
|
-
export declare const defaultPlatformRunners: PlatformRunners;
|
|
80
|
+
type PlatformRunners = Record<PlatformId, PlatformRunner>;
|
|
109
81
|
/** Options for {@link runParityReplay}. All injectable for testing. */
|
|
110
82
|
export interface ParityOptions {
|
|
111
83
|
/** Per-case, per-platform time bound. Default: {@link DEFAULT_PARITY_TIMEOUT_MS}. */
|
|
@@ -126,4 +98,5 @@ export declare function cleanupAllParitySandboxes(): void;
|
|
|
126
98
|
* empty list — creating no sandbox — when there is nothing to replay.
|
|
127
99
|
*/
|
|
128
100
|
export declare function runParityReplay(context: CheckContext, options?: ParityOptions): Promise<CheckResult[]>;
|
|
101
|
+
export {};
|
|
129
102
|
//# sourceMappingURL=parity.d.ts.map
|
|
@@ -52,13 +52,13 @@ export const PARITY_FAMILY_ID = "deep-parity";
|
|
|
52
52
|
/** Human title of the parity family. */
|
|
53
53
|
export const PARITY_FAMILY_TITLE = "Cross-platform parity replay";
|
|
54
54
|
/** Default per-case, per-platform time bound. */
|
|
55
|
-
|
|
55
|
+
const DEFAULT_PARITY_TIMEOUT_MS = 10_000;
|
|
56
56
|
/** The gate profile the replay runs each hook under. Strict is the broadest
|
|
57
57
|
* allowlist, so profile gating never suppresses the hook whose LOGIC is being
|
|
58
58
|
* compared across platforms — profile-specific behaviour is US-008's concern. */
|
|
59
|
-
|
|
59
|
+
const PARITY_GATE_PROFILE = "strict";
|
|
60
60
|
/** The session id stamped onto every replay payload. */
|
|
61
|
-
|
|
61
|
+
const PARITY_SESSION_ID = "hq-doctor-parity-replay";
|
|
62
62
|
function spawnWithStdin(command, args, stdin, input) {
|
|
63
63
|
const result = spawnSync(command, args, {
|
|
64
64
|
input: stdin,
|
|
@@ -96,12 +96,12 @@ function runThroughGate(input, platformDir, payload) {
|
|
|
96
96
|
return { ...spawned };
|
|
97
97
|
}
|
|
98
98
|
/** The real Claude runner: the canonical script through the Claude gate. */
|
|
99
|
-
|
|
99
|
+
const defaultClaudeRunner = (input) => runThroughGate(input, ".claude", JSON.stringify(toClaudePayload(input.testCase, {
|
|
100
100
|
cwd: input.sandbox,
|
|
101
101
|
sessionId: PARITY_SESSION_ID,
|
|
102
102
|
})));
|
|
103
103
|
/** The real Codex runner: the duplicated mirror through the Codex gate. */
|
|
104
|
-
|
|
104
|
+
const defaultCodexRunner = (input) => runThroughGate(input, ".codex", JSON.stringify(toCodexPayload(input.testCase, {
|
|
105
105
|
cwd: input.sandbox,
|
|
106
106
|
sessionId: PARITY_SESSION_ID,
|
|
107
107
|
})));
|
|
@@ -118,7 +118,7 @@ export const defaultCodexRunner = (input) => runThroughGate(input, ".codex", JSO
|
|
|
118
118
|
* non-zero exit is what the adapter would translate into a stdout `deny`
|
|
119
119
|
* ({@link interpretGrokBlock} in verdictFor recognises both).
|
|
120
120
|
*/
|
|
121
|
-
|
|
121
|
+
const defaultGrokRunner = (input) => {
|
|
122
122
|
const grokPayload = toGrokPayload(input.testCase, {
|
|
123
123
|
cwd: input.sandbox,
|
|
124
124
|
sessionId: PARITY_SESSION_ID,
|
|
@@ -127,7 +127,7 @@ export const defaultGrokRunner = (input) => {
|
|
|
127
127
|
return runThroughGate(input, ".claude", JSON.stringify(snake));
|
|
128
128
|
};
|
|
129
129
|
/** The default real runner set wired to each platform's real adapter path. */
|
|
130
|
-
|
|
130
|
+
const defaultPlatformRunners = {
|
|
131
131
|
claude: defaultClaudeRunner,
|
|
132
132
|
codex: defaultCodexRunner,
|
|
133
133
|
grok: defaultGrokRunner,
|
|
@@ -46,21 +46,10 @@ export declare const DEFAULT_PER_CASE_TIMEOUT_MS = 15000;
|
|
|
46
46
|
* come under sandboxed execution.
|
|
47
47
|
*/
|
|
48
48
|
export type Precondition = "git-repo" | "session-metadata" | "company-scaffold";
|
|
49
|
-
/** Every precondition the default seeder knows how to provide. */
|
|
50
|
-
export declare const KNOWN_PRECONDITIONS: readonly Precondition[];
|
|
51
|
-
/**
|
|
52
|
-
* Tree entries copied from the live tree into every sandbox — the same curated
|
|
53
|
-
* set US-008 uses: the hook scripts and gate, the settings a hook may read, the
|
|
54
|
-
* Codex/Grok mirrors, and `core/` (helpers hooks source). Missing entries are
|
|
55
|
-
* skipped so a partial tree still sandboxes cleanly.
|
|
56
|
-
*/
|
|
57
|
-
export declare const SANDBOX_SEED_RELPATHS: readonly string[];
|
|
58
49
|
/** Remove every side-effect sandbox still on disk. Safe to call repeatedly. */
|
|
59
50
|
export declare function cleanupAllSandboxes(): void;
|
|
60
51
|
/** The sandbox roots this process is still tracking (for tests). */
|
|
61
52
|
export declare function trackedSandboxRoots(): string[];
|
|
62
|
-
/** Remove one sandbox and stop tracking it. Idempotent. */
|
|
63
|
-
export declare function destroySandbox(root: string): void;
|
|
64
53
|
/** The outcome of seeding one sandbox. */
|
|
65
54
|
export interface SeedResult {
|
|
66
55
|
/** Absolute, realpath'd sandbox root under the OS temp dir. */
|
|
@@ -91,7 +80,7 @@ export interface SeedOptions {
|
|
|
91
80
|
*/
|
|
92
81
|
export declare function seedSandbox(hqRoot: string, opts?: SeedOptions): SeedResult;
|
|
93
82
|
/** One hook execution's inputs. */
|
|
94
|
-
|
|
83
|
+
interface HookRunInput {
|
|
95
84
|
/** Absolute path to the sandboxed hook script. */
|
|
96
85
|
hookScriptPath: string;
|
|
97
86
|
/** The JSON payload piped to the hook's stdin. */
|
|
@@ -104,7 +93,7 @@ export interface HookRunInput {
|
|
|
104
93
|
timeoutMs: number;
|
|
105
94
|
}
|
|
106
95
|
/** One hook execution's observed result (exit code is diagnostic only). */
|
|
107
|
-
|
|
96
|
+
interface HookRunResult {
|
|
108
97
|
/** True when the execution exceeded {@link HookRunInput.timeoutMs}. */
|
|
109
98
|
timedOut: boolean;
|
|
110
99
|
/** Exit code, or null when it did not exit normally (timeout/signal). */
|
|
@@ -113,17 +102,9 @@ export interface HookRunResult {
|
|
|
113
102
|
stderr: string;
|
|
114
103
|
}
|
|
115
104
|
/** Runs one hook. Injectable so the tier's aggregation is unit-testable. */
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* The real runner: fire the hook script directly with the payload on stdin, its
|
|
119
|
-
* cwd and session env pointed at the sandbox, and a hard timeout. Unlike a pure
|
|
120
|
-
* guard (US-008), a side-effecting hook is run directly rather than through the
|
|
121
|
-
* gate — its exit code is irrelevant here; what matters is the effect it leaves
|
|
122
|
-
* on the sandbox, which effects.ts reads afterwards.
|
|
123
|
-
*/
|
|
124
|
-
export declare const defaultHookRunner: HookRunner;
|
|
105
|
+
type HookRunner = (input: HookRunInput) => HookRunResult;
|
|
125
106
|
/** One parsed side-effecting fixture case (its own shape, not the US-007 schema). */
|
|
126
|
-
|
|
107
|
+
interface SideEffectCase {
|
|
127
108
|
name: string;
|
|
128
109
|
event: string;
|
|
129
110
|
tool: string;
|
|
@@ -136,13 +117,13 @@ export interface SideEffectCase {
|
|
|
136
117
|
expectedFailure?: ExpectedFailure;
|
|
137
118
|
}
|
|
138
119
|
/** A validated side-effecting fixture. */
|
|
139
|
-
|
|
120
|
+
interface SideEffectFixture {
|
|
140
121
|
hookId: string;
|
|
141
122
|
relpath: string;
|
|
142
123
|
cases: SideEffectCase[];
|
|
143
124
|
}
|
|
144
125
|
/** A side-effecting fixture that could not be parsed. */
|
|
145
|
-
|
|
126
|
+
interface SideEffectFixtureError {
|
|
146
127
|
hookId: string | null;
|
|
147
128
|
relpath: string;
|
|
148
129
|
message: string;
|
|
@@ -187,4 +168,5 @@ export interface SideEffectTestOptions {
|
|
|
187
168
|
* Returns an empty list when there is nothing side-effecting to run.
|
|
188
169
|
*/
|
|
189
170
|
export declare function runSideEffectTests(context: CheckContext, options?: SideEffectTestOptions): Promise<CheckResult[]>;
|
|
171
|
+
export {};
|
|
190
172
|
//# sourceMappingURL=sandbox.d.ts.map
|
|
@@ -46,7 +46,7 @@ import { captureEffectBaseline, describeEffect, defaultGitProbe, evaluateEffects
|
|
|
46
46
|
/** Default per-case time bound. No latency target, but no case may hang. */
|
|
47
47
|
export const DEFAULT_PER_CASE_TIMEOUT_MS = 15_000;
|
|
48
48
|
/** Every precondition the default seeder knows how to provide. */
|
|
49
|
-
|
|
49
|
+
const KNOWN_PRECONDITIONS = [
|
|
50
50
|
"git-repo",
|
|
51
51
|
"session-metadata",
|
|
52
52
|
"company-scaffold",
|
|
@@ -57,7 +57,7 @@ export const KNOWN_PRECONDITIONS = [
|
|
|
57
57
|
* Codex/Grok mirrors, and `core/` (helpers hooks source). Missing entries are
|
|
58
58
|
* skipped so a partial tree still sandboxes cleanly.
|
|
59
59
|
*/
|
|
60
|
-
|
|
60
|
+
const SANDBOX_SEED_RELPATHS = [
|
|
61
61
|
".claude/hooks",
|
|
62
62
|
".claude/settings.json",
|
|
63
63
|
".claude/settings.local.json",
|
|
@@ -100,7 +100,7 @@ function removeTree(root) {
|
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
102
|
/** Remove one sandbox and stop tracking it. Idempotent. */
|
|
103
|
-
|
|
103
|
+
function destroySandbox(root) {
|
|
104
104
|
removeTree(root);
|
|
105
105
|
trackedSandboxes.delete(root);
|
|
106
106
|
}
|
|
@@ -198,7 +198,7 @@ function git(root, args) {
|
|
|
198
198
|
* gate — its exit code is irrelevant here; what matters is the effect it leaves
|
|
199
199
|
* on the sandbox, which effects.ts reads afterwards.
|
|
200
200
|
*/
|
|
201
|
-
|
|
201
|
+
const defaultHookRunner = (input) => {
|
|
202
202
|
const result = spawnSync("bash", [input.hookScriptPath], {
|
|
203
203
|
input: input.payload,
|
|
204
204
|
cwd: input.cwd,
|