@indigoai-us/hq-cli 5.294.0 → 5.295.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 +25 -0
- package/dist/command-catalog.generated.d.ts +12 -9
- package/dist/command-catalog.generated.js +15 -12
- package/dist/commands/auth.d.ts +1 -0
- package/dist/commands/auth.js +76 -1
- package/dist/commands/doctor.js +0 -74
- package/dist/commands/lanes-message.js +23 -5
- package/dist/commands/lanes-resume.js +0 -3
- package/dist/commands/lanes-shared.d.ts +1 -2
- package/dist/commands/lanes.d.ts +1 -9
- package/dist/commands/lanes.js +177 -172
- package/dist/fast-scaffold.d.ts +0 -5
- package/dist/fast-scaffold.js +0 -10
- package/dist/index.js +1 -10
- package/dist/lib/core-utils/sentry-report.d.ts +0 -19
- package/dist/lib/core-utils/sentry-report.js +8 -61
- package/dist/lib/doctor/checks/claude-wiring.js +9 -0
- package/dist/lib/lanes/flag-keys.d.ts +2 -0
- package/dist/lib/lanes/flag-keys.js +2 -0
- package/dist/lib/lanes/manager-status-gate.d.ts +0 -17
- package/dist/lib/lanes/manager-status-gate.js +2 -43
- package/dist/lib/lanes/outpost-dispatch.d.ts +2 -0
- package/dist/lib/lanes/outpost-dispatch.js +7 -1
- package/dist/lib/lanes/outpost-hire-cache.d.ts +0 -1
- package/dist/lib/lanes/outpost-hire-cache.js +0 -1
- package/dist/lib/lanes/outpost-snapshot-memo.d.ts +6 -0
- package/dist/lib/lanes/outpost-snapshot-memo.js +14 -0
- package/dist/lib/lanes/reconcile.d.ts +2 -3
- package/dist/lib/lanes/reconcile.js +44 -38
- package/dist/lib/lanes/spawn.d.ts +0 -2
- package/dist/lib/lanes/spawn.js +18 -5
- package/dist/lib/lanes/store.js +22 -0
- package/dist/lib/lanes/types.d.ts +18 -1
- package/dist/lib/lanes/types.js +1 -1
- package/dist/lib/lanes/watcher.d.ts +4 -3
- package/dist/lib/lanes/watcher.js +102 -34
- package/dist/lib/mesh/live/daemon/run.js +18 -1
- package/dist/utils/cognito-session.d.ts +6 -0
- package/dist/utils/cognito-session.js +9 -0
- package/dist/utils/self-update.js +31 -0
- package/dist/utils/version-gate.js +22 -3
- package/package.json +1 -1
- package/dist/fast-lanes-list-route.d.ts +0 -8
- package/dist/fast-lanes-list-route.js +0 -32
- package/dist/fast-lanes-list.d.ts +0 -3
- package/dist/fast-lanes-list.js +0 -224
- package/dist/lib/doctor/client-health-sentry-flag.d.ts +0 -23
- package/dist/lib/doctor/client-health-sentry-flag.js +0 -99
- package/dist/lib/doctor/client-health-sentry.d.ts +0 -71
- package/dist/lib/doctor/client-health-sentry.js +0 -286
- package/dist/lib/lanes/outpost-snapshot-cache.d.ts +0 -26
- package/dist/lib/lanes/outpost-snapshot-cache.js +0 -215
- package/dist/lib/lanes/outpost-snapshot-refresh.d.ts +0 -21
- package/dist/lib/lanes/outpost-snapshot-refresh.js +0 -329
package/dist/lib/lanes/spawn.js
CHANGED
|
@@ -354,12 +354,13 @@ function archivedResumeMessageText(hqRoot, laneId, messageId) {
|
|
|
354
354
|
}
|
|
355
355
|
function pendingResumeForRetry(hqRoot, lane, sessionId, at) {
|
|
356
356
|
const last = lane.last_resume;
|
|
357
|
+
const keepAlive = last?.keep_alive === true ? { keep_alive: true } : {};
|
|
357
358
|
if (last === undefined || last.session_id !== sessionId) {
|
|
358
|
-
return { session_id: sessionId, trigger: "resume", resumed_from: "interrupted", at };
|
|
359
|
+
return { session_id: sessionId, trigger: "resume", resumed_from: "interrupted", at, ...keepAlive };
|
|
359
360
|
}
|
|
360
361
|
const compact = last.compact === undefined ? {} : { compact: last.compact };
|
|
361
362
|
if (last.trigger === "answered_questions") {
|
|
362
|
-
return { session_id: sessionId, trigger: "answered_questions", at, ...compact };
|
|
363
|
+
return { session_id: sessionId, trigger: "answered_questions", at, ...compact, ...keepAlive };
|
|
363
364
|
}
|
|
364
365
|
if (last.trigger === "message") {
|
|
365
366
|
if (last.message_id === undefined) {
|
|
@@ -376,6 +377,7 @@ function pendingResumeForRetry(hqRoot, lane, sessionId, at) {
|
|
|
376
377
|
message_id: last.message_id,
|
|
377
378
|
at,
|
|
378
379
|
...compact,
|
|
380
|
+
...keepAlive,
|
|
379
381
|
};
|
|
380
382
|
}
|
|
381
383
|
return {
|
|
@@ -386,6 +388,7 @@ function pendingResumeForRetry(hqRoot, lane, sessionId, at) {
|
|
|
386
388
|
...(last.message_id !== undefined ? { message_id: last.message_id } : {}),
|
|
387
389
|
at,
|
|
388
390
|
...compact,
|
|
391
|
+
...keepAlive,
|
|
389
392
|
};
|
|
390
393
|
}
|
|
391
394
|
/** Change account only after a provider/runtime account failure, then re-admit. */
|
|
@@ -1112,6 +1115,8 @@ export function envelopeRepairPrompt(input) {
|
|
|
1112
1115
|
`Missing expected envelope file: ${input.envelopePath}`,
|
|
1113
1116
|
"The previous envelope is stale and must not be reused or copied into this run's result.",
|
|
1114
1117
|
"Printing an envelope in the conversation or writing it to last-message.txt is not enough; write the JSON object directly to the expected file.",
|
|
1118
|
+
"If the task is unfinished, write decision: done, story_complete: false, and set story_status and story_complete_reason to describe the work that remains. The repair turn itself is never a reason for blocked.",
|
|
1119
|
+
"This repair turn only writes the missing envelope; do not resume the task.",
|
|
1115
1120
|
"Schema:",
|
|
1116
1121
|
JSON.stringify(LANE_ENVELOPE_JSON_SCHEMA, null, 2),
|
|
1117
1122
|
`Write the corrected JSON object to ${input.envelopePath}, overwriting it. ` +
|
|
@@ -2000,9 +2005,16 @@ export function spawnLaneWorker(hqRoot, lane, deps = {}, launch = {}) {
|
|
|
2000
2005
|
// Claim running before spawning. Two callers that both observe queued must
|
|
2001
2006
|
// not both start processes; the CAS loser returns without spawn.
|
|
2002
2007
|
const nowIso = (deps.now ?? (() => new Date()))().toISOString();
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2008
|
+
let statusGateHeartbeat;
|
|
2009
|
+
try {
|
|
2010
|
+
statusGateHeartbeat = managerStatusGateHeartbeat(hqRoot, existing, laneTurnId, nowIso);
|
|
2011
|
+
}
|
|
2012
|
+
catch (err) {
|
|
2013
|
+
const code = err instanceof LanesError ? err.error : "manager_status_gate_unavailable";
|
|
2014
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2015
|
+
process.stderr.write(`hq lanes: manager status gate disabled for ${lane.lane_id}: ${code}: ${message}\n`);
|
|
2016
|
+
statusGateHeartbeat = undefined;
|
|
2017
|
+
}
|
|
2006
2018
|
let claimed = false;
|
|
2007
2019
|
const claimedRecord = retryUpdateLane(hqRoot, lane.lane_id, "spawn", (current) => {
|
|
2008
2020
|
claimed = false;
|
|
@@ -2102,6 +2114,7 @@ export function spawnLaneWorker(hqRoot, lane, deps = {}, launch = {}) {
|
|
|
2102
2114
|
at: nowIso,
|
|
2103
2115
|
account: resolved.id,
|
|
2104
2116
|
trigger: pendingResume.trigger,
|
|
2117
|
+
...(pendingResume.keep_alive === true ? { keep_alive: true } : {}),
|
|
2105
2118
|
...(pendingResume.message_id !== undefined ? { message_id: pendingResume.message_id } : {}),
|
|
2106
2119
|
...(pendingResume.monitor_event === true ? { monitor_event: true } : {}),
|
|
2107
2120
|
...(pendingResume.resumed_from !== undefined ? { resumed_from: pendingResume.resumed_from } : {}),
|
package/dist/lib/lanes/store.js
CHANGED
|
@@ -751,6 +751,28 @@ export function parseLaneRecord(raw, filePath) {
|
|
|
751
751
|
path: filePath,
|
|
752
752
|
});
|
|
753
753
|
}
|
|
754
|
+
if (raw.keep_alive_history !== undefined) {
|
|
755
|
+
if (!Array.isArray(raw.keep_alive_history) || raw.keep_alive_history.length > HISTORY_CAP) {
|
|
756
|
+
throw new LaneSchemaError(`keep_alive_history must be an array capped at ${HISTORY_CAP} entries`, {
|
|
757
|
+
path: filePath,
|
|
758
|
+
field: "keep_alive_history",
|
|
759
|
+
});
|
|
760
|
+
}
|
|
761
|
+
for (const [index, value] of raw.keep_alive_history.entries()) {
|
|
762
|
+
const field = `keep_alive_history[${index}]`;
|
|
763
|
+
if (!isPlainObject(value) ||
|
|
764
|
+
Object.keys(value).some((key) => !["at", "resumed_from", "account"].includes(key)) ||
|
|
765
|
+
(value.resumed_from !== "done" && value.resumed_from !== "interrupted" && value.resumed_from !== "blocked") ||
|
|
766
|
+
(value.account !== null && typeof value.account !== "string")) {
|
|
767
|
+
throw new LaneSchemaError(`${field} must be a keep-alive resume history entry`, {
|
|
768
|
+
path: filePath,
|
|
769
|
+
field,
|
|
770
|
+
value,
|
|
771
|
+
});
|
|
772
|
+
}
|
|
773
|
+
requireTimestamp(value.at, `${field}.at`, filePath);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
754
776
|
if (raw.stop_history !== undefined) {
|
|
755
777
|
if (!Array.isArray(raw.stop_history)) {
|
|
756
778
|
throw new LaneSchemaError("stop_history must be an array when present", {
|
|
@@ -407,6 +407,11 @@ export interface KeepAliveBlockedHistoryEntry {
|
|
|
407
407
|
resume_skipped_reason?: "open_question" | "blocked_grace";
|
|
408
408
|
resume_queued?: boolean;
|
|
409
409
|
}
|
|
410
|
+
export interface KeepAliveResumeHistoryEntry {
|
|
411
|
+
at: string;
|
|
412
|
+
resumed_from: "done" | "interrupted" | "blocked";
|
|
413
|
+
account: string | null;
|
|
414
|
+
}
|
|
410
415
|
export interface KrFreshnessHistoryEntry {
|
|
411
416
|
event: "kr_stalled";
|
|
412
417
|
turn_id: string;
|
|
@@ -418,7 +423,13 @@ export interface KrFreshnessHistoryEntry {
|
|
|
418
423
|
resume_queued?: boolean;
|
|
419
424
|
resume_skipped_reason?: "open_question" | "blocked" | "miss_limit" | "no_provider_session";
|
|
420
425
|
}
|
|
421
|
-
export
|
|
426
|
+
export interface EnvelopeRepairContinueHistoryEntry {
|
|
427
|
+
event: "envelope_repair_continued";
|
|
428
|
+
at: string;
|
|
429
|
+
envelope_ref: string;
|
|
430
|
+
session_id: string;
|
|
431
|
+
}
|
|
432
|
+
export type LaneGateHistoryEntry = ManagerStatusGateHistoryEntry | BlockerFreshnessHistoryEntry | KrFreshnessHistoryEntry | EnvelopeRepairContinueHistoryEntry | KeepAliveBlockedHistoryEntry;
|
|
422
433
|
export interface LaneStory {
|
|
423
434
|
project_id: string;
|
|
424
435
|
story_id: string;
|
|
@@ -491,6 +502,8 @@ export interface LaneRecord {
|
|
|
491
502
|
keep_alive_blocked_reason?: "not_opted_in" | "missing_opt_in_time" | "envelope_before_opt_in" | "interruption_before_opt_in" | "missing_interruption_time" | "retired" | "superseded" | "retired_list_invalid" | "terminal_state";
|
|
492
503
|
/** Consecutive automatic keep-alive resumes since the last human reset. */
|
|
493
504
|
keep_alive_resume_count?: number;
|
|
505
|
+
/** Bounded audit trail of automatic keep-alive resumes; human resets never clear it. */
|
|
506
|
+
keep_alive_history?: KeepAliveResumeHistoryEntry[];
|
|
494
507
|
/** Blocked keep-alive resumes in the current blocked episode. */
|
|
495
508
|
keep_alive_blocked_resume_streak?: number;
|
|
496
509
|
/** Time the most recent automatic keep-alive resume was queued. */
|
|
@@ -700,6 +713,8 @@ export interface PendingResume {
|
|
|
700
713
|
/** Fresh answered-question launches need no previous session id. */
|
|
701
714
|
session_id?: string;
|
|
702
715
|
trigger: "answered_questions" | "message" | "resume";
|
|
716
|
+
/** True when this queued resume was initiated automatically by keep-alive. */
|
|
717
|
+
keep_alive?: true;
|
|
703
718
|
resumed_from?: "interrupted" | "done" | "blocked" | "failed";
|
|
704
719
|
from_failed?: true;
|
|
705
720
|
/** Senior that authorized a failed-lane resume, rechecked at the claim CAS. */
|
|
@@ -727,6 +742,8 @@ export interface LaneResumeRecord {
|
|
|
727
742
|
/** Account the resumed run was bound to. */
|
|
728
743
|
account?: string;
|
|
729
744
|
trigger: "answered_questions" | "message" | "resume";
|
|
745
|
+
/** True when this was an automatic keep-alive resume. */
|
|
746
|
+
keep_alive?: true;
|
|
730
747
|
message_id?: string;
|
|
731
748
|
monitor_event?: true;
|
|
732
749
|
/**
|
package/dist/lib/lanes/types.js
CHANGED
|
@@ -225,7 +225,7 @@ export const LANE_FIELD_WRITERS = {
|
|
|
225
225
|
pickup_validation_pending: ["creator", "command", "spawn", "reconcile"],
|
|
226
226
|
pickup_validation_pending_since: ["creator", "command", "spawn", "reconcile"],
|
|
227
227
|
priority: ["command"], worker: ["creator", "command"], worker_path: ["creator", "command"], worker_pack: ["creator", "command"], worker_profile: ["creator", "command"], worker_profile_hash: ["creator", "command", "spawn"], registry_worker: ["creator", "command", "spawn"], registry_worker_approval_history: ["command", "watcher", "reconcile"], worker_profile_repin_history: ["command"], worker_profile_ignored: ["creator", "command", "spawn"], worker_policy_delivery: ["command", "spawn"],
|
|
228
|
-
keep_alive: ["creator", "command"], keep_alive_standing: ["command"], keep_alive_opted_in_at: ["creator", "command"], keep_alive_blocked_reason: ["command", "reconcile"], keep_alive_resume_count: ["command", "reconcile"], keep_alive_blocked_resume_streak: ["reconcile", "spawn", "watcher"], keep_alive_last_resume_at: ["command", "reconcile"], keep_alive_next_eligible_at: ["command", "reconcile"], keep_alive_exhausted_at: ["command", "reconcile"],
|
|
228
|
+
keep_alive: ["creator", "command"], keep_alive_standing: ["command"], keep_alive_opted_in_at: ["creator", "command"], keep_alive_blocked_reason: ["command", "reconcile"], keep_alive_resume_count: ["command", "reconcile"], keep_alive_history: ["reconcile"], keep_alive_blocked_resume_streak: ["reconcile", "spawn", "watcher"], keep_alive_last_resume_at: ["command", "reconcile"], keep_alive_next_eligible_at: ["command", "reconcile"], keep_alive_exhausted_at: ["command", "reconcile"],
|
|
229
229
|
provider: ["creator"], account: ["creator", "spawn", "command", "watcher", "reconcile"], account_selection: ["creator", "command"], admission_accounts: ["creator", "command"], account_history: ["command", "spawn", "watcher", "reconcile"], failed_resume_history: ["command"], pending_resume: ["command", "spawn", "watcher", "reconcile"], placement_default: ["creator"], host: ["creator", "command"], origin_host: ["creator"], origin_host_source: ["creator"],
|
|
230
230
|
version: ["creator"], created_at: ["creator"], updated_at: ["creator"], ended_at: ["reconcile"], envelope_summary: ["reconcile"], attribution_history: ["command"],
|
|
231
231
|
last_envelope: ["watcher", "reconcile"], model: ["creator", "spawn", "command"], model_source: ["creator", "spawn", "command"], effort: ["creator", "spawn", "command"], effort_source: ["creator", "spawn", "command"], fast_mode: ["spawn"],
|
|
@@ -41,6 +41,7 @@ import { type EnvelopeRepairSpawnInput, type SpawnLaneDeps } from "./spawn.js";
|
|
|
41
41
|
/** Spec §5.7: no signal for 20 minutes while the process is still alive. */
|
|
42
42
|
export declare const SILENCE_MS: number;
|
|
43
43
|
export declare const WATCHER_POLL_MS = 5000;
|
|
44
|
+
export declare const ENVELOPE_REPAIR_CONTINUE_TEXT = "continue the task; the previous turn ended without an envelope.";
|
|
44
45
|
/** Synced last_host_report_at cadence. Reconcile dead-host TTL is 20 minutes. */
|
|
45
46
|
export declare const WATCHER_HEARTBEAT_MS: number;
|
|
46
47
|
/**
|
|
@@ -49,7 +50,7 @@ export declare const WATCHER_HEARTBEAT_MS: number;
|
|
|
49
50
|
* once the file would exceed this, the oldest complete lines are dropped.
|
|
50
51
|
*/
|
|
51
52
|
export declare const WATCHER_OUT_MAX_BYTES: number;
|
|
52
|
-
export declare const WATCHER_DECISION_EVENTS: readonly ["liveness_transition", "silence_deadline", "envelope_observed", "envelope_invalid", "envelope_repair", "classification", "terminal_write", "message_recovery", "message_recovery_failed", "message_undeliverable", "mesh_event", "mesh_event_failed"];
|
|
53
|
+
export declare const WATCHER_DECISION_EVENTS: readonly ["liveness_transition", "silence_deadline", "envelope_observed", "envelope_invalid", "envelope_repair", "envelope_repair_continue", "classification", "terminal_write", "message_recovery", "message_recovery_failed", "message_undeliverable", "mesh_event", "mesh_event_failed"];
|
|
53
54
|
/** Terminal reason when the envelope failed to parse after the repair turn (spec §4). */
|
|
54
55
|
export { ENVELOPE_INVALID_REASON };
|
|
55
56
|
export type WatcherDecisionEvent = (typeof WATCHER_DECISION_EVENTS)[number];
|
|
@@ -232,11 +233,11 @@ export interface WatcherLoopDeps {
|
|
|
232
233
|
/** Deliver a lane-senior escalation through the normal lane-message router. */
|
|
233
234
|
deliverLaneSeniorMessage?: (seniorLaneId: string, text: string) => Promise<void>;
|
|
234
235
|
/** Test seam; production reads the company-scoped hq-flags row. */
|
|
235
|
-
readManagerStatusGateFlag?: typeof readLanesRegistryFlag;
|
|
236
|
-
/** Test seam; production reads the company-scoped hq-flags row. */
|
|
237
236
|
readBlockerFreshnessFlag?: typeof readLanesRegistryFlag;
|
|
238
237
|
/** Test seam; production reads the company-scoped hq-flags row. */
|
|
239
238
|
readKrFreshnessFlag?: typeof readLanesRegistryFlag;
|
|
239
|
+
/** Test seam; production reads the company-scoped hq-flags row. */
|
|
240
|
+
readEnvelopeRepairContinueFlag?: typeof readLanesRegistryFlag;
|
|
240
241
|
}
|
|
241
242
|
/**
|
|
242
243
|
* Wrap one mesh call. A failed emit is logged as `mesh_event_failed` with
|
|
@@ -37,7 +37,7 @@ import { LanesError } from "./errors.js";
|
|
|
37
37
|
import { questionRecordRefusalCode, raiseEnvelopeQuestions, resolveQuestionCompanyWithFallback, } from "./questions.js";
|
|
38
38
|
import { isTerminalLaneState, ENVELOPE_INVALID_REASON, parseLaneEnvelope, STORY_STATUSES, LANE_PROVIDERS, } from "./types.js";
|
|
39
39
|
import { laneStories, storyStatusForEnvelope } from "./stories.js";
|
|
40
|
-
import { appendCappedHistory, readLane, retryUpdateLane, withLaneHireLocks } from "./store.js";
|
|
40
|
+
import { appendCappedHistory, listQuestionIds, readLane, readQuestion, retryUpdateLane, withLaneHireLocks } from "./store.js";
|
|
41
41
|
import { enforceRegistryApprovalGate } from "./approval-gate.js";
|
|
42
42
|
import { readWorkerRunResult } from "./delivery.js";
|
|
43
43
|
import { applyDefaultQuotaHold, quotaSignalFromFields, readQuotaSignal, recordAccountQuota, } from "./quota.js";
|
|
@@ -51,13 +51,15 @@ import { readLanesRegistryFlag } from "./registry-flag.js";
|
|
|
51
51
|
import { laneCompanyUidSource, resolveLaneCompany } from "./lane-company.js";
|
|
52
52
|
import { blockerFreshnessFingerprint, resolveBlockerFreshnessForLane, } from "./blocker-freshness.js";
|
|
53
53
|
import { krFreshnessFingerprint, resolveKrFreshnessForLane, } from "./kr-freshness.js";
|
|
54
|
-
import {
|
|
54
|
+
import { managerStatusWasWritten, readStatusSnapshot, resolveManagerStatusTarget, } from "./manager-status-gate.js";
|
|
55
55
|
import { queueLaneTerminalResume, queueManagerStatusGateResume } from "./keep-alive.js";
|
|
56
|
+
import { LANE_ENVELOPE_REPAIR_CONTINUE_FLAG } from "./flag-keys.js";
|
|
56
57
|
import { monitorSessionDir } from "../monitor.js";
|
|
57
58
|
import { laneRunDir, missingPidProbe, probePidRecord, readPidRecord, readProcStarttime, readSpawnManifest, envelopeRepairMode, spawnEnvelopeRepair, writePidRecord, retryLaneOnAlternateAccount, } from "./spawn.js";
|
|
58
59
|
/** Spec §5.7: no signal for 20 minutes while the process is still alive. */
|
|
59
60
|
export const SILENCE_MS = 20 * 60 * 1000;
|
|
60
61
|
export const WATCHER_POLL_MS = 5_000;
|
|
62
|
+
export const ENVELOPE_REPAIR_CONTINUE_TEXT = "continue the task; the previous turn ended without an envelope.";
|
|
61
63
|
/** Synced last_host_report_at cadence. Reconcile dead-host TTL is 20 minutes. */
|
|
62
64
|
export const WATCHER_HEARTBEAT_MS = 60 * 1000;
|
|
63
65
|
/**
|
|
@@ -72,6 +74,7 @@ export const WATCHER_DECISION_EVENTS = [
|
|
|
72
74
|
"envelope_observed",
|
|
73
75
|
"envelope_invalid",
|
|
74
76
|
"envelope_repair",
|
|
77
|
+
"envelope_repair_continue",
|
|
75
78
|
"classification",
|
|
76
79
|
"terminal_write",
|
|
77
80
|
"message_recovery",
|
|
@@ -659,26 +662,9 @@ export function missingExpectedEnvelopeFile(hqRoot, laneId, envelopePath) {
|
|
|
659
662
|
};
|
|
660
663
|
}
|
|
661
664
|
async function settleManagerStatusGate(hqRoot, lane, manifestTurnId, at, deps, logDecision) {
|
|
662
|
-
const readFlag = deps.readManagerStatusGateFlag ?? readLanesRegistryFlag;
|
|
663
|
-
let enabled = false;
|
|
664
|
-
try {
|
|
665
|
-
const resolution = resolveLaneCompany(hqRoot, lane);
|
|
666
|
-
const company = resolution.company ?? undefined;
|
|
667
|
-
enabled = await readFlag(MANAGER_STATUS_GATE_FLAG, {
|
|
668
|
-
company,
|
|
669
|
-
...(company ? {
|
|
670
|
-
companyUidSource: laneCompanyUidSource(resolution.source),
|
|
671
|
-
} : {}),
|
|
672
|
-
});
|
|
673
|
-
}
|
|
674
|
-
catch (err) {
|
|
675
|
-
process.stderr.write(`hq lanes watcher: manager status gate flag read failed for ${lane.lane_id}: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
676
|
-
logDecision({ event: "status_gate_flag_failed", at, error: err instanceof Error ? err.name : "UnknownError" });
|
|
677
|
-
}
|
|
678
|
-
if (!enabled)
|
|
679
|
-
return lane;
|
|
680
665
|
let target;
|
|
681
666
|
try {
|
|
667
|
+
resolveLaneCompany(hqRoot, lane);
|
|
682
668
|
target = resolveManagerStatusTarget(hqRoot, lane);
|
|
683
669
|
}
|
|
684
670
|
catch (err) {
|
|
@@ -725,8 +711,8 @@ async function settleManagerStatusGate(hqRoot, lane, manifestTurnId, at, deps, l
|
|
|
725
711
|
turn_id: gate.turn_id,
|
|
726
712
|
path: gate.status_path,
|
|
727
713
|
consecutive_misses: updated.status_gate?.consecutive_misses ?? 0,
|
|
728
|
-
resume_queued: miss.resume_queued === true,
|
|
729
|
-
resume_skipped_reason: miss.resume_skipped_reason,
|
|
714
|
+
resume_queued: "resume_queued" in miss && miss.resume_queued === true,
|
|
715
|
+
resume_skipped_reason: "resume_skipped_reason" in miss ? miss.resume_skipped_reason : undefined,
|
|
730
716
|
state: updated.state,
|
|
731
717
|
});
|
|
732
718
|
}
|
|
@@ -1058,6 +1044,92 @@ function claimWatcherLoop(hqRoot, laneId, deps) {
|
|
|
1058
1044
|
});
|
|
1059
1045
|
};
|
|
1060
1046
|
}
|
|
1047
|
+
async function maybeContinueAfterMissingEnvelopeRepair(hqRoot, lane, envelope, envelopeRef, at, deps, logDecision) {
|
|
1048
|
+
const repair = lane.envelope_repair;
|
|
1049
|
+
const missingRepair = repair?.attempted === true &&
|
|
1050
|
+
repair.succeeded === true &&
|
|
1051
|
+
repair.envelope_ref === envelopeRef &&
|
|
1052
|
+
repair.first_error.startsWith("Missing expected envelope file:");
|
|
1053
|
+
if (!missingRepair || envelope === null || envelopeRef === undefined) {
|
|
1054
|
+
if (envelope?.decision === "blocked")
|
|
1055
|
+
logDecision({ event: "envelope_repair_continue", at, outcome: "skipped", reason: "not_a_successful_missing_repair", missing_repair: missingRepair, envelope_ref: envelopeRef, repair_ref: repair?.envelope_ref, first_error: repair?.first_error, succeeded: repair?.succeeded });
|
|
1056
|
+
return lane;
|
|
1057
|
+
}
|
|
1058
|
+
const incompleteDone = envelope.decision === "done" && envelope.story_complete === false;
|
|
1059
|
+
const repairInstructionBlock = envelope.decision === "blocked" &&
|
|
1060
|
+
(envelope.questions?.length ?? 0) === 0 &&
|
|
1061
|
+
/(?:not to resume|do not resume|don't resume)(?: the)? task/i.test(envelope.blocked_reason ?? "");
|
|
1062
|
+
if (!incompleteDone && !repairInstructionBlock) {
|
|
1063
|
+
if (envelope.decision === "blocked")
|
|
1064
|
+
logDecision({ event: "envelope_repair_continue", at, outcome: "skipped", reason: "blocked_reason_not_repair_instruction", blocked_reason: envelope.blocked_reason });
|
|
1065
|
+
return lane;
|
|
1066
|
+
}
|
|
1067
|
+
if (lane.last_resume?.text === ENVELOPE_REPAIR_CONTINUE_TEXT) {
|
|
1068
|
+
logDecision({ event: "envelope_repair_continue", at, outcome: "skipped", reason: "continue_already_used" });
|
|
1069
|
+
return lane;
|
|
1070
|
+
}
|
|
1071
|
+
let pendingQuestion = false;
|
|
1072
|
+
try {
|
|
1073
|
+
for (const id of listQuestionIds(hqRoot)) {
|
|
1074
|
+
const question = readQuestion(hqRoot, id);
|
|
1075
|
+
if (question.lane_id === lane.lane_id && question.status === "pending") {
|
|
1076
|
+
pendingQuestion = true;
|
|
1077
|
+
break;
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
catch (error) {
|
|
1082
|
+
logDecision({ event: "envelope_repair_continue", at, outcome: "skipped", reason: "question_check_failed", error: error instanceof Error ? error.message : String(error) });
|
|
1083
|
+
return lane;
|
|
1084
|
+
}
|
|
1085
|
+
if (pendingQuestion) {
|
|
1086
|
+
logDecision({ event: "envelope_repair_continue", at, outcome: "skipped", reason: "pending_question" });
|
|
1087
|
+
return lane;
|
|
1088
|
+
}
|
|
1089
|
+
const readFlag = deps.readEnvelopeRepairContinueFlag ?? readLanesRegistryFlag;
|
|
1090
|
+
let enabled = false;
|
|
1091
|
+
try {
|
|
1092
|
+
const resolution = resolveLaneCompany(hqRoot, lane);
|
|
1093
|
+
const company = resolution.company ?? undefined;
|
|
1094
|
+
enabled = await readFlag(LANE_ENVELOPE_REPAIR_CONTINUE_FLAG, {
|
|
1095
|
+
company,
|
|
1096
|
+
...(company ? { companyUidSource: laneCompanyUidSource(resolution.source) } : {}),
|
|
1097
|
+
});
|
|
1098
|
+
}
|
|
1099
|
+
catch (error) {
|
|
1100
|
+
logDecision({ event: "envelope_repair_continue", at, outcome: "skipped", reason: "flag_read_failed", error: error instanceof Error ? error.message : String(error) });
|
|
1101
|
+
return lane;
|
|
1102
|
+
}
|
|
1103
|
+
if (!enabled)
|
|
1104
|
+
return lane;
|
|
1105
|
+
const queued = retryUpdateLane(hqRoot, lane.lane_id, "watcher", (current) => {
|
|
1106
|
+
if (current.version !== lane.version ||
|
|
1107
|
+
(current.state !== "done" && current.state !== "blocked") ||
|
|
1108
|
+
current.last_envelope?.ref !== envelopeRef ||
|
|
1109
|
+
current.envelope_repair?.attempted !== true ||
|
|
1110
|
+
current.envelope_repair.succeeded !== true ||
|
|
1111
|
+
current.envelope_repair.envelope_ref !== envelopeRef ||
|
|
1112
|
+
current.envelope_repair.first_error !== repair.first_error ||
|
|
1113
|
+
current.last_resume?.text === ENVELOPE_REPAIR_CONTINUE_TEXT)
|
|
1114
|
+
return current;
|
|
1115
|
+
const result = queueLaneTerminalResume(current, at, ENVELOPE_REPAIR_CONTINUE_TEXT, { allowBlockedResume: true });
|
|
1116
|
+
if (!result.resumeQueued || result.lane.pending_resume?.session_id === undefined)
|
|
1117
|
+
return current;
|
|
1118
|
+
return {
|
|
1119
|
+
...result.lane,
|
|
1120
|
+
status_gate_history: appendCappedHistory(current.status_gate_history ?? [], {
|
|
1121
|
+
event: "envelope_repair_continued",
|
|
1122
|
+
at,
|
|
1123
|
+
envelope_ref: envelopeRef,
|
|
1124
|
+
session_id: result.lane.pending_resume.session_id,
|
|
1125
|
+
}),
|
|
1126
|
+
};
|
|
1127
|
+
});
|
|
1128
|
+
if (queued.state === "queued" && queued.pending_resume?.text === ENVELOPE_REPAIR_CONTINUE_TEXT) {
|
|
1129
|
+
logDecision({ event: "envelope_repair_continue", at, outcome: "queued", envelope_ref: envelopeRef, session_id: queued.pending_resume.session_id });
|
|
1130
|
+
}
|
|
1131
|
+
return queued;
|
|
1132
|
+
}
|
|
1061
1133
|
/** Detached and synchronous classifiers share one process-identity claim. */
|
|
1062
1134
|
export async function runWatcherLoop(hqRoot, laneId, deps = {}) {
|
|
1063
1135
|
const release = claimWatcherLoop(hqRoot, laneId, deps);
|
|
@@ -1144,17 +1216,10 @@ async function runWatcherLoopOwned(hqRoot, laneId, deps) {
|
|
|
1144
1216
|
}
|
|
1145
1217
|
const progressIntervalMs = deps.progressEventMinIntervalMs ?? readProgressIntervalMs(hqRoot, readLaneSoft(hqRoot, laneId));
|
|
1146
1218
|
const spawnRepair = deps.spawnRepair ?? spawnEnvelopeRepair;
|
|
1147
|
-
const retryAccount = deps.retryAccount ?? (
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
});
|
|
1152
|
-
return retryLaneOnAlternateAccount(root, lane, reason, "watcher", {
|
|
1153
|
-
...deps.retrySpawnDeps,
|
|
1154
|
-
managerStatusGateEnabled,
|
|
1155
|
-
admissionHost: (deps.resolveAdmissionHost ?? resolveProcessAdmissionHost)(),
|
|
1156
|
-
});
|
|
1157
|
-
});
|
|
1219
|
+
const retryAccount = deps.retryAccount ?? ((root, lane, reason) => retryLaneOnAlternateAccount(root, lane, reason, "watcher", {
|
|
1220
|
+
...deps.retrySpawnDeps,
|
|
1221
|
+
admissionHost: (deps.resolveAdmissionHost ?? resolveProcessAdmissionHost)(),
|
|
1222
|
+
}));
|
|
1158
1223
|
let lastSignalMs = now();
|
|
1159
1224
|
let lastSeenMtime = 0;
|
|
1160
1225
|
let lastAlive = null;
|
|
@@ -1542,7 +1607,10 @@ async function runWatcherLoopOwned(hqRoot, laneId, deps) {
|
|
|
1542
1607
|
((entry.event === "status_missed" && entry.resume_queued === true) ||
|
|
1543
1608
|
(entry.event === "blocker_stale" && entry.resume_queued === true) ||
|
|
1544
1609
|
(entry.event === "kr_stalled" && entry.resume_queued === true))) === true;
|
|
1545
|
-
if (terminalWrite.changed &&
|
|
1610
|
+
if (terminalWrite.changed && !managerStatusFollowupQueued) {
|
|
1611
|
+
updated = await maybeContinueAfterMissingEnvelopeRepair(hqRoot, updated, effectiveEnvelope ?? null, latest?.ref, tick.emit.at, deps, logDecision);
|
|
1612
|
+
}
|
|
1613
|
+
if (terminalWrite.changed && deps.recoverQueuedMessages && !managerStatusFollowupQueued && updated.state !== "queued") {
|
|
1546
1614
|
try {
|
|
1547
1615
|
if (dropboxInboxHasMessages(hqRoot, laneId)) {
|
|
1548
1616
|
await deps.recoverQueuedMessages(updated);
|
|
@@ -701,13 +701,30 @@ export async function runMeshDaemon(deps = {}) {
|
|
|
701
701
|
if (!fs.existsSync(spoolPath)) {
|
|
702
702
|
fs.writeFileSync(spoolPath, "", { mode: 0o600 });
|
|
703
703
|
}
|
|
704
|
-
|
|
704
|
+
const spoolWatcher = fs.watch(path.dirname(spoolPath), { persistent: true }, (_event, filename) => {
|
|
705
705
|
if (stopped)
|
|
706
706
|
return;
|
|
707
707
|
if (shouldFlushOnSpoolDirEvent(filename)) {
|
|
708
708
|
scheduleDebouncedFlush();
|
|
709
709
|
}
|
|
710
710
|
});
|
|
711
|
+
watcher = spoolWatcher;
|
|
712
|
+
spoolWatcher.on("error", (err) => {
|
|
713
|
+
// fs.watch can fail asynchronously after returning a watcher. Without
|
|
714
|
+
// an error listener Node treats EMFILE as an uncaught exception; the
|
|
715
|
+
// periodic flush below already provides a polling fallback.
|
|
716
|
+
if (watcher === spoolWatcher)
|
|
717
|
+
watcher = null;
|
|
718
|
+
try {
|
|
719
|
+
spoolWatcher.close();
|
|
720
|
+
}
|
|
721
|
+
catch (closeError) {
|
|
722
|
+
const closeMessage = closeError instanceof Error ? closeError.message : String(closeError);
|
|
723
|
+
log(dir, `fs.watch close failed after watcher error: ${closeMessage.slice(0, 120)}`);
|
|
724
|
+
}
|
|
725
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
726
|
+
log(dir, `fs.watch error; continuing interval flush: ${msg.slice(0, 120)}`);
|
|
727
|
+
});
|
|
711
728
|
}
|
|
712
729
|
catch (err) {
|
|
713
730
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -237,6 +237,12 @@ export declare function withMachineTokenStateDir<T>(fn: () => Promise<T>, opts?:
|
|
|
237
237
|
home?: string;
|
|
238
238
|
env?: NodeJS.ProcessEnv;
|
|
239
239
|
}): Promise<T>;
|
|
240
|
+
/**
|
|
241
|
+
* Ensure the current machine identity has a valid session and return only the
|
|
242
|
+
* access/ID token pair needed by supported machine-session handoff scripts.
|
|
243
|
+
* The machine state directory is isolated from the person's session cache.
|
|
244
|
+
*/
|
|
245
|
+
export declare function ensureMachineSessionTokens(): Promise<Pick<CognitoTokens, "accessToken" | "idToken" | "expiresAt">>;
|
|
240
246
|
/**
|
|
241
247
|
* True when a person-token refresh failed with a definitive Cognito auth
|
|
242
248
|
* rejection (refresh token invalid/revoked). Transient network/5xx/timeouts
|
|
@@ -840,6 +840,15 @@ function withTokenStateDirSection(fn) {
|
|
|
840
840
|
async function ensureMachineTokens() {
|
|
841
841
|
return withMachineTokenStateDir(() => getValidMachineTokens(DEFAULT_COGNITO));
|
|
842
842
|
}
|
|
843
|
+
/**
|
|
844
|
+
* Ensure the current machine identity has a valid session and return only the
|
|
845
|
+
* access/ID token pair needed by supported machine-session handoff scripts.
|
|
846
|
+
* The machine state directory is isolated from the person's session cache.
|
|
847
|
+
*/
|
|
848
|
+
export async function ensureMachineSessionTokens() {
|
|
849
|
+
const tokens = await ensureMachineTokensMaybeFallback({ tokenSource: "machine" });
|
|
850
|
+
return { accessToken: tokens.accessToken, idToken: tokens.idToken, expiresAt: tokens.expiresAt };
|
|
851
|
+
}
|
|
843
852
|
/**
|
|
844
853
|
* Mint machine tokens (daemon state dir cache). Logs once when the fresh-box
|
|
845
854
|
* default-creds fallback is what selected the machine path.
|
|
@@ -62,6 +62,8 @@ import * as fs from "node:fs";
|
|
|
62
62
|
import * as path from "node:path";
|
|
63
63
|
import semver from "semver";
|
|
64
64
|
import chalk from "chalk";
|
|
65
|
+
import { Sentry } from "../sentry.js";
|
|
66
|
+
import { isCliTelemetryDisabled } from "./cli-telemetry.js";
|
|
65
67
|
import { CLI_NAME, CLI_VERSION } from "../cli-version.js";
|
|
66
68
|
import { buildBunInstallArgv, buildPnpmInstallArgv, buildPrefixedInstallArgv, buildSpawnPlan, checkUpdateConvergence, derivePnpmHome, detachUpdateSupervisor, isBunManagedPackageDir, isLocalDependencyInstall, isPnpmManagedPackageDir, isPrefixWritable, nonWritablePrefixNote, openInstallOutput, pnpmUpdateEnv, resolveRunningInstall, runUpdateCommand, runSupervisedUpdateCommand, } from "./version-gate.js";
|
|
67
69
|
import { canStageInstall, stagedNpmInstall } from "./staged-install.js";
|
|
@@ -499,6 +501,7 @@ async function updateAndReexec(argv, flavor, known, deps) {
|
|
|
499
501
|
console.error(chalk.dim(` Or repair the install: hq rescue`));
|
|
500
502
|
}
|
|
501
503
|
console.error(chalk.dim(` Continuing the ${flavor.noun} on ${current}.`));
|
|
504
|
+
await captureSelfUpdateOutcome("update-failed", current, latestValid, install.manager, result.code, env, flavor.noun === "rescue");
|
|
502
505
|
return { action: "update-failed", latest };
|
|
503
506
|
}
|
|
504
507
|
if (installSnapshot)
|
|
@@ -521,6 +524,7 @@ async function updateAndReexec(argv, flavor, known, deps) {
|
|
|
521
524
|
if (converged === false) {
|
|
522
525
|
(deps.markIneffective ?? markLatestIneffective)(latest);
|
|
523
526
|
console.error(chalk.dim(` Continuing the ${flavor.noun} on ${current}.`));
|
|
527
|
+
await captureSelfUpdateOutcome("update-ineffective", current, latestValid, install.manager, undefined, env, flavor.noun === "rescue");
|
|
524
528
|
return { action: "update-ineffective", latest };
|
|
525
529
|
}
|
|
526
530
|
// A resident mesh daemon keeps the old JS loaded after a global install.
|
|
@@ -550,6 +554,33 @@ async function updateAndReexec(argv, flavor, known, deps) {
|
|
|
550
554
|
console.error(chalk.dim(`✓ hq-cli updated to ${latest}.`));
|
|
551
555
|
return { action: "reexec", reexecStatus: status, latest };
|
|
552
556
|
}
|
|
557
|
+
const SELF_UPDATE_ERROR_CLASSES = new Set(["MODULE_NOT_FOUND", "EACCES", "EEXIST"]);
|
|
558
|
+
async function captureSelfUpdateOutcome(action, fromVersion, targetVersion, installFlavor, code, env, flushBeforeExit) {
|
|
559
|
+
if (isCliTelemetryDisabled(env))
|
|
560
|
+
return;
|
|
561
|
+
const error = new Error(action === "update-failed" ? "Self-update failed" : "Self-update ineffective");
|
|
562
|
+
// This event needs no stack: frames would expose the machine's install path.
|
|
563
|
+
error.stack = undefined;
|
|
564
|
+
Sentry.captureException(error, {
|
|
565
|
+
fingerprint: ["self-update", action],
|
|
566
|
+
tags: {
|
|
567
|
+
from_version: fromVersion,
|
|
568
|
+
target_version: targetVersion,
|
|
569
|
+
install_flavor: installFlavor,
|
|
570
|
+
error_class: code && SELF_UPDATE_ERROR_CLASSES.has(code) ? code : "other",
|
|
571
|
+
},
|
|
572
|
+
});
|
|
573
|
+
if (flushBeforeExit) {
|
|
574
|
+
try {
|
|
575
|
+
if (!(await Sentry.flush(500))) {
|
|
576
|
+
console.error(chalk.dim("hq: Sentry flush timed out after self-update."));
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
catch {
|
|
580
|
+
console.error(chalk.dim("hq: Sentry flush failed after self-update."));
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
}
|
|
553
584
|
async function readStagedSelfUpdateFlag(deps, env) {
|
|
554
585
|
try {
|
|
555
586
|
if (deps.stagedSelfUpdate)
|
|
@@ -706,13 +706,32 @@ export function detachUpdateSupervisor() {
|
|
|
706
706
|
try {
|
|
707
707
|
rmSync(tempDir, { recursive: true, force: true });
|
|
708
708
|
}
|
|
709
|
-
catch {
|
|
710
|
-
|
|
709
|
+
catch (error) {
|
|
710
|
+
const failureClass = error &&
|
|
711
|
+
typeof error === "object" &&
|
|
712
|
+
"code" in error &&
|
|
713
|
+
typeof error.code === "string" &&
|
|
714
|
+
/^[A-Z0-9_]{1,32}$/.test(error.code)
|
|
715
|
+
? error.code
|
|
716
|
+
: error instanceof Error
|
|
717
|
+
? "Error"
|
|
718
|
+
: "UnknownError";
|
|
719
|
+
console.error(chalk.yellow(`Could not remove update supervisor temp dir (${failureClass}).`));
|
|
711
720
|
}
|
|
712
721
|
},
|
|
713
722
|
};
|
|
714
723
|
}
|
|
715
|
-
catch {
|
|
724
|
+
catch (error) {
|
|
725
|
+
const failureClass = error &&
|
|
726
|
+
typeof error === "object" &&
|
|
727
|
+
"code" in error &&
|
|
728
|
+
typeof error.code === "string" &&
|
|
729
|
+
/^[A-Z0-9_]{1,32}$/.test(error.code)
|
|
730
|
+
? error.code
|
|
731
|
+
: error instanceof Error
|
|
732
|
+
? "Error"
|
|
733
|
+
: "UnknownError";
|
|
734
|
+
console.error(chalk.yellow(`Could not copy update supervisor (${failureClass}).`));
|
|
716
735
|
if (dir)
|
|
717
736
|
rmSync(dir, { recursive: true, force: true });
|
|
718
737
|
return null;
|
package/package.json
CHANGED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
export interface FastLanesListRouteDeps {
|
|
2
|
-
flagEnabled: (flagKey: string, env: NodeJS.ProcessEnv) => Promise<boolean>;
|
|
3
|
-
runFastList: () => Promise<void>;
|
|
4
|
-
runPlacementList: () => Promise<void>;
|
|
5
|
-
}
|
|
6
|
-
/** Returns false unless this exact list form is safely enabled in the local flag snapshot. */
|
|
7
|
-
export declare function runFastLanesListRequest(argv: readonly string[], env?: NodeJS.ProcessEnv, deps?: FastLanesListRouteDeps): Promise<boolean>;
|
|
8
|
-
//# sourceMappingURL=fast-lanes-list-route.d.ts.map
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { isFastCachedLanesListRequest } from "./fast-scaffold.js";
|
|
2
|
-
import { fastFlagEnabled } from "./native-fast-route.js";
|
|
3
|
-
import { registerOutpostLaneSnapshotRefreshChild } from "./lib/lanes/outpost-snapshot-refresh.js";
|
|
4
|
-
import { LANE_OUTPOST_SNAPSHOT_CACHE_FLAG } from "./lib/lanes/outpost-hire-cache.js";
|
|
5
|
-
import { LANE_OUTPOST_LOAD_BALANCING_FLAG } from "./lib/lanes/flag-keys.js";
|
|
6
|
-
/** Returns false unless this exact list form is safely enabled in the local flag snapshot. */
|
|
7
|
-
export async function runFastLanesListRequest(argv, env = process.env, deps = {
|
|
8
|
-
flagEnabled: fastFlagEnabled,
|
|
9
|
-
runFastList: async () => {
|
|
10
|
-
const { runFastCachedLanesList } = await import("./fast-lanes-list.js");
|
|
11
|
-
await runFastCachedLanesList();
|
|
12
|
-
},
|
|
13
|
-
runPlacementList: async () => {
|
|
14
|
-
const { runFastCachedLanesListFromEntrypoint } = await import("./commands/lanes.js");
|
|
15
|
-
await runFastCachedLanesListFromEntrypoint();
|
|
16
|
-
},
|
|
17
|
-
}) {
|
|
18
|
-
registerOutpostLaneSnapshotRefreshChild(env);
|
|
19
|
-
if (!isFastCachedLanesListRequest(argv))
|
|
20
|
-
return false;
|
|
21
|
-
if (env.HQ_INTERNAL_LANES_REFRESH_REMOTE_SNAPSHOT === "1")
|
|
22
|
-
return false;
|
|
23
|
-
if (!await deps.flagEnabled(LANE_OUTPOST_SNAPSHOT_CACHE_FLAG, env))
|
|
24
|
-
return false;
|
|
25
|
-
const lanePlacementEnabled = await deps.flagEnabled(LANE_OUTPOST_LOAD_BALANCING_FLAG, env);
|
|
26
|
-
if (lanePlacementEnabled)
|
|
27
|
-
await deps.runPlacementList();
|
|
28
|
-
else
|
|
29
|
-
await deps.runFastList();
|
|
30
|
-
return true;
|
|
31
|
-
}
|
|
32
|
-
//# sourceMappingURL=fast-lanes-list-route.js.map
|