@indigoai-us/hq-cli 5.311.0 → 5.311.2
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/dist/commands/core-delegate-w1b.js +111 -14
- package/dist/commands/core-delegate.d.ts +9 -0
- package/dist/commands/core-delegate.js +44 -23
- package/dist/commands/integrations-core.d.ts +2 -0
- package/dist/commands/integrations-core.js +4 -0
- package/dist/commands/integrations.js +2 -2
- package/dist/commands/lanes-message.js +18 -7
- package/dist/commands/lanes-shared.d.ts +2 -0
- package/dist/commands/lanes-watch.js +1 -5
- package/dist/commands/lanes.js +4 -10
- package/dist/fast-scaffold.d.ts +4 -0
- package/dist/fast-scaffold.js +5 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/lib/bot/runtime/windows.d.ts +2 -0
- package/dist/lib/bot/runtime/windows.js +1 -1
- package/dist/lib/flag-registry.d.ts +2 -0
- package/dist/lib/flag-registry.js +19 -12
- package/dist/lib/lanes/autopilot.js +19 -2
- package/dist/lib/lanes/delivery.d.ts +1 -0
- package/dist/lib/lanes/keep-alive.d.ts +1 -3
- package/dist/lib/lanes/keep-alive.js +2 -4
- package/dist/lib/lanes/reconcile.js +14 -36
- package/dist/lib/lanes/spawn.d.ts +2 -0
- package/dist/lib/lanes/spawn.js +35 -12
- package/dist/lib/lanes/types.d.ts +2 -2
- package/dist/lib/lanes/watch.d.ts +0 -1
- package/dist/lib/lanes/watch.js +4 -9
- package/dist/main.js +1 -1
- package/dist/node-network-compat-core.d.ts +12 -0
- package/dist/node-network-compat-core.js +41 -0
- package/dist/node-network-compat-fast.d.ts +7 -0
- package/dist/node-network-compat-fast.js +23 -0
- package/dist/node-network-compat.d.ts +2 -18
- package/dist/node-network-compat.js +4 -39
- package/dist/node-preflight.d.ts +7 -1
- package/dist/node-preflight.js +5 -5
- package/dist/utils/hq-core-yaml.d.ts +10 -0
- package/dist/utils/hq-core-yaml.js +40 -0
- package/dist/utils/pack-contributions.d.ts +2 -9
- package/dist/utils/pack-contributions.js +1 -39
- package/dist/utils/run-bundled-script.d.ts +6 -0
- package/dist/utils/run-bundled-script.js +14 -3
- package/dist/utils/scaffold-fast-import-guard.js +5 -1
- package/dist/utils/version-report.js +1 -1
- package/package.json +1 -1
|
@@ -8,7 +8,7 @@ function envValue(env, name) {
|
|
|
8
8
|
return key ? env[key] : undefined;
|
|
9
9
|
}
|
|
10
10
|
/** Resolve an npm-installed provider command using Windows PATH and PATHEXT. */
|
|
11
|
-
function resolveWindowsExecutable(name, env, existsSync = fs.existsSync, cwd = process.cwd()) {
|
|
11
|
+
export function resolveWindowsExecutable(name, env, existsSync = fs.existsSync, cwd = process.cwd()) {
|
|
12
12
|
const pathValue = envValue(env, "PATH") ?? "";
|
|
13
13
|
const rawExtensions = (envValue(env, "PATHEXT") || DEFAULT_PATHEXT)
|
|
14
14
|
.split(";")
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { type WorkerOptions } from "node:worker_threads";
|
|
9
9
|
import { createFlagClient, type FlagClient, type FlagLookupOptions, type FlagSnapshot } from "@indigoai-us/hq-flags-client";
|
|
10
|
+
type FlagRegistrySentry = Pick<typeof import("@sentry/node"), "captureException" | "withScope">;
|
|
10
11
|
export { resolveProcessFlagCompanyContext } from "./process-flag-company-context.js";
|
|
11
12
|
export type { ProcessFlagCompanyContext } from "./process-flag-company-context.js";
|
|
12
13
|
export { FLAG_REGISTRY_ENDPOINT_ENV } from "./flag-registry-endpoint.js";
|
|
@@ -20,6 +21,7 @@ interface WorkerLike {
|
|
|
20
21
|
export interface FlagRegistryDependencies {
|
|
21
22
|
endpoint?: string;
|
|
22
23
|
createWorker?: (filename: string | URL, options: WorkerOptions) => WorkerLike;
|
|
24
|
+
sentry?: FlagRegistrySentry;
|
|
23
25
|
/** Test seam for exercising the no-source fail-open path. */
|
|
24
26
|
workerSource?: string;
|
|
25
27
|
captureWorkerFailure?: (error: Error, tags: Record<string, string>) => void;
|
|
@@ -6,12 +6,16 @@
|
|
|
6
6
|
* keep a short-lived, offline CLI alive until the client's network deadline.
|
|
7
7
|
*/
|
|
8
8
|
import { Worker } from "node:worker_threads";
|
|
9
|
-
import * as Sentry from "@sentry/node";
|
|
10
9
|
import { createFlagClient, isFlagSnapshot, readLocalOverride, resolveFlag, } from "@indigoai-us/hq-flags-client";
|
|
11
10
|
import { resolveFlagRegistryEndpoint, } from "./flag-registry-endpoint.js";
|
|
12
11
|
import { FLAG_REGISTRY_WORKER_SOURCE } from "./flag-registry-worker-source.generated.js";
|
|
13
12
|
import { resolveProcessFlagCompanyContext } from "./process-flag-company-context.js";
|
|
14
13
|
import { writeFlagSnapshotFile } from "./flag-snapshot-file.js";
|
|
14
|
+
function captureFlagRegistryException(error, tags, sentry) {
|
|
15
|
+
if (!sentry)
|
|
16
|
+
return;
|
|
17
|
+
sentry.captureException(error, { tags });
|
|
18
|
+
}
|
|
15
19
|
export { resolveProcessFlagCompanyContext } from "./process-flag-company-context.js";
|
|
16
20
|
export { FLAG_REGISTRY_ENDPOINT_ENV } from "./flag-registry-endpoint.js";
|
|
17
21
|
const COMPANY_UID_RE = /^cmp_[A-Za-z0-9]{3,128}$/;
|
|
@@ -58,6 +62,7 @@ export function kickFlagRegistryReadiness(dependencies = {}) {
|
|
|
58
62
|
return processReader;
|
|
59
63
|
const endpoint = resolveFlagRegistryEndpoint(dependencies.endpoint, process.env);
|
|
60
64
|
const company = resolveProcessFlagCompanyContext();
|
|
65
|
+
const sentry = dependencies.sentry;
|
|
61
66
|
const companyUid = company.companyUid;
|
|
62
67
|
const companyIdentifiers = [companyUid, company.companySlug].filter((value) => Boolean(value));
|
|
63
68
|
processReader = processSnapshotReader(companyIdentifiers);
|
|
@@ -65,7 +70,7 @@ export function kickFlagRegistryReadiness(dependencies = {}) {
|
|
|
65
70
|
const embeddedSource = dependencies.workerSource ?? FLAG_REGISTRY_WORKER_SOURCE;
|
|
66
71
|
if (embeddedSource.length === 0) {
|
|
67
72
|
(dependencies.captureWorkerFailure ?? ((error, tags) => {
|
|
68
|
-
|
|
73
|
+
captureFlagRegistryException(error, tags, sentry);
|
|
69
74
|
}))(new Error("Self-contained flag-registry worker source is unavailable"), {
|
|
70
75
|
component: "flag-registry-worker-startup",
|
|
71
76
|
worker: "flag-registry",
|
|
@@ -87,10 +92,14 @@ export function kickFlagRegistryReadiness(dependencies = {}) {
|
|
|
87
92
|
// its process-wide captureWorkerErrors option is disabled. Scope only this
|
|
88
93
|
// worker so the reporting boundary can classify its startup errors without
|
|
89
94
|
// changing capture for unrelated workers.
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
95
|
+
const createWorker = dependencies.createWorker ??
|
|
96
|
+
((filename, options) => new Worker(filename, options));
|
|
97
|
+
const worker = sentry
|
|
98
|
+
? sentry.withScope((scope) => {
|
|
99
|
+
scope.setTag("worker", "flag-registry");
|
|
100
|
+
return createWorker(embeddedSource, workerOptions);
|
|
101
|
+
})
|
|
102
|
+
: createWorker(embeddedSource, workerOptions);
|
|
94
103
|
worker.on("message", (value) => {
|
|
95
104
|
if (isFlagSnapshot(value)) {
|
|
96
105
|
heldSnapshot = value;
|
|
@@ -99,12 +108,10 @@ export function kickFlagRegistryReadiness(dependencies = {}) {
|
|
|
99
108
|
writeFlagSnapshotFile(companyUid, value.flags);
|
|
100
109
|
}
|
|
101
110
|
catch (error) {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
},
|
|
107
|
-
});
|
|
111
|
+
captureFlagRegistryException(error instanceof Error ? error : new Error(String(error)), {
|
|
112
|
+
component: "flag-snapshot-cache-write",
|
|
113
|
+
company_uid_present: "true",
|
|
114
|
+
}, sentry);
|
|
108
115
|
}
|
|
109
116
|
}
|
|
110
117
|
}
|
|
@@ -154,7 +154,7 @@ function projectStories(hqRoot, relativePath, companySlug, projectId) {
|
|
|
154
154
|
status,
|
|
155
155
|
...(story.passes === true ? { passes: true } : {}),
|
|
156
156
|
...(briefPath ? { briefPath } : {}),
|
|
157
|
-
...(
|
|
157
|
+
...storyLaneIds(story),
|
|
158
158
|
...(typeof story.updated_at === "string" ? { updatedAt: story.updated_at } : {}),
|
|
159
159
|
...(typeof story.external_blocker === "string" ? { externalBlocker: story.external_blocker } : {}),
|
|
160
160
|
};
|
|
@@ -207,6 +207,18 @@ function parseOkrReports(status) {
|
|
|
207
207
|
return { at: value.at, kpis: parseLines(value.kpis, "KPI"), okrs: parseLines(value.okrs, "OKR") };
|
|
208
208
|
}).sort((a, b) => a.at.localeCompare(b.at));
|
|
209
209
|
}
|
|
210
|
+
// A story's current `lane` counts alongside its `lanes` history, so `lanes: []` with `lane: X` is assigned to X.
|
|
211
|
+
function storyLaneIds(story) {
|
|
212
|
+
const history = Array.isArray(story.lanes)
|
|
213
|
+
? story.lanes.map((lane) => typeof lane === "string" ? lane : lane.lane_id)
|
|
214
|
+
: undefined;
|
|
215
|
+
const current = typeof story.lane === "string" && story.lane.trim() ? story.lane : undefined;
|
|
216
|
+
if (history)
|
|
217
|
+
return { laneIds: current && !history.includes(current) ? [...history, current] : history };
|
|
218
|
+
if (current)
|
|
219
|
+
return { laneIds: [current] };
|
|
220
|
+
return story.lane === "" ? { laneIds: [] } : {};
|
|
221
|
+
}
|
|
210
222
|
function statusBlockers(status) {
|
|
211
223
|
const raw = status.blockers;
|
|
212
224
|
if (raw === undefined)
|
|
@@ -256,7 +268,9 @@ export function planLeadAutopilotWakes(input) {
|
|
|
256
268
|
const finishedMs = Date.parse(story.updatedAt);
|
|
257
269
|
if (!Number.isFinite(finishedMs) || now.getTime() - finishedMs < LEAD_AUTOPILOT_REVIEW_DELAY_MS)
|
|
258
270
|
return [];
|
|
259
|
-
|
|
271
|
+
// The lead's own lane is never a finished worker of itself.
|
|
272
|
+
return story.laneIds.filter((laneId) => laneId !== lead.lane_id && !knownChildIds.has(laneId))
|
|
273
|
+
.map((laneId) => ({ laneId, story, finishedAt: story.updatedAt }));
|
|
260
274
|
});
|
|
261
275
|
const overdueReviews = [...finishedReviews, ...missingReviews].sort((a, b) => a.laneId.localeCompare(b.laneId));
|
|
262
276
|
if (overdueReviews.length > 0) {
|
|
@@ -351,6 +365,9 @@ export function planLeadAutopilotWakes(input) {
|
|
|
351
365
|
if (story.status !== "in_progress")
|
|
352
366
|
return [];
|
|
353
367
|
const laneIds = story.laneIds ?? [];
|
|
368
|
+
// A story assigned to the lead itself is worked by the lead, which is the lane being woken.
|
|
369
|
+
if (laneIds.includes(lead.lane_id))
|
|
370
|
+
return [];
|
|
354
371
|
const live = laneIds.filter((laneId) => children.some((child) => child.lane_id === laneId && LIVE_STATES.has(child.state)));
|
|
355
372
|
if (live.length > 0)
|
|
356
373
|
return [];
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { type LaneRecord, type ManagerStatusGateHistoryEntry, type QuestionRecord } from "./types.js";
|
|
2
2
|
export declare const LEAD_LANE_KEEP_ALIVE_FLAG = "lanes.lead-keep-alive";
|
|
3
3
|
export declare const LEAD_LANE_KEEP_ALIVE_DEFAULT_ENABLED = false;
|
|
4
|
-
export declare const LEAD_LANE_KEEP_ALIVE_NEXT_CHECK_FLAG = "lanes.keep-alive-next-check-at";
|
|
5
|
-
export declare const LEAD_LANE_KEEP_ALIVE_NEXT_CHECK_DEFAULT_ENABLED = false;
|
|
6
4
|
export declare const LEAD_LANE_KEEP_ALIVE_NEXT_CHECK_MAX_DELAY_MS: number;
|
|
7
5
|
export declare const LEAD_LANE_KEEP_ALIVE_RESUME_BLOCKED_FLAG = "lanes.keep-alive-resume-blocked";
|
|
8
6
|
export declare const LEAD_LANE_KEEP_ALIVE_RESUME_BLOCKED_DEFAULT_ENABLED = false;
|
|
@@ -30,6 +28,7 @@ export declare function queueLaneTerminalResume(current: LaneRecord, at: string,
|
|
|
30
28
|
allowResume?: boolean;
|
|
31
29
|
mergeExisting?: boolean;
|
|
32
30
|
allowBlockedResume?: boolean;
|
|
31
|
+
trigger?: "resume" | "watcher";
|
|
33
32
|
}): LaneTerminalResumeResult;
|
|
34
33
|
/** Persist a manager status miss and queue its one bounded follow-up turn. */
|
|
35
34
|
export declare function queueManagerStatusGateResume(hqRoot: string, laneId: string, entry: Omit<ManagerStatusGateHistoryEntry, "event" | "resume_queued" | "resume_skipped_reason">, resumeText: string): LaneRecord;
|
|
@@ -47,7 +46,6 @@ export type DoneKeepAliveVerdict = "eligible" | "standing_idle" | "story_complet
|
|
|
47
46
|
export declare function keepAliveNextCheckAtMs(value: unknown, nowMs: number, anchorMs?: number): number | undefined;
|
|
48
47
|
/** Shared done-state policy used by watch, reconcile, and the show command. */
|
|
49
48
|
export declare function doneKeepAliveVerdict(lane: Pick<LaneRecord, "keep_alive_standing">, envelope: KeepAliveDoneEnvelope, hasQueuedWork: boolean, options?: {
|
|
50
|
-
nextCheckAtEnabled?: boolean;
|
|
51
49
|
hasPendingWork?: boolean;
|
|
52
50
|
nowMs?: number;
|
|
53
51
|
nextCheckAnchorMs?: number;
|
|
@@ -6,8 +6,6 @@ import { appendCappedHistory, listQuestionIds, readQuestion, retryUpdateLane } f
|
|
|
6
6
|
import { isTerminalLaneState, isIsoDateTime, } from "./types.js";
|
|
7
7
|
export const LEAD_LANE_KEEP_ALIVE_FLAG = "lanes.lead-keep-alive";
|
|
8
8
|
export const LEAD_LANE_KEEP_ALIVE_DEFAULT_ENABLED = false;
|
|
9
|
-
export const LEAD_LANE_KEEP_ALIVE_NEXT_CHECK_FLAG = "lanes.keep-alive-next-check-at";
|
|
10
|
-
export const LEAD_LANE_KEEP_ALIVE_NEXT_CHECK_DEFAULT_ENABLED = false;
|
|
11
9
|
export const LEAD_LANE_KEEP_ALIVE_NEXT_CHECK_MAX_DELAY_MS = 24 * 60 * 60 * 1000;
|
|
12
10
|
export const LEAD_LANE_KEEP_ALIVE_RESUME_BLOCKED_FLAG = "lanes.keep-alive-resume-blocked";
|
|
13
11
|
export const LEAD_LANE_KEEP_ALIVE_RESUME_BLOCKED_DEFAULT_ENABLED = false;
|
|
@@ -63,7 +61,7 @@ export function queueLaneTerminalResume(current, at, resumeText, options = {}) {
|
|
|
63
61
|
terminal_details: undefined,
|
|
64
62
|
pending_resume: {
|
|
65
63
|
session_id: sessionId,
|
|
66
|
-
trigger: "
|
|
64
|
+
trigger: options.trigger ?? "watcher",
|
|
67
65
|
resumed_from: current.state,
|
|
68
66
|
text: resumeText,
|
|
69
67
|
compact: true,
|
|
@@ -126,7 +124,7 @@ export function doneKeepAliveVerdict(lane, envelope, hasQueuedWork, options = {}
|
|
|
126
124
|
verdict = "story_complete";
|
|
127
125
|
else
|
|
128
126
|
verdict = hasQueuedWork ? "eligible" : "standing_idle";
|
|
129
|
-
if (verdict === "eligible" && options.
|
|
127
|
+
if (verdict === "eligible" && options.hasPendingWork !== true) {
|
|
130
128
|
const nowMs = options.nowMs ?? Date.now();
|
|
131
129
|
const anchorMs = options.nextCheckAnchorMs ?? nowMs;
|
|
132
130
|
const nextCheckAtMs = keepAliveNextCheckAtMs(envelope.next_check_at, nowMs, anchorMs);
|
|
@@ -25,7 +25,7 @@ import { readLanesRegistryFlag } from "./registry-flag.js";
|
|
|
25
25
|
import { resolveLaneCompany } from "./lane-company.js";
|
|
26
26
|
import { appendDropboxMessage } from "./dropbox.js";
|
|
27
27
|
import { DEFAULT_RUNAWAY_SEARCH_GUARD_CONFIG, listHostProcesses, runRunawayToolGuard, RUNAWAY_TOOL_GUARD_FLAG, } from "./runaway-tool-guard.js";
|
|
28
|
-
import { LEAD_LANE_KEEP_ALIVE_FLAG,
|
|
28
|
+
import { LEAD_LANE_KEEP_ALIVE_FLAG, LEAD_LANE_KEEP_ALIVE_RESUME_BLOCKED_FLAG, LEAD_LANE_KEEP_ALIVE_IMMEDIATE_FIRST_RESUME_FLAG, LEAD_LANE_KEEP_ALIVE_FIRST_RESUME_SETTLE_MS, LEAD_LANE_KEEP_ALIVE_BLOCKED_GRACE_MS, LEAD_LANE_KEEP_ALIVE_BLOCKED_RESUME_TEXT, queueLaneTerminalResume, LEAD_LANE_KEEP_ALIVE_INTERRUPTED_RESUME_TEXT, LEAD_LANE_KEEP_ALIVE_MAX_RESUMES, LEAD_LANE_KEEP_ALIVE_RESUME_TEXT, LANE_SUPERSESSION_LINEAGE_AWARE_FLAG, doneKeepAliveVerdict, hasPendingKeepAliveWork, keepAliveExclusion, loadRetiredLaneIds, leadLaneKeepAliveBackoffMs, undeliveredAnsweredQuestions, } from "./keep-alive.js";
|
|
29
29
|
import { providerSupportsSessionResume } from "./delivery.js";
|
|
30
30
|
import { applyDefaultQuotaHold, clearAccountQuotaAfterSuccessfulTurnBestEffort, readQuotaSignal, recordAccountQuota, } from "./quota.js";
|
|
31
31
|
import { answeredQuestionEntries, questionRecordRefusalCode, raiseEnvelopeQuestions, resolveQuestionCompanyWithFallback, } from "./questions.js";
|
|
@@ -1087,7 +1087,6 @@ export async function runPassBody(hqRoot, deps) {
|
|
|
1087
1087
|
}
|
|
1088
1088
|
}));
|
|
1089
1089
|
const keepAliveFlagCompanies = new Map();
|
|
1090
|
-
const nextCheckAtFlagCompanyKeys = new Set();
|
|
1091
1090
|
for (const lane of lanes) {
|
|
1092
1091
|
if (!(lane.state === "done" || lane.state === "interrupted" || lane.state === "blocked" || lane.state === "killed" || lane.state === "cancelled" || lane.state === "failed") ||
|
|
1093
1092
|
(lane.keep_alive !== true && lane.worker_profile === undefined) ||
|
|
@@ -1098,26 +1097,25 @@ export async function runPassBody(hqRoot, deps) {
|
|
|
1098
1097
|
!providerSupportsSessionResume(lane.provider) ||
|
|
1099
1098
|
!(lane.provider_session_id ?? lane.last_resume?.session_id))))
|
|
1100
1099
|
continue;
|
|
1101
|
-
let hasNextCheckAt = false;
|
|
1102
1100
|
if (lane.state === "done") {
|
|
1103
1101
|
const envelope = readLatestEnvelopeRecord(hqRoot, lane.lane_id)?.envelope;
|
|
1104
1102
|
if (!envelope || envelope.decision !== "done")
|
|
1105
1103
|
continue;
|
|
1106
1104
|
const hasQueuedWork = lane.keep_alive_standing === true && hasPendingKeepAliveWork(hqRoot, lane, questions);
|
|
1107
|
-
if (doneKeepAliveVerdict(lane, envelope, hasQueuedWork
|
|
1105
|
+
if (doneKeepAliveVerdict(lane, envelope, hasQueuedWork, {
|
|
1106
|
+
hasPendingWork: hasPendingKeepAliveWork(hqRoot, lane, questions),
|
|
1107
|
+
nowMs: now.getTime(),
|
|
1108
|
+
nextCheckAnchorMs: Date.parse(lane.last_envelope?.at ?? lane.updated_at),
|
|
1109
|
+
}) !== "eligible")
|
|
1108
1110
|
continue;
|
|
1109
|
-
hasNextCheckAt = envelope.next_check_at !== undefined;
|
|
1110
1111
|
}
|
|
1111
1112
|
const company = resolveLaneCompany(hqRoot, lane).company;
|
|
1112
1113
|
if (company === null)
|
|
1113
1114
|
continue;
|
|
1114
1115
|
const key = keepAliveCompanyKey(company);
|
|
1115
1116
|
keepAliveFlagCompanies.set(key, company);
|
|
1116
|
-
if (hasNextCheckAt)
|
|
1117
|
-
nextCheckAtFlagCompanyKeys.add(key);
|
|
1118
1117
|
}
|
|
1119
1118
|
const keepAliveFlagByCompany = new Map();
|
|
1120
|
-
const nextCheckAtFlagByCompany = new Map();
|
|
1121
1119
|
const blockedResumeFlagByCompany = new Map();
|
|
1122
1120
|
const immediateFirstResumeFlagByCompany = new Map();
|
|
1123
1121
|
const supersessionLineageAwareByCompany = new Map();
|
|
@@ -1130,11 +1128,8 @@ export async function runPassBody(hqRoot, deps) {
|
|
|
1130
1128
|
lane.keep_alive === true &&
|
|
1131
1129
|
laneRunsHere(lane, startDeps.host) &&
|
|
1132
1130
|
keepAliveCompanyKey(resolveLaneCompany(hqRoot, lane).company) === key);
|
|
1133
|
-
const [keepAliveEnabled,
|
|
1131
|
+
const [keepAliveEnabled, lineageAware, blockedResumeEnabled, immediateFirstResumeEnabled] = await Promise.all([
|
|
1134
1132
|
readRegistryFlag(LEAD_LANE_KEEP_ALIVE_FLAG, options),
|
|
1135
|
-
nextCheckAtFlagCompanyKeys.has(key)
|
|
1136
|
-
? readRegistryFlag(LEAD_LANE_KEEP_ALIVE_NEXT_CHECK_FLAG, options)
|
|
1137
|
-
: Promise.resolve(false),
|
|
1138
1133
|
readRegistryFlag(LANE_SUPERSESSION_LINEAGE_AWARE_FLAG, options),
|
|
1139
1134
|
hasBlockedKeepAliveLane
|
|
1140
1135
|
? readRegistryFlag(LEAD_LANE_KEEP_ALIVE_RESUME_BLOCKED_FLAG, options)
|
|
@@ -1142,7 +1137,6 @@ export async function runPassBody(hqRoot, deps) {
|
|
|
1142
1137
|
readRegistryFlag(LEAD_LANE_KEEP_ALIVE_IMMEDIATE_FIRST_RESUME_FLAG, options),
|
|
1143
1138
|
]);
|
|
1144
1139
|
keepAliveFlagByCompany.set(key, keepAliveEnabled);
|
|
1145
|
-
nextCheckAtFlagByCompany.set(key, nextCheckAtEnabled);
|
|
1146
1140
|
blockedResumeFlagByCompany.set(key, blockedResumeEnabled);
|
|
1147
1141
|
immediateFirstResumeFlagByCompany.set(key, immediateFirstResumeEnabled);
|
|
1148
1142
|
supersessionLineageAwareByCompany.set(key, lineageAware);
|
|
@@ -1165,7 +1159,7 @@ export async function runPassBody(hqRoot, deps) {
|
|
|
1165
1159
|
const freshLanes = loadLaneRecords(hqRoot, summary);
|
|
1166
1160
|
const freshQuestions = loadQuestionRecords(hqRoot, summary);
|
|
1167
1161
|
await closeInterruptedDoneStoryWorkers(hqRoot, freshLanes, freshQuestions, summary, admissionDeps, now, storyCloseEnabledByCompany);
|
|
1168
|
-
await resumeKeepAlive(hqRoot, freshLanes, freshQuestions, summary, admissionDeps, now, keepAliveFlagByCompany, supersessionLineageAwareByCompany, blockedResumeFlagByCompany,
|
|
1162
|
+
await resumeKeepAlive(hqRoot, freshLanes, freshQuestions, summary, admissionDeps, now, keepAliveFlagByCompany, supersessionLineageAwareByCompany, blockedResumeFlagByCompany, immediateFirstResumeFlagByCompany);
|
|
1169
1163
|
const drainLanes = loadLaneRecords(hqRoot, summary);
|
|
1170
1164
|
await admitQueued(hqRoot, drainLanes, summary, admissionDeps, now);
|
|
1171
1165
|
await resumeAwaiting(hqRoot, drainLanes, freshQuestions, summary, admissionDeps);
|
|
@@ -2188,7 +2182,7 @@ async function resumeBlockedKeepAlive(hqRoot, lane, questions, summary, deps, no
|
|
|
2188
2182
|
current = afterPreparation;
|
|
2189
2183
|
}
|
|
2190
2184
|
const expectedVersion = current.version;
|
|
2191
|
-
const result = queueLaneTerminalResume(current, now.toISOString(), LEAD_LANE_KEEP_ALIVE_BLOCKED_RESUME_TEXT, { allowBlockedResume: true });
|
|
2185
|
+
const result = queueLaneTerminalResume(current, now.toISOString(), LEAD_LANE_KEEP_ALIVE_BLOCKED_RESUME_TEXT, { allowBlockedResume: true, trigger: "resume" });
|
|
2192
2186
|
if (!result.resumeQueued) {
|
|
2193
2187
|
if (result.resumeSkippedReason !== undefined) {
|
|
2194
2188
|
summary.skipped.push({ kind: "lane", id: lane.lane_id, why: "keep_alive_" + result.resumeSkippedReason });
|
|
@@ -2244,7 +2238,7 @@ function appendBlockedKeepAliveHistory(hqRoot, lane, terminalAt, at, reason, dep
|
|
|
2244
2238
|
}),
|
|
2245
2239
|
}), false);
|
|
2246
2240
|
}
|
|
2247
|
-
async function resumeKeepAlive(hqRoot, lanes, questions, summary, deps, now, flagByCompany, supersessionLineageAwareByCompany, blockedResumeFlagByCompany,
|
|
2241
|
+
async function resumeKeepAlive(hqRoot, lanes, questions, summary, deps, now, flagByCompany, supersessionLineageAwareByCompany, blockedResumeFlagByCompany, immediateFirstResumeFlagByCompany) {
|
|
2248
2242
|
const nowMs = now.getTime();
|
|
2249
2243
|
const nowIso = now.toISOString();
|
|
2250
2244
|
const config = loadLanesConfig(hqRoot);
|
|
@@ -2291,13 +2285,9 @@ async function resumeKeepAlive(hqRoot, lanes, questions, summary, deps, now, fla
|
|
|
2291
2285
|
const envelope = readLatestEnvelopeRecord(hqRoot, lane.lane_id)?.envelope;
|
|
2292
2286
|
if (!envelope || envelope.decision !== "done")
|
|
2293
2287
|
continue;
|
|
2294
|
-
const
|
|
2295
|
-
const hasPendingWork = lane.keep_alive_standing === true || nextCheckAtEnabled
|
|
2296
|
-
? hasPendingKeepAliveWork(hqRoot, lane, questions)
|
|
2297
|
-
: false;
|
|
2288
|
+
const hasPendingWork = hasPendingKeepAliveWork(hqRoot, lane, questions);
|
|
2298
2289
|
const hasQueuedWork = lane.keep_alive_standing === true && hasPendingWork;
|
|
2299
2290
|
const verdict = doneKeepAliveVerdict(lane, envelope, hasQueuedWork, {
|
|
2300
|
-
nextCheckAtEnabled,
|
|
2301
2291
|
hasPendingWork,
|
|
2302
2292
|
nowMs,
|
|
2303
2293
|
nextCheckAnchorMs: Date.parse(lane.last_envelope?.at ?? lane.updated_at),
|
|
@@ -2415,13 +2405,9 @@ async function resumeKeepAlive(hqRoot, lanes, questions, summary, deps, now, fla
|
|
|
2415
2405
|
const latest = readLatestEnvelopeRecord(hqRoot, lane.lane_id);
|
|
2416
2406
|
if (!latest || latest.envelope.decision !== "done")
|
|
2417
2407
|
continue;
|
|
2418
|
-
const
|
|
2419
|
-
const hasPendingWork = lane.keep_alive_standing === true || nextCheckAtEnabled
|
|
2420
|
-
? hasPendingKeepAliveWork(hqRoot, lane, questions)
|
|
2421
|
-
: false;
|
|
2408
|
+
const hasPendingWork = hasPendingKeepAliveWork(hqRoot, lane, questions);
|
|
2422
2409
|
const hasQueuedWork = lane.keep_alive_standing === true && hasPendingWork;
|
|
2423
2410
|
const verdict = doneKeepAliveVerdict(lane, latest.envelope, hasQueuedWork, {
|
|
2424
|
-
nextCheckAtEnabled,
|
|
2425
2411
|
hasPendingWork,
|
|
2426
2412
|
nowMs,
|
|
2427
2413
|
nextCheckAnchorMs: Date.parse(lane.last_envelope?.at ?? lane.updated_at),
|
|
@@ -2507,13 +2493,9 @@ async function resumeKeepAlive(hqRoot, lanes, questions, summary, deps, now, fla
|
|
|
2507
2493
|
const currentEnvelope = readLatestEnvelopeRecord(hqRoot, current.lane_id);
|
|
2508
2494
|
if (!currentEnvelope || currentEnvelope.envelope.decision !== "done")
|
|
2509
2495
|
continue;
|
|
2510
|
-
const
|
|
2511
|
-
const hasPendingWork = current.keep_alive_standing === true || nextCheckAtEnabled
|
|
2512
|
-
? hasPendingKeepAliveWork(hqRoot, current, questions)
|
|
2513
|
-
: false;
|
|
2496
|
+
const hasPendingWork = hasPendingKeepAliveWork(hqRoot, current, questions);
|
|
2514
2497
|
const hasQueuedWork = current.keep_alive_standing === true && hasPendingWork;
|
|
2515
2498
|
const verdict = doneKeepAliveVerdict(current, currentEnvelope.envelope, hasQueuedWork, {
|
|
2516
|
-
nextCheckAtEnabled,
|
|
2517
2499
|
hasPendingWork,
|
|
2518
2500
|
nowMs,
|
|
2519
2501
|
nextCheckAnchorMs: Date.parse(current.last_envelope?.at ?? current.updated_at),
|
|
@@ -2794,12 +2776,8 @@ async function resumeKeepAlive(hqRoot, lanes, questions, summary, deps, now, fla
|
|
|
2794
2776
|
const envelope = readLatestEnvelopeRecord(hqRoot, latest.lane_id);
|
|
2795
2777
|
if (!envelope || envelope.envelope.decision !== "done")
|
|
2796
2778
|
continue;
|
|
2797
|
-
const
|
|
2798
|
-
const hasPendingWork = latest.keep_alive_standing === true || nextCheckAtEnabled
|
|
2799
|
-
? hasPendingKeepAliveWork(hqRoot, latest, questions)
|
|
2800
|
-
: false;
|
|
2779
|
+
const hasPendingWork = hasPendingKeepAliveWork(hqRoot, latest, questions);
|
|
2801
2780
|
const finalDoneVerdict = doneKeepAliveVerdict(latest, envelope.envelope, latest.keep_alive_standing === true && hasPendingWork, {
|
|
2802
|
-
nextCheckAtEnabled,
|
|
2803
2781
|
hasPendingWork,
|
|
2804
2782
|
nowMs,
|
|
2805
2783
|
nextCheckAnchorMs: Date.parse(latest.last_envelope?.at ?? latest.updated_at),
|
|
@@ -198,6 +198,8 @@ export declare function resumePrompt(input: {
|
|
|
198
198
|
answers?: AnsweredQuestionLine[];
|
|
199
199
|
/** `hq lanes resume`: the state the operator resumed the lane from. */
|
|
200
200
|
resumedFrom?: string;
|
|
201
|
+
/** Automatic continuation initiated by the lane watcher. */
|
|
202
|
+
resumeSource?: "watcher";
|
|
201
203
|
/** Exact validator errors recorded after the prior envelope repair failed. */
|
|
202
204
|
envelopeRepairErrors?: {
|
|
203
205
|
first_error: string;
|
package/dist/lib/lanes/spawn.js
CHANGED
|
@@ -372,7 +372,18 @@ function pendingResumeForRetry(hqRoot, lane, sessionId, at) {
|
|
|
372
372
|
if (last.trigger === "answered_questions") {
|
|
373
373
|
return { session_id: sessionId, trigger: "answered_questions", at, ...compact, ...keepAlive };
|
|
374
374
|
}
|
|
375
|
-
if (last.trigger === "
|
|
375
|
+
if (last.trigger === "watcher") {
|
|
376
|
+
return {
|
|
377
|
+
session_id: sessionId,
|
|
378
|
+
trigger: "watcher",
|
|
379
|
+
resumed_from: last.resumed_from ?? "interrupted",
|
|
380
|
+
...(last.text !== undefined ? { text: last.text } : {}),
|
|
381
|
+
at,
|
|
382
|
+
...compact,
|
|
383
|
+
...keepAlive,
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
if (last.trigger === "message" || last.trigger === "monitor_event" || last.trigger === "system_notice") {
|
|
376
387
|
if (last.message_id === undefined) {
|
|
377
388
|
throw new LanesError("account_retry_context_missing", `The message id needed to resume lane ${lane.lane_id} is missing`, { lane_id: lane.lane_id, trigger: last.trigger });
|
|
378
389
|
}
|
|
@@ -382,9 +393,10 @@ function pendingResumeForRetry(hqRoot, lane, sessionId, at) {
|
|
|
382
393
|
}
|
|
383
394
|
return {
|
|
384
395
|
session_id: sessionId,
|
|
385
|
-
trigger: "message",
|
|
396
|
+
trigger: last.trigger === "message" && last.monitor_event === true ? "monitor_event" : last.trigger,
|
|
386
397
|
text,
|
|
387
398
|
message_id: last.message_id,
|
|
399
|
+
...(last.monitor_event === true || last.trigger === "monitor_event" ? { monitor_event: true } : {}),
|
|
388
400
|
at,
|
|
389
401
|
...compact,
|
|
390
402
|
...keepAlive,
|
|
@@ -1071,7 +1083,7 @@ export function readAnsweredQuestionLines(answersPath) {
|
|
|
1071
1083
|
export function resumePrompt(input) {
|
|
1072
1084
|
const parts = [];
|
|
1073
1085
|
if (input.resumedFrom !== undefined) {
|
|
1074
|
-
parts.push(
|
|
1086
|
+
parts.push(`${input.resumeSource === "watcher" ? "The lane watcher" : "The operator"} resumed this lane from state ${input.resumedFrom}. ` +
|
|
1075
1087
|
(input.resumedFrom === "interrupted"
|
|
1076
1088
|
? "The previous run ended without writing its envelope; continue the work from where it stopped."
|
|
1077
1089
|
: "Continue the work from where the previous run stopped."));
|
|
@@ -1743,7 +1755,7 @@ export function spawnLaneWorker(hqRoot, lane, deps = {}, launch = {}) {
|
|
|
1743
1755
|
const profileChanged = profile !== undefined && existing.worker_profile_hash !== profile.contentHash;
|
|
1744
1756
|
const config = loadLanesConfig(hqRoot);
|
|
1745
1757
|
const repairRecordedAccount = plan.mode === "resume" &&
|
|
1746
|
-
(trigger.kind === "resume" || pendingResume?.trigger === "resume");
|
|
1758
|
+
(trigger.kind === "resume" || pendingResume?.trigger === "resume" || pendingResume?.trigger === "watcher");
|
|
1747
1759
|
const recordedModel = repairRecordedAccount && existing.model_source === "account"
|
|
1748
1760
|
? existing.model
|
|
1749
1761
|
: undefined;
|
|
@@ -1919,6 +1931,7 @@ export function spawnLaneWorker(hqRoot, lane, deps = {}, launch = {}) {
|
|
|
1919
1931
|
? pendingResume.text
|
|
1920
1932
|
: undefined,
|
|
1921
1933
|
resumedFrom: trigger.kind === "resume" ? trigger.from : pendingResume?.resumed_from,
|
|
1934
|
+
resumeSource: pendingResume?.trigger === "watcher" ? "watcher" : undefined,
|
|
1922
1935
|
answersPath: answeredLines !== undefined ? answersPath : undefined,
|
|
1923
1936
|
answers: answeredLines,
|
|
1924
1937
|
envelopeRepairErrors: lane.terminal_reason === ENVELOPE_INVALID_REASON &&
|
|
@@ -2019,14 +2032,24 @@ export function spawnLaneWorker(hqRoot, lane, deps = {}, launch = {}) {
|
|
|
2019
2032
|
const nowIso = (deps.now ?? (() => new Date()))().toISOString();
|
|
2020
2033
|
const resumeHistoryTrigger = pendingResume?.keep_alive === true
|
|
2021
2034
|
? "keep_alive"
|
|
2022
|
-
: pendingResume?.trigger === "
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2035
|
+
: pendingResume?.trigger === "monitor_event" || pendingResume?.monitor_event === true ||
|
|
2036
|
+
(trigger.kind === "message" && trigger.monitor_event === true)
|
|
2037
|
+
? "monitor_event"
|
|
2038
|
+
: pendingResume?.trigger === "system_notice" ||
|
|
2039
|
+
(trigger.kind === "message" && trigger.system_notice === true)
|
|
2040
|
+
? "system_notice"
|
|
2041
|
+
: pendingResume?.trigger === "watcher"
|
|
2042
|
+
? "watcher"
|
|
2043
|
+
: pendingResume?.trigger === "message" || trigger.kind === "message"
|
|
2044
|
+
? "message"
|
|
2045
|
+
: pendingResume?.trigger === "resume" || trigger.kind === "resume"
|
|
2046
|
+
? "resume_cli"
|
|
2047
|
+
: "reconcile";
|
|
2027
2048
|
const resumeHistoryCaller = pendingResume?.caller ||
|
|
2028
|
-
(typeof env.HQ_LANE_ID === "string" ? env.HQ_LANE_ID.trim() : "") ||
|
|
2029
|
-
(trigger
|
|
2049
|
+
(pendingResume?.trigger !== "watcher" && typeof env.HQ_LANE_ID === "string" ? env.HQ_LANE_ID.trim() : "") ||
|
|
2050
|
+
(pendingResume?.trigger === "message" || pendingResume?.trigger === "monitor_event" ||
|
|
2051
|
+
pendingResume?.trigger === "system_notice" || trigger.kind === "answered_questions" ||
|
|
2052
|
+
trigger.kind === "resume" || trigger.kind === "message"
|
|
2030
2053
|
? "user"
|
|
2031
2054
|
: undefined);
|
|
2032
2055
|
let statusGateHeartbeat;
|
|
@@ -2159,7 +2182,7 @@ export function spawnLaneWorker(hqRoot, lane, deps = {}, launch = {}) {
|
|
|
2159
2182
|
session_id: resume.session_id,
|
|
2160
2183
|
at: nowIso,
|
|
2161
2184
|
account: resolved.id,
|
|
2162
|
-
trigger: trigger.kind,
|
|
2185
|
+
trigger: trigger.kind === "message" && trigger.system_notice === true ? "system_notice" : trigger.kind,
|
|
2163
2186
|
...(trigger.kind === "message"
|
|
2164
2187
|
? {
|
|
2165
2188
|
message_id: trigger.message_id,
|
|
@@ -766,7 +766,7 @@ export interface FailedResumeHistoryEntry {
|
|
|
766
766
|
export interface PendingResume {
|
|
767
767
|
/** Fresh answered-question launches need no previous session id. */
|
|
768
768
|
session_id?: string;
|
|
769
|
-
trigger: "answered_questions" | "message" | "resume";
|
|
769
|
+
trigger: "answered_questions" | "message" | "monitor_event" | "system_notice" | "resume" | "watcher";
|
|
770
770
|
/** True when this queued resume was initiated automatically by keep-alive. */
|
|
771
771
|
keep_alive?: true;
|
|
772
772
|
resumed_from?: "interrupted" | "done" | "blocked" | "failed" | "awaiting_input";
|
|
@@ -797,7 +797,7 @@ export interface LaneResumeRecord {
|
|
|
797
797
|
at: string;
|
|
798
798
|
/** Account the resumed run was bound to. */
|
|
799
799
|
account?: string;
|
|
800
|
-
trigger: "answered_questions" | "message" | "resume";
|
|
800
|
+
trigger: "answered_questions" | "message" | "monitor_event" | "system_notice" | "resume" | "watcher";
|
|
801
801
|
/** True when this was an automatic keep-alive resume. */
|
|
802
802
|
keep_alive?: true;
|
|
803
803
|
message_id?: string;
|
|
@@ -23,7 +23,6 @@ export interface LaneWatchDeps {
|
|
|
23
23
|
readQuestion?: (hqRoot: string, questionId: string) => QuestionRecord | null;
|
|
24
24
|
readAccountStatuses?: (lanes: readonly LaneRecord[]) => Promise<readonly LaneWatchAccountStatus[]>;
|
|
25
25
|
keepAliveEnabled?: (lane: LaneRecord) => boolean | Promise<boolean>;
|
|
26
|
-
keepAliveNextCheckAtEnabled?: (lane: LaneRecord) => boolean | Promise<boolean>;
|
|
27
26
|
sleep?: (ms: number) => Promise<void>;
|
|
28
27
|
intervalMs?: number;
|
|
29
28
|
maxOutputLines?: number;
|
package/dist/lib/lanes/watch.js
CHANGED
|
@@ -32,7 +32,7 @@ function validateLaneIds(laneIds) {
|
|
|
32
32
|
}
|
|
33
33
|
return unique;
|
|
34
34
|
}
|
|
35
|
-
function canResumeKeepAliveLane(hqRoot, lane,
|
|
35
|
+
function canResumeKeepAliveLane(hqRoot, lane, nowMs = Date.now()) {
|
|
36
36
|
if (lane.keep_alive !== true ||
|
|
37
37
|
lane.keep_alive_opted_in_at === undefined ||
|
|
38
38
|
!Number.isFinite(Date.parse(lane.keep_alive_opted_in_at)) ||
|
|
@@ -49,10 +49,8 @@ function canResumeKeepAliveLane(hqRoot, lane, nextCheckAtEnabled = false, nowMs
|
|
|
49
49
|
return lane.terminal_reason === "exited_with_partial_output";
|
|
50
50
|
}
|
|
51
51
|
if (lane.state === "done") {
|
|
52
|
-
const hasPendingWork = lane
|
|
53
|
-
|
|
54
|
-
: false;
|
|
55
|
-
const nextCheckAtMs = nextCheckAtEnabled && !hasPendingWork
|
|
52
|
+
const hasPendingWork = hasPendingKeepAliveWork(hqRoot, lane);
|
|
53
|
+
const nextCheckAtMs = !hasPendingWork
|
|
56
54
|
? keepAliveNextCheckAtMs(lane.last_envelope?.next_check_at, nowMs, Date.parse(lane.last_envelope?.at ?? lane.updated_at))
|
|
57
55
|
: undefined;
|
|
58
56
|
if (nextCheckAtMs !== undefined && nowMs < nextCheckAtMs)
|
|
@@ -143,10 +141,7 @@ export async function runLaneWatch(hqRoot, laneIds, deps = {}) {
|
|
|
143
141
|
continue;
|
|
144
142
|
if (!await deps.keepAliveEnabled(record))
|
|
145
143
|
continue;
|
|
146
|
-
|
|
147
|
-
? false
|
|
148
|
-
: await deps.keepAliveNextCheckAtEnabled(record);
|
|
149
|
-
if (canResumeKeepAliveLane(hqRoot, record, nextCheckAtEnabled))
|
|
144
|
+
if (canResumeKeepAliveLane(hqRoot, record))
|
|
150
145
|
keepAliveWaiting.add(record.lane_id);
|
|
151
146
|
}
|
|
152
147
|
}
|
package/dist/main.js
CHANGED
|
@@ -201,7 +201,7 @@ export async function runCli() {
|
|
|
201
201
|
const isAuthLogin = process.argv[2] === "auth" && process.argv[3] === "login";
|
|
202
202
|
if (!isAuthLogin) {
|
|
203
203
|
runGlobalNetworkLifecycle(process.argv, () => {
|
|
204
|
-
void kickFlagRegistryReadiness();
|
|
204
|
+
void kickFlagRegistryReadiness({ sentry: Sentry });
|
|
205
205
|
});
|
|
206
206
|
}
|
|
207
207
|
// Set when a self-update re-exec'd this command on a newer CLI: the child
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Node network defaults shared by the full graph and the selective fast path. */
|
|
2
|
+
export declare const HQ_NETWORK_FAMILY_ATTEMPT_TIMEOUT_MS = 1000;
|
|
3
|
+
export interface NodeNetworkCompatibilityOptions {
|
|
4
|
+
nodeOptions?: string;
|
|
5
|
+
execArgv?: readonly string[];
|
|
6
|
+
/** Inject the net timeout setter (tests). Pass null to simulate a runtime without it. */
|
|
7
|
+
setAttemptTimeout?: ((milliseconds: number) => void) | null;
|
|
8
|
+
}
|
|
9
|
+
export declare function configureNodeNetworkCompatibility(options?: NodeNetworkCompatibilityOptions): boolean;
|
|
10
|
+
/** Apply at most once when both entrypoint routing layers import this module. */
|
|
11
|
+
export declare function configureNodeNetworkCompatibilityOnce(): boolean;
|
|
12
|
+
//# sourceMappingURL=node-network-compat-core.d.ts.map
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/** Node network defaults shared by the full graph and the selective fast path. */
|
|
2
|
+
import * as nodeNet from "node:net";
|
|
3
|
+
export const HQ_NETWORK_FAMILY_ATTEMPT_TIMEOUT_MS = 1000;
|
|
4
|
+
const ATTEMPT_TIMEOUT_OPTION = "--network-family-autoselection-attempt-timeout";
|
|
5
|
+
function hasExplicitAttemptTimeout(nodeOptions, execArgv) {
|
|
6
|
+
const optionPattern = new RegExp("(?:^|\\s)" + ATTEMPT_TIMEOUT_OPTION + "(?:=|\\s|$)");
|
|
7
|
+
return (optionPattern.test(nodeOptions) ||
|
|
8
|
+
execArgv.some((argument) => argument === ATTEMPT_TIMEOUT_OPTION ||
|
|
9
|
+
argument.startsWith(ATTEMPT_TIMEOUT_OPTION + "=")));
|
|
10
|
+
}
|
|
11
|
+
function resolveSetAttemptTimeout(override) {
|
|
12
|
+
if (override === null)
|
|
13
|
+
return undefined;
|
|
14
|
+
if (override)
|
|
15
|
+
return override;
|
|
16
|
+
const fn = nodeNet.setDefaultAutoSelectFamilyAttemptTimeout;
|
|
17
|
+
return typeof fn === "function" ? fn.bind(nodeNet) : undefined;
|
|
18
|
+
}
|
|
19
|
+
export function configureNodeNetworkCompatibility(options = {}) {
|
|
20
|
+
const nodeOptions = options.nodeOptions ?? process.env.NODE_OPTIONS ?? "";
|
|
21
|
+
const execArgv = options.execArgv ?? process.execArgv;
|
|
22
|
+
if (hasExplicitAttemptTimeout(nodeOptions, execArgv)) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
const setAttemptTimeout = resolveSetAttemptTimeout(options.setAttemptTimeout);
|
|
26
|
+
if (!setAttemptTimeout) {
|
|
27
|
+
// Runtime lacks the API; the Node preflight should already have exited.
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
setAttemptTimeout(HQ_NETWORK_FAMILY_ATTEMPT_TIMEOUT_MS);
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
let configurationAttempted = false;
|
|
34
|
+
/** Apply at most once when both entrypoint routing layers import this module. */
|
|
35
|
+
export function configureNodeNetworkCompatibilityOnce() {
|
|
36
|
+
if (configurationAttempted)
|
|
37
|
+
return false;
|
|
38
|
+
configurationAttempted = true;
|
|
39
|
+
return configureNodeNetworkCompatibility();
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=node-network-compat-core.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Selective Node network setup for the entrypoint's fast routes. */
|
|
2
|
+
/**
|
|
3
|
+
* Full-graph invocations keep the historical unconditional network patch.
|
|
4
|
+
* Fast routes opt in only when their direct implementation uses Node networking.
|
|
5
|
+
*/
|
|
6
|
+
export declare function shouldConfigureNodeNetworkCompatibility(argv: readonly string[]): boolean;
|
|
7
|
+
//# sourceMappingURL=node-network-compat-fast.d.ts.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** Selective Node network setup for the entrypoint's fast routes. */
|
|
2
|
+
import { parseFastCore, parseSecretsEnvFastRequest } from "./fast-scaffold.js";
|
|
3
|
+
import { isVersionRequest, wantsLabelledVersionReport } from "./version-request.js";
|
|
4
|
+
/**
|
|
5
|
+
* Full-graph invocations keep the historical unconditional network patch.
|
|
6
|
+
* Fast routes opt in only when their direct implementation uses Node networking.
|
|
7
|
+
*/
|
|
8
|
+
export function shouldConfigureNodeNetworkCompatibility(argv) {
|
|
9
|
+
if (isVersionRequest(argv))
|
|
10
|
+
return wantsLabelledVersionReport(argv);
|
|
11
|
+
const fastCore = parseFastCore(argv);
|
|
12
|
+
if (fastCore)
|
|
13
|
+
return fastCore.entry.requiresNodeNetworkCompatibility === true;
|
|
14
|
+
// The lightweight secrets route still makes vault API requests.
|
|
15
|
+
if (parseSecretsEnvFastRequest(argv))
|
|
16
|
+
return true;
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
if (shouldConfigureNodeNetworkCompatibility(process.argv)) {
|
|
20
|
+
const { configureNodeNetworkCompatibilityOnce } = await import("./node-network-compat-core.js");
|
|
21
|
+
configureNodeNetworkCompatibilityOnce();
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=node-network-compat-fast.js.map
|