@indigoai-us/hq-cli 5.345.55 → 5.345.56
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 +12 -0
- package/assets/scaffold/core/scripts/derive-trigger-facts.sh +10 -1
- package/assets/scaffold/core/scripts/jobs-validate.sh +2 -1
- package/assets/scaffold/core/scripts/session-title.sh +18 -15
- package/dist/command-catalog.generated.d.ts +40 -0
- package/dist/command-catalog.generated.js +51 -0
- package/dist/commands/core-delegate-w1b.js +18 -5
- package/dist/commands/core-derive-trigger-facts-command.js +14 -0
- package/dist/commands/core-derive-trigger-facts-native.d.ts +1 -0
- package/dist/commands/core-derive-trigger-facts-native.js +20 -1
- package/dist/commands/doctor.d.ts +1 -0
- package/dist/commands/doctor.js +15 -2
- package/dist/commands/lanes-checks.d.ts +3 -0
- package/dist/commands/lanes-checks.js +34 -0
- package/dist/commands/lanes-session-start.d.ts +2 -7
- package/dist/commands/lanes-session-start.js +9 -29
- package/dist/commands/lanes-shared.d.ts +2 -1
- package/dist/commands/lanes.d.ts +3 -1
- package/dist/commands/lanes.js +117 -16
- package/dist/commands/members.d.ts +4 -0
- package/dist/commands/members.js +23 -2
- package/dist/lanes-drain-hook.js +6 -2
- package/dist/lib/doctor/checks/sync-health.d.ts +2 -0
- package/dist/lib/doctor/checks/sync-health.js +47 -23
- package/dist/lib/lanes/dropbox.d.ts +4 -2
- package/dist/lib/lanes/dropbox.js +54 -9
- package/dist/lib/lanes/flag-keys.d.ts +8 -0
- package/dist/lib/lanes/flag-keys.js +8 -0
- package/dist/lib/lanes/hooks-install.d.ts +14 -11
- package/dist/lib/lanes/hooks-install.js +70 -50
- package/dist/lib/lanes/reaper.d.ts +58 -0
- package/dist/lib/lanes/reaper.js +403 -0
- package/dist/lib/lanes/spawn.d.ts +2 -0
- package/dist/lib/lanes/spawn.js +32 -2
- package/dist/lib/lanes/status-fields.d.ts +12 -0
- package/dist/lib/lanes/status-fields.js +189 -0
- package/dist/lib/lanes/types.js +1 -1
- package/dist/lib/lanes/worker-continuity.d.ts +18 -0
- package/dist/lib/lanes/worker-continuity.js +81 -0
- package/dist/lib/lanes/worker-process.d.ts +4 -0
- package/dist/lib/lanes/worker-process.js +77 -0
- package/dist/main.js +9 -3
- package/dist/utils/cli-telemetry.d.ts +4 -1
- package/dist/utils/cli-telemetry.js +8 -0
- package/dist/utils/jq-windows-compat.d.ts +13 -0
- package/dist/utils/jq-windows-compat.js +48 -0
- package/dist/utils/run-bundled-script.d.ts +3 -0
- package/dist/utils/run-bundled-script.js +9 -80
- package/dist/utils/windows-bash.d.ts +5 -0
- package/dist/utils/windows-bash.js +75 -0
- package/package.json +1 -1
|
@@ -30,6 +30,14 @@ export declare const LEAD_AUTOPILOT_OUTPOST_NATIVE_LEADS_FLAG = "lanes.autopilot
|
|
|
30
30
|
export declare const LANE_ORPHANED_BARE_ID_DROPBOX_REPAIR_FLAG = "lanes.orphaned-bare-id-dropbox-repair";
|
|
31
31
|
/** Allows a Claude lane to leave an exhausted profile account pin for an eligible account. */
|
|
32
32
|
export declare const LANE_PROFILE_PIN_EXHAUSTED_FALLBACK_FLAG = "lanes.profile-pin-exhausted-fallback";
|
|
33
|
+
/** Kill switch for PostToolUse and Stop drop-box delivery. Default on. */
|
|
34
|
+
export declare const LANE_DROPBOX_LIFECYCLE_DELIVERY_FLAG = "lanes.dropbox-lifecycle-delivery";
|
|
35
|
+
/** Kill switch for bounded local status fields in lane list/show JSON. Default on. */
|
|
36
|
+
export declare const LANE_STATUS_ROW_FIELDS_FLAG = "lanes.status-row-fields";
|
|
33
37
|
/** Enables conduct-mode SessionStart bootstrapping (defaults on; operator kill switch). */
|
|
34
38
|
export declare const LANE_CONDUCT_SESSION_START_FLAG = "lanes.conduct-session-start";
|
|
39
|
+
/** Kill switch for stopping local orphan lane process trees; off means report-only. */
|
|
40
|
+
export declare const LANE_REAP_STOP_FLAG = "lanes.reap-stop";
|
|
41
|
+
/** Enables same-senior worker lane continuity on create; keep ON as the kill switch default. */
|
|
42
|
+
export declare const LANE_WORKER_CONTINUITY_FLAG = "lanes.worker-continuity";
|
|
35
43
|
//# sourceMappingURL=flag-keys.d.ts.map
|
|
@@ -30,6 +30,14 @@ export const LEAD_AUTOPILOT_OUTPOST_NATIVE_LEADS_FLAG = "lanes.autopilot-outpost
|
|
|
30
30
|
export const LANE_ORPHANED_BARE_ID_DROPBOX_REPAIR_FLAG = "lanes.orphaned-bare-id-dropbox-repair";
|
|
31
31
|
/** Allows a Claude lane to leave an exhausted profile account pin for an eligible account. */
|
|
32
32
|
export const LANE_PROFILE_PIN_EXHAUSTED_FALLBACK_FLAG = "lanes.profile-pin-exhausted-fallback";
|
|
33
|
+
/** Kill switch for PostToolUse and Stop drop-box delivery. Default on. */
|
|
34
|
+
export const LANE_DROPBOX_LIFECYCLE_DELIVERY_FLAG = "lanes.dropbox-lifecycle-delivery";
|
|
35
|
+
/** Kill switch for bounded local status fields in lane list/show JSON. Default on. */
|
|
36
|
+
export const LANE_STATUS_ROW_FIELDS_FLAG = "lanes.status-row-fields";
|
|
33
37
|
/** Enables conduct-mode SessionStart bootstrapping (defaults on; operator kill switch). */
|
|
34
38
|
export const LANE_CONDUCT_SESSION_START_FLAG = "lanes.conduct-session-start";
|
|
39
|
+
/** Kill switch for stopping local orphan lane process trees; off means report-only. */
|
|
40
|
+
export const LANE_REAP_STOP_FLAG = "lanes.reap-stop";
|
|
41
|
+
/** Enables same-senior worker lane continuity on create; keep ON as the kill switch default. */
|
|
42
|
+
export const LANE_WORKER_CONTINUITY_FLAG = "lanes.worker-continuity";
|
|
35
43
|
//# sourceMappingURL=flag-keys.js.map
|
|
@@ -6,15 +6,18 @@
|
|
|
6
6
|
* registers nothing:
|
|
7
7
|
*
|
|
8
8
|
* - grok reads every `*.json` under `$GROK_HOME/hooks/`, so the registration
|
|
9
|
-
* file in that directory is the registration.
|
|
9
|
+
* file in that directory is the registration. Grok passive events cannot
|
|
10
|
+
* inject model-facing context or block, so its drain stays on PreToolUse.
|
|
10
11
|
* - claude reads `$CLAUDE_CONFIG_DIR/settings.json` and nothing under
|
|
11
|
-
* `hooks/`. The registration is merged into
|
|
12
|
-
* that file.
|
|
13
|
-
* - codex reads `$CODEX_HOME/config.toml` (`[[hooks
|
|
14
|
-
* `[features] hooks = true`). The registration is appended
|
|
12
|
+
* `hooks/`. The registration is merged into PreToolUse, PostToolUse, and
|
|
13
|
+
* Stop arrays in that file.
|
|
14
|
+
* - codex reads `$CODEX_HOME/config.toml` (`[[hooks.<Event>]]`, gated by
|
|
15
|
+
* `[features] hooks = true`). The registration is appended for all three
|
|
16
|
+
* delivery events.
|
|
15
17
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
+
* Claude and Codex register all three delivery events. Grok registers only
|
|
19
|
+
* PreToolUse because its PostToolUse and Stop output cannot reach the model.
|
|
20
|
+
* Registration merges keep existing operator hooks and are idempotent.
|
|
18
21
|
* Never merges into hq-hq-bridge.json / hq-hq-bridge.sh.
|
|
19
22
|
*/
|
|
20
23
|
import type { LaneProvider } from "./types.js";
|
|
@@ -40,9 +43,9 @@ export interface DrainHookInstallResult {
|
|
|
40
43
|
files: string[];
|
|
41
44
|
registration: DrainHookRegistration;
|
|
42
45
|
}
|
|
43
|
-
export declare function drainHookCommand(_shPath?: string): string;
|
|
46
|
+
export declare function drainHookCommand(_shPath?: string, provider?: LaneProvider): string;
|
|
44
47
|
export declare function drainHookShPath(providerHome: string): string;
|
|
45
|
-
export declare function renderDrainHookJson(shPath: string): string;
|
|
48
|
+
export declare function renderDrainHookJson(shPath: string, provider?: LaneProvider): string;
|
|
46
49
|
export interface MergeResult {
|
|
47
50
|
text: string;
|
|
48
51
|
changed: boolean;
|
|
@@ -56,9 +59,9 @@ export interface MergeResult {
|
|
|
56
59
|
export declare function mergeClaudeSettingsHook(existingText: string | null, command: string, settingsPath: string, legacyCommand?: string): MergeResult;
|
|
57
60
|
/**
|
|
58
61
|
* Codex: hooks come from `config.toml` in the codex home. The drain is
|
|
59
|
-
* appended
|
|
62
|
+
* appended to PreToolUse, PostToolUse, and Stop, and `[features] hooks = true`
|
|
60
63
|
* is ensured. Edits are textual so comments and unrelated tables are kept
|
|
61
|
-
* byte for byte; the result is re-parsed and must contain
|
|
64
|
+
* byte for byte; the result is re-parsed and must contain every registration,
|
|
62
65
|
* otherwise nothing is written.
|
|
63
66
|
*/
|
|
64
67
|
export declare function mergeCodexConfigHook(existingText: string | null, command: string, configPath: string, legacyCommand?: string): MergeResult;
|
|
@@ -6,15 +6,18 @@
|
|
|
6
6
|
* registers nothing:
|
|
7
7
|
*
|
|
8
8
|
* - grok reads every `*.json` under `$GROK_HOME/hooks/`, so the registration
|
|
9
|
-
* file in that directory is the registration.
|
|
9
|
+
* file in that directory is the registration. Grok passive events cannot
|
|
10
|
+
* inject model-facing context or block, so its drain stays on PreToolUse.
|
|
10
11
|
* - claude reads `$CLAUDE_CONFIG_DIR/settings.json` and nothing under
|
|
11
|
-
* `hooks/`. The registration is merged into
|
|
12
|
-
* that file.
|
|
13
|
-
* - codex reads `$CODEX_HOME/config.toml` (`[[hooks
|
|
14
|
-
* `[features] hooks = true`). The registration is appended
|
|
12
|
+
* `hooks/`. The registration is merged into PreToolUse, PostToolUse, and
|
|
13
|
+
* Stop arrays in that file.
|
|
14
|
+
* - codex reads `$CODEX_HOME/config.toml` (`[[hooks.<Event>]]`, gated by
|
|
15
|
+
* `[features] hooks = true`). The registration is appended for all three
|
|
16
|
+
* delivery events.
|
|
15
17
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
+
* Claude and Codex register all three delivery events. Grok registers only
|
|
19
|
+
* PreToolUse because its PostToolUse and Stop output cannot reach the model.
|
|
20
|
+
* Registration merges keep existing operator hooks and are idempotent.
|
|
18
21
|
* Never merges into hq-hq-bridge.json / hq-hq-bridge.sh.
|
|
19
22
|
*/
|
|
20
23
|
import * as fs from "node:fs";
|
|
@@ -44,8 +47,9 @@ export const PROVIDER_REGISTRATION_KIND = {
|
|
|
44
47
|
codex: "config_toml",
|
|
45
48
|
grok: "hooks_dir",
|
|
46
49
|
};
|
|
47
|
-
export function drainHookCommand(_shPath) {
|
|
48
|
-
|
|
50
|
+
export function drainHookCommand(_shPath, provider) {
|
|
51
|
+
const providerEnv = provider === "grok" ? "HQ_LANE_PROVIDER=grok " : "";
|
|
52
|
+
return `${providerEnv}${shellQuote(process.execPath)} ${shellQuote(drainHookEntryPath())}`;
|
|
49
53
|
}
|
|
50
54
|
function legacyDrainHookCommand(shPath) {
|
|
51
55
|
return `bash ${shPath}`;
|
|
@@ -148,7 +152,10 @@ function isInstalledDrainEntry(entryPath) {
|
|
|
148
152
|
function drainCommandsMatch(candidate, expected) {
|
|
149
153
|
if (candidate === expected)
|
|
150
154
|
return true;
|
|
151
|
-
const
|
|
155
|
+
const withoutProviderEnv = candidate.startsWith("HQ_LANE_PROVIDER=grok ")
|
|
156
|
+
? candidate.slice("HQ_LANE_PROVIDER=grok ".length)
|
|
157
|
+
: candidate;
|
|
158
|
+
const argv = parseShellQuotedArguments(withoutProviderEnv);
|
|
152
159
|
if (argv === null || argv.length !== 2)
|
|
153
160
|
return false;
|
|
154
161
|
if (path.parse(argv[0]).name.toLowerCase() !== "node")
|
|
@@ -170,7 +177,9 @@ function groupsContainDrainCommand(groups, expected) {
|
|
|
170
177
|
});
|
|
171
178
|
}
|
|
172
179
|
function tomlHasDrainCommand(parsed, expected) {
|
|
173
|
-
|
|
180
|
+
const hooks = isPlainObject(parsed.hooks) ? parsed.hooks : null;
|
|
181
|
+
return hooks !== null &&
|
|
182
|
+
["PreToolUse", "PostToolUse", "Stop"].every((event) => groupsContainDrainCommand(hooks[event], expected));
|
|
174
183
|
}
|
|
175
184
|
function removeCommandFromGroups(groups, command) {
|
|
176
185
|
let changed = false;
|
|
@@ -187,22 +196,14 @@ function removeCommandFromGroups(groups, command) {
|
|
|
187
196
|
});
|
|
188
197
|
return { groups: next, changed };
|
|
189
198
|
}
|
|
190
|
-
export function renderDrainHookJson(shPath) {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
{
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
command: drainHookCommand(shPath),
|
|
199
|
-
timeout: DRAIN_HOOK_TIMEOUT_SECONDS,
|
|
200
|
-
},
|
|
201
|
-
],
|
|
202
|
-
},
|
|
203
|
-
],
|
|
204
|
-
},
|
|
205
|
-
}, null, 2)}\n`;
|
|
199
|
+
export function renderDrainHookJson(shPath, provider = "grok") {
|
|
200
|
+
const command = drainHookCommand(shPath, provider);
|
|
201
|
+
const registration = {
|
|
202
|
+
hooks: Object.fromEntries(["PreToolUse"].map((event) => [event, [
|
|
203
|
+
{ hooks: [{ type: "command", command, timeout: DRAIN_HOOK_TIMEOUT_SECONDS }] },
|
|
204
|
+
]])),
|
|
205
|
+
};
|
|
206
|
+
return `${JSON.stringify(registration, null, 2)}\n`;
|
|
206
207
|
}
|
|
207
208
|
/**
|
|
208
209
|
* Claude Code: `settings.json` is the one file it reads hooks from. Merge the
|
|
@@ -227,27 +228,35 @@ export function mergeClaudeSettingsHook(existingText, command, settingsPath, leg
|
|
|
227
228
|
if (!isPlainObject(hooks)) {
|
|
228
229
|
throw new LanesError("settings_unparseable", `${settingsPath} has a non-object "hooks" key; refusing to rewrite it`, { path: settingsPath });
|
|
229
230
|
}
|
|
230
|
-
const
|
|
231
|
+
const events = ["PreToolUse", "PostToolUse", "Stop"];
|
|
232
|
+
const eventGroups = new Map();
|
|
233
|
+
for (const event of events) {
|
|
234
|
+
const groups = hooks[event] === undefined ? [] : hooks[event];
|
|
235
|
+
if (!Array.isArray(groups)) {
|
|
236
|
+
throw new LanesError("settings_unparseable", `${settingsPath} has a non-array "hooks.${event}"; refusing to rewrite it`, { path: settingsPath });
|
|
237
|
+
}
|
|
238
|
+
eventGroups.set(event, groups);
|
|
239
|
+
}
|
|
240
|
+
const pre = eventGroups.get("PreToolUse");
|
|
231
241
|
if (!Array.isArray(pre)) {
|
|
232
242
|
throw new LanesError("settings_unparseable", `${settingsPath} has a non-array "hooks.PreToolUse"; refusing to rewrite it`, { path: settingsPath });
|
|
233
243
|
}
|
|
234
244
|
const migration = legacyCommand && legacyCommand !== command
|
|
235
245
|
? removeCommandFromGroups(pre, legacyCommand)
|
|
236
246
|
: { groups: pre, changed: false };
|
|
237
|
-
|
|
247
|
+
const allRegistered = events.every((event) => groupsContainCommand(eventGroups.get(event), command));
|
|
248
|
+
if (allRegistered && !migration.changed) {
|
|
238
249
|
return { text: existingText ?? "", changed: false };
|
|
239
250
|
}
|
|
240
|
-
const
|
|
241
|
-
|
|
242
|
-
:
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
{ type: "command", command, timeout: DRAIN_HOOK_TIMEOUT_SECONDS },
|
|
247
|
-
],
|
|
248
|
-
},
|
|
251
|
+
const nextHooks = { ...hooks };
|
|
252
|
+
for (const event of events) {
|
|
253
|
+
const groups = event === "PreToolUse" ? migration.groups : eventGroups.get(event);
|
|
254
|
+
nextHooks[event] = groupsContainCommand(groups, command) ? groups : [
|
|
255
|
+
...groups,
|
|
256
|
+
{ hooks: [{ type: "command", command, timeout: DRAIN_HOOK_TIMEOUT_SECONDS }] },
|
|
249
257
|
];
|
|
250
|
-
|
|
258
|
+
}
|
|
259
|
+
const nextRoot = { ...root, hooks: nextHooks };
|
|
251
260
|
return { text: `${JSON.stringify(nextRoot, null, 2)}\n`, changed: true };
|
|
252
261
|
}
|
|
253
262
|
function tomlBasicString(value) {
|
|
@@ -424,9 +433,9 @@ function enableTomlHooksFeature(text) {
|
|
|
424
433
|
}
|
|
425
434
|
/**
|
|
426
435
|
* Codex: hooks come from `config.toml` in the codex home. The drain is
|
|
427
|
-
* appended
|
|
436
|
+
* appended to PreToolUse, PostToolUse, and Stop, and `[features] hooks = true`
|
|
428
437
|
* is ensured. Edits are textual so comments and unrelated tables are kept
|
|
429
|
-
* byte for byte; the result is re-parsed and must contain
|
|
438
|
+
* byte for byte; the result is re-parsed and must contain every registration,
|
|
430
439
|
* otherwise nothing is written.
|
|
431
440
|
*/
|
|
432
441
|
export function mergeCodexConfigHook(existingText, command, configPath, legacyCommand) {
|
|
@@ -435,7 +444,8 @@ export function mergeCodexConfigHook(existingText, command, configPath, legacyCo
|
|
|
435
444
|
const migration = removeLegacyCodexHook(original, parsed, legacyCommand, configPath);
|
|
436
445
|
const textAfterMigration = migration.text;
|
|
437
446
|
const current = parseTomlOrThrow(textAfterMigration, configPath, "after legacy hook removal");
|
|
438
|
-
const
|
|
447
|
+
const hasPreToolCommand = tomlHasCommand(current, command);
|
|
448
|
+
const hasCommand = tomlHasDrainCommand(current, command);
|
|
439
449
|
const featureOn = tomlHooksFeatureEnabled(current);
|
|
440
450
|
if (hasCommand && featureOn && !migration.changed) {
|
|
441
451
|
return { text: original, changed: false };
|
|
@@ -443,7 +453,7 @@ export function mergeCodexConfigHook(existingText, command, configPath, legacyCo
|
|
|
443
453
|
let text = textAfterMigration;
|
|
444
454
|
if (!featureOn)
|
|
445
455
|
text = enableTomlHooksFeature(text);
|
|
446
|
-
if (!
|
|
456
|
+
if (!hasPreToolCommand) {
|
|
447
457
|
const sep = text === "" || text.endsWith("\n") ? "" : "\n";
|
|
448
458
|
text =
|
|
449
459
|
`${text}${sep}\n` +
|
|
@@ -455,8 +465,18 @@ export function mergeCodexConfigHook(existingText, command, configPath, legacyCo
|
|
|
455
465
|
`command = ${tomlBasicString(command)}\n` +
|
|
456
466
|
`timeout = ${DRAIN_HOOK_TIMEOUT_SECONDS}\n`;
|
|
457
467
|
}
|
|
468
|
+
for (const event of ["PostToolUse", "Stop"]) {
|
|
469
|
+
if (isPlainObject(current.hooks) && groupsContainCommand(current.hooks[event], command))
|
|
470
|
+
continue;
|
|
471
|
+
const sep = text === "" || text.endsWith("\n") ? "" : "\n";
|
|
472
|
+
text += `${sep}\n# hq lanes: drop-box delivery\n[[hooks.${event}]]\nmatcher = ".*"\n\n[[hooks.${event}.hooks]]\ntype = "command"\ncommand = ${tomlBasicString(command)}\ntimeout = ${DRAIN_HOOK_TIMEOUT_SECONDS}\n`;
|
|
473
|
+
}
|
|
458
474
|
const check = parseTomlOrThrow(text, configPath, "after merge");
|
|
459
|
-
if (!tomlHasCommand(check, command) ||
|
|
475
|
+
if (!tomlHasCommand(check, command) ||
|
|
476
|
+
!isPlainObject(check.hooks) ||
|
|
477
|
+
!groupsContainCommand(check.hooks.PostToolUse, command) ||
|
|
478
|
+
!groupsContainCommand(check.hooks.Stop, command) ||
|
|
479
|
+
!tomlHooksFeatureEnabled(check)) {
|
|
460
480
|
throw new LanesError("config_toml_merge_failed", `Merged ${configPath} does not carry the drain registration; refusing to write it`, { path: configPath, command });
|
|
461
481
|
}
|
|
462
482
|
return { text, changed: true };
|
|
@@ -479,7 +499,7 @@ export function probeDrainHookRegistration(provider, providerHome) {
|
|
|
479
499
|
const parsed = parseLaneProvider(provider);
|
|
480
500
|
const kind = PROVIDER_REGISTRATION_KIND[parsed];
|
|
481
501
|
const regPath = drainHookRegistrationPath(parsed, providerHome);
|
|
482
|
-
const command = drainHookCommand(drainHookShPath(providerHome));
|
|
502
|
+
const command = drainHookCommand(drainHookShPath(providerHome), parsed);
|
|
483
503
|
const text = readTextIfExists(regPath);
|
|
484
504
|
const base = { kind, path: regPath, command, file_present: text !== null };
|
|
485
505
|
if (text === null)
|
|
@@ -493,11 +513,11 @@ export function probeDrainHookRegistration(provider, providerHome) {
|
|
|
493
513
|
};
|
|
494
514
|
}
|
|
495
515
|
const json = JSON.parse(text);
|
|
516
|
+
const hooks = isPlainObject(json) && isPlainObject(json.hooks) ? json.hooks : null;
|
|
496
517
|
return {
|
|
497
518
|
...base,
|
|
498
|
-
registered:
|
|
499
|
-
|
|
500
|
-
groupsContainDrainCommand(json.hooks.PreToolUse, command),
|
|
519
|
+
registered: hooks !== null &&
|
|
520
|
+
(parsed === "grok" ? ["PreToolUse"] : ["PreToolUse", "PostToolUse", "Stop"]).every((event) => groupsContainDrainCommand(hooks[event], command)),
|
|
501
521
|
};
|
|
502
522
|
}
|
|
503
523
|
catch (err) {
|
|
@@ -508,9 +528,9 @@ export function probeDrainHookRegistration(provider, providerHome) {
|
|
|
508
528
|
function registerDrainHook(provider, providerHome, shPath) {
|
|
509
529
|
const kind = PROVIDER_REGISTRATION_KIND[provider];
|
|
510
530
|
const regPath = drainHookRegistrationPath(provider, providerHome);
|
|
511
|
-
const command = drainHookCommand(shPath);
|
|
531
|
+
const command = drainHookCommand(shPath, provider);
|
|
512
532
|
if (kind === "hooks_dir") {
|
|
513
|
-
const changed = writeIfChanged(regPath, renderDrainHookJson(shPath), 0o644);
|
|
533
|
+
const changed = writeIfChanged(regPath, renderDrainHookJson(shPath, provider), 0o644);
|
|
514
534
|
return { kind, path: regPath, command, changed };
|
|
515
535
|
}
|
|
516
536
|
const existing = readTextIfExists(regPath);
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { LaneRecord } from "./types.js";
|
|
2
|
+
export declare const DEFAULT_REAP_MIN_AGE_MINUTES = 120;
|
|
3
|
+
export declare const REAP_SINGLE_FLIGHT_LOCK_STALE_MS: number;
|
|
4
|
+
export declare const REAP_RECENT_SWEEP_MS: number;
|
|
5
|
+
export interface ReapProcess {
|
|
6
|
+
pid: number;
|
|
7
|
+
ppid: number;
|
|
8
|
+
args: string;
|
|
9
|
+
}
|
|
10
|
+
export interface ReapFinding {
|
|
11
|
+
lane_id: string;
|
|
12
|
+
orphaned: boolean;
|
|
13
|
+
reason: string;
|
|
14
|
+
age_minutes: number;
|
|
15
|
+
session_id?: string;
|
|
16
|
+
pid?: number;
|
|
17
|
+
process_starttime?: number | null;
|
|
18
|
+
would_stop?: boolean;
|
|
19
|
+
stopped?: boolean;
|
|
20
|
+
stop_complete?: boolean;
|
|
21
|
+
stop_error?: string;
|
|
22
|
+
stale_state?: string[];
|
|
23
|
+
}
|
|
24
|
+
export interface ReapDeps {
|
|
25
|
+
now?: () => Date;
|
|
26
|
+
hostname?: () => string;
|
|
27
|
+
processes?: () => ReapProcess[];
|
|
28
|
+
isAlive?: (pid: number) => boolean;
|
|
29
|
+
readStarttime?: (pid: number) => number | null;
|
|
30
|
+
sessionState?: (root: string, sessionId: string, now: Date) => string | null;
|
|
31
|
+
stopEnabled?: (lane: LaneRecord) => Promise<boolean> | boolean;
|
|
32
|
+
stopLane?: (laneId: string, reason: string) => Promise<void>;
|
|
33
|
+
sleep?: (ms: number) => void;
|
|
34
|
+
}
|
|
35
|
+
export declare function parseReapMinAge(value: string | undefined): number;
|
|
36
|
+
export declare function isProcessRunning(pid: number): boolean;
|
|
37
|
+
export declare function classifyLocalLanes(root: string, now: Date, minAgeMinutes: number, deps?: ReapDeps): Promise<ReapFinding[]>;
|
|
38
|
+
export interface ReapOptions {
|
|
39
|
+
minAgeMinutes: number;
|
|
40
|
+
stop: boolean;
|
|
41
|
+
applyStaleState: boolean;
|
|
42
|
+
reason: string;
|
|
43
|
+
json: boolean;
|
|
44
|
+
}
|
|
45
|
+
export interface ReapSummary {
|
|
46
|
+
ok: true;
|
|
47
|
+
action: "lanes.reap";
|
|
48
|
+
generated_at: string;
|
|
49
|
+
min_age_minutes: number;
|
|
50
|
+
mode: "report" | "apply_stale_state" | "stop";
|
|
51
|
+
stop_enabled: boolean;
|
|
52
|
+
skipped?: "already_running" | "recent_sweep";
|
|
53
|
+
lanes: ReapFinding[];
|
|
54
|
+
}
|
|
55
|
+
export declare function reapLocalLanes(root: string, options: ReapOptions, deps?: ReapDeps): Promise<ReapSummary>;
|
|
56
|
+
/** Internal alias used by session-start hooks; it has no shell or OS-specific dependency. */
|
|
57
|
+
export declare const runSessionStartReap: typeof reapLocalLanes;
|
|
58
|
+
//# sourceMappingURL=reaper.d.ts.map
|