@indigoai-us/hq-cli 5.342.12 → 5.342.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/dist/commands/agent-kit.d.ts +2 -0
- package/dist/commands/agent-kit.js +23 -17
- package/dist/commands/agent-mcp.d.ts +2 -0
- package/dist/commands/agent-mcp.js +21 -15
- package/dist/commands/lanes-message.d.ts +20 -2
- package/dist/commands/lanes-message.js +99 -38
- package/dist/commands/lanes-shared.d.ts +1 -0
- package/dist/commands/lanes.d.ts +4 -0
- package/dist/commands/lanes.js +24 -30
- package/dist/commands/mesh.js +2 -1
- package/dist/commands/monitor.d.ts +14 -1
- package/dist/commands/monitor.js +43 -14
- package/dist/lib/doctor/checks/anywhere.js +11 -6
- package/dist/lib/flag-registry-worker-source.generated.d.ts +1 -1
- package/dist/lib/flag-registry-worker-source.generated.js +1 -1
- package/dist/lib/lanes/keep-alive-audit.d.ts +3 -0
- package/dist/lib/lanes/keep-alive-audit.js +59 -11
- package/dist/lib/lanes/reconcile.js +46 -3
- package/dist/lib/lanes/types.d.ts +2 -1
- package/dist/utils/resident-package-preload.d.ts +8 -1
- package/dist/utils/resident-package-preload.js +68 -0
- package/docs/anywhere-runtime.md +3 -1
- package/package.json +4 -4
- package/dist/lib/db/remote-engine.d.ts +0 -58
- package/dist/lib/db/remote-engine.js +0 -87
- package/dist/lib/db/remote-sql.d.ts +0 -22
- package/dist/lib/db/remote-sql.js +0 -36
|
@@ -14,6 +14,8 @@ interface KeepAliveAuditRow {
|
|
|
14
14
|
due_at: string;
|
|
15
15
|
due_reason: "next_check_at" | "backoff";
|
|
16
16
|
restart: KeepAliveAuditRestart | null;
|
|
17
|
+
blocked_outcome?: "woken" | "skipped";
|
|
18
|
+
blocked_outcome_reason?: string;
|
|
17
19
|
verdict: KeepAliveAuditVerdict;
|
|
18
20
|
}
|
|
19
21
|
interface KeepAliveAuditSummary {
|
|
@@ -42,6 +44,7 @@ export interface KeepAliveAuditResult {
|
|
|
42
44
|
export interface StopEnvelope {
|
|
43
45
|
name: string;
|
|
44
46
|
mtimeMs: number;
|
|
47
|
+
decision: "done" | "blocked";
|
|
45
48
|
nextCheckAt?: string;
|
|
46
49
|
}
|
|
47
50
|
export declare function readStopEnvelopes(hqRoot: string, laneId: string): {
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
-
import { LEAD_LANE_KEEP_ALIVE_BASE_BACKOFF_MS } from "./keep-alive.js";
|
|
3
|
+
import { LEAD_LANE_KEEP_ALIVE_BASE_BACKOFF_MS, LEAD_LANE_KEEP_ALIVE_NEXT_CHECK_MAX_DELAY_MS } from "./keep-alive.js";
|
|
4
4
|
import { LanesError } from "./errors.js";
|
|
5
5
|
import { laneRunDir } from "./spawn.js";
|
|
6
6
|
import { compareEnvelopeNames } from "./envelope-sequence.js";
|
|
7
7
|
import { listQuestionIds, readAllLanes, tryReadQuestion } from "./store.js";
|
|
8
|
-
|
|
8
|
+
import { isIsoDateTime } from "./types.js";
|
|
9
|
+
function readTerminalClassifications(hqRoot, laneId) {
|
|
9
10
|
const root = fs.realpathSync(hqRoot);
|
|
10
11
|
const watcherLog = path.join(laneRunDir(root, laneId), "logs", "watcher.out");
|
|
11
12
|
if (!withinRoot(root, watcherLog)) {
|
|
@@ -28,23 +29,23 @@ function readDoneClassifications(hqRoot, laneId) {
|
|
|
28
29
|
return [];
|
|
29
30
|
throw err;
|
|
30
31
|
}
|
|
31
|
-
const
|
|
32
|
+
const classifications = [];
|
|
32
33
|
for (const line of text.split(/\r?\n/)) {
|
|
33
34
|
if (!line.includes('"classification"'))
|
|
34
35
|
continue;
|
|
35
36
|
try {
|
|
36
37
|
const value = JSON.parse(line);
|
|
37
|
-
if (!isRecord(value) || value.state !== "done")
|
|
38
|
+
if (!isRecord(value) || (value.state !== "done" && value.state !== "blocked"))
|
|
38
39
|
continue;
|
|
39
40
|
const at = parseTime(value.at);
|
|
40
41
|
if (at !== undefined)
|
|
41
|
-
|
|
42
|
+
classifications.push({ at, state: value.state });
|
|
42
43
|
}
|
|
43
44
|
catch (err) {
|
|
44
45
|
throw new LanesError("keep_alive_audit_event_invalid", `Cannot parse lane watcher classification in ${watcherLog}: ${err instanceof Error ? err.message : String(err)}`, { lane_id: laneId, path: watcherLog });
|
|
45
46
|
}
|
|
46
47
|
}
|
|
47
|
-
return
|
|
48
|
+
return classifications.sort((a, b) => a.at - b.at);
|
|
48
49
|
}
|
|
49
50
|
const KEEP_ALIVE_ON_TIME_TOLERANCE_MS = 60_000;
|
|
50
51
|
const KEEP_ALIVE_NOT_RESTARTED_GRACE_MS = 5 * 60_000;
|
|
@@ -161,11 +162,15 @@ export function readStopEnvelopes(hqRoot, laneId) {
|
|
|
161
162
|
if (!isRecord(raw) || raw.schema_version !== "hq-lane-envelope.v1") {
|
|
162
163
|
throw new LanesError("keep_alive_audit_envelope_invalid", `Lane envelope is not hq-lane-envelope.v1: ${file}`, { lane_id: laneId, path: file });
|
|
163
164
|
}
|
|
164
|
-
|
|
165
|
+
const doneStop = raw.decision === "done" && raw.story_complete === false;
|
|
166
|
+
const blockedStop = raw.decision === "blocked" && typeof raw.next_check_at === "string" &&
|
|
167
|
+
isIsoDateTime(raw.next_check_at) && Date.parse(raw.next_check_at) > stat.mtimeMs;
|
|
168
|
+
if (!doneStop && !blockedStop)
|
|
165
169
|
continue;
|
|
166
170
|
stops.push({
|
|
167
171
|
name,
|
|
168
172
|
mtimeMs: stat.mtimeMs,
|
|
173
|
+
decision: blockedStop ? "blocked" : "done",
|
|
169
174
|
...(typeof raw.next_check_at === "string" ? { nextCheckAt: raw.next_check_at } : {}),
|
|
170
175
|
});
|
|
171
176
|
}
|
|
@@ -227,11 +232,14 @@ function readKeepAliveResumeEvents(hqRoot, laneId) {
|
|
|
227
232
|
return events.sort((a, b) => a.at - b.at);
|
|
228
233
|
}
|
|
229
234
|
function dueTime(stop, resumeEvents) {
|
|
235
|
+
const nextCheck = parseTime(stop.nextCheckAt);
|
|
236
|
+
if (stop.decision === "blocked" && nextCheck !== undefined && nextCheck > stop.mtimeMs) {
|
|
237
|
+
return { at: Math.min(nextCheck, stop.mtimeMs + LEAD_LANE_KEEP_ALIVE_NEXT_CHECK_MAX_DELAY_MS), reason: "next_check_at" };
|
|
238
|
+
}
|
|
230
239
|
const previousResume = resumeEvents.filter((event) => event.at <= stop.mtimeMs).at(-1);
|
|
231
240
|
const terminalPlusBackoff = stop.mtimeMs + LEAD_LANE_KEEP_ALIVE_BASE_BACKOFF_MS;
|
|
232
241
|
let due = Math.max(terminalPlusBackoff, previousResume?.nextEligibleAt ?? 0);
|
|
233
242
|
let reason = "backoff";
|
|
234
|
-
const nextCheck = parseTime(stop.nextCheckAt);
|
|
235
243
|
if (nextCheck !== undefined && nextCheck > stop.mtimeMs) {
|
|
236
244
|
due = Math.max(due, Math.min(nextCheck, stop.mtimeMs + MAX_NEXT_CHECK_DELAY_MS));
|
|
237
245
|
reason = "next_check_at";
|
|
@@ -329,6 +337,38 @@ function laneWasKeepAliveOffAtStop(lane, stoppedAt) {
|
|
|
329
337
|
const optedInAt = parseTime(lane.keep_alive_opted_in_at);
|
|
330
338
|
return optedInAt === undefined || optedInAt > stoppedAt;
|
|
331
339
|
}
|
|
340
|
+
function blockedKeepAliveOutcome(lane, stop, nextEnvelopeMs) {
|
|
341
|
+
const terminalAt = lane.last_envelope?.ref === `envelopes/${stop.name}`
|
|
342
|
+
? parseTime(lane.last_envelope.at)
|
|
343
|
+
: undefined;
|
|
344
|
+
const candidates = (lane.status_gate_history ?? []).flatMap((entry) => {
|
|
345
|
+
if (entry.event !== "keep_alive_blocked_resumed" && entry.event !== "keep_alive_blocked_skipped")
|
|
346
|
+
return [];
|
|
347
|
+
const at = parseTime(entry.at);
|
|
348
|
+
const terminal = parseTime(entry.terminal_at);
|
|
349
|
+
if (at === undefined || terminal === undefined)
|
|
350
|
+
return [];
|
|
351
|
+
if (nextEnvelopeMs !== undefined && at >= nextEnvelopeMs)
|
|
352
|
+
return [];
|
|
353
|
+
const sameStop = terminalAt !== undefined
|
|
354
|
+
? terminal === terminalAt
|
|
355
|
+
: Math.abs(terminal - stop.mtimeMs) <= 10 * 60_000;
|
|
356
|
+
if (!sameStop)
|
|
357
|
+
return [];
|
|
358
|
+
if (entry.event === "keep_alive_blocked_skipped" && entry.reason !== "flag_off")
|
|
359
|
+
return [];
|
|
360
|
+
return [{
|
|
361
|
+
status: entry.event === "keep_alive_blocked_resumed" ? "woken" : "skipped",
|
|
362
|
+
...(entry.reason === undefined ? {} : { reason: entry.reason }),
|
|
363
|
+
at: entry.at,
|
|
364
|
+
atMs: at,
|
|
365
|
+
}];
|
|
366
|
+
});
|
|
367
|
+
const latest = candidates.sort((left, right) => right.atMs - left.atMs)[0];
|
|
368
|
+
if (!latest)
|
|
369
|
+
return undefined;
|
|
370
|
+
return { status: latest.status, ...(latest.reason === undefined ? {} : { reason: latest.reason }), at: latest.at };
|
|
371
|
+
}
|
|
332
372
|
function firstRestartAfter(lane, stop, nextEnvelopeMs, workerResumeEvents) {
|
|
333
373
|
const recordedWorkerResume = workerResumeEvents
|
|
334
374
|
.filter((event) => event.at > stop.mtimeMs && (nextEnvelopeMs === undefined || event.at < nextEnvelopeMs))
|
|
@@ -449,15 +489,18 @@ export function buildKeepAliveAudit(input) {
|
|
|
449
489
|
const { allMtimes, stops } = readStopEnvelopes(input.hqRoot, lane.lane_id);
|
|
450
490
|
const keepAliveResumeEvents = readKeepAliveResumeEvents(input.hqRoot, lane.lane_id);
|
|
451
491
|
const laneWorkerResumeEvents = readLaneWorkerResumeEvents(input.hqRoot, lane.lane_id);
|
|
452
|
-
const
|
|
492
|
+
const terminalClassifications = readTerminalClassifications(input.hqRoot, lane.lane_id);
|
|
453
493
|
for (const stop of stops) {
|
|
454
494
|
if (stop.mtimeMs < sinceMs || stop.mtimeMs > nowMs)
|
|
455
495
|
continue;
|
|
456
|
-
if (!
|
|
496
|
+
if (!terminalClassifications.some((classification) => classification.state === stop.decision && classification.at >= stop.mtimeMs && classification.at - stop.mtimeMs <= 10 * 60_000))
|
|
457
497
|
continue;
|
|
458
498
|
const nextEnvelopeMs = allMtimes.find((mtime) => mtime > stop.mtimeMs);
|
|
499
|
+
const blockedOutcome = stop.decision === "blocked" ? blockedKeepAliveOutcome(lane, stop, nextEnvelopeMs) : undefined;
|
|
459
500
|
const restart = firstRestartAfter(lane, stop, nextEnvelopeMs, laneWorkerResumeEvents);
|
|
460
501
|
const due = dueTime(stop, keepAliveResumeEvents);
|
|
502
|
+
const reportBlockedOutcome = blockedOutcome?.status === "woken" ||
|
|
503
|
+
(blockedOutcome?.status === "skipped" && due.at <= nowMs);
|
|
461
504
|
rows.push({
|
|
462
505
|
lane: lane.lane_id,
|
|
463
506
|
project: lane.project_id,
|
|
@@ -467,6 +510,10 @@ export function buildKeepAliveAudit(input) {
|
|
|
467
510
|
due_at: iso(due.at),
|
|
468
511
|
due_reason: due.reason,
|
|
469
512
|
restart,
|
|
513
|
+
...(reportBlockedOutcome && blockedOutcome ? {
|
|
514
|
+
blocked_outcome: blockedOutcome.status,
|
|
515
|
+
...(blockedOutcome.reason === undefined ? {} : { blocked_outcome_reason: blockedOutcome.reason }),
|
|
516
|
+
} : {}),
|
|
470
517
|
verdict: classifyVerdict({ dueAt: due.at, stoppedAt: stop.mtimeMs, nextCheckAt: stop.nextCheckAt, restart, lane, nowMs, resumeEvents: keepAliveResumeEvents, ...(nextEnvelopeMs === undefined ? {} : { nextEnvelopeAt: nextEnvelopeMs }), hqRoot: input.hqRoot }),
|
|
471
518
|
});
|
|
472
519
|
}
|
|
@@ -514,7 +561,7 @@ export function buildKeepAliveAudit(input) {
|
|
|
514
561
|
};
|
|
515
562
|
}
|
|
516
563
|
export function formatKeepAliveAuditText(result) {
|
|
517
|
-
const headings = ["LANE", "PROJECT", "PROFILE", "STOPPED", "NEXT_CHECK", "DUE", "RESTART", "VERDICT"];
|
|
564
|
+
const headings = ["LANE", "PROJECT", "PROFILE", "STOPPED", "NEXT_CHECK", "DUE", "RESTART", "OUTCOME", "VERDICT"];
|
|
518
565
|
const values = result.rows.map((row) => [
|
|
519
566
|
row.lane,
|
|
520
567
|
row.project,
|
|
@@ -523,6 +570,7 @@ export function formatKeepAliveAuditText(result) {
|
|
|
523
570
|
row.next_check_at ?? "—",
|
|
524
571
|
row.due_at,
|
|
525
572
|
row.restart ? `${row.restart.at ?? "untimed"} ${row.restart.trigger}${row.restart.caller ? ` (${row.restart.caller})` : ""}` : "—",
|
|
573
|
+
row.blocked_outcome === "woken" ? "woken" : row.blocked_outcome === "skipped" ? `due, not woken: ${(row.blocked_outcome_reason ?? "unknown").replaceAll("_", " ")}` : "—",
|
|
526
574
|
row.verdict,
|
|
527
575
|
]);
|
|
528
576
|
const widths = headings.map((heading, index) => Math.max(heading.length, ...values.map((row) => row[index].length)));
|
|
@@ -27,8 +27,9 @@ import { checkLaneDiskSpace, configuredCodexRolloutHomes, lowDiskGuardEnabledFor
|
|
|
27
27
|
import { laneCompanyUidSource, resolveLaneCompany } from "./lane-company.js";
|
|
28
28
|
import { appendDropboxMessage } from "./dropbox.js";
|
|
29
29
|
import { DEFAULT_RUNAWAY_SEARCH_GUARD_CONFIG, listHostProcesses, runRunawayToolGuard, RUNAWAY_TOOL_GUARD_FLAG, } from "./runaway-tool-guard.js";
|
|
30
|
-
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_NEXT_CHECK_MAX_DELAY_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";
|
|
30
|
+
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_NEXT_CHECK_MAX_DELAY_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, keepAliveNextCheckAtMs, keepAliveExclusion, loadRetiredLaneIds, leadLaneKeepAliveBackoffMs, undeliveredAnsweredQuestions, } from "./keep-alive.js";
|
|
31
31
|
import { providerSupportsSessionResume } from "./delivery.js";
|
|
32
|
+
import { isIsoDateTime } from "./types.js";
|
|
32
33
|
import { applyDefaultQuotaHold, clearAccountQuotaAfterSuccessfulTurnBestEffort, readQuotaSignal, recordAccountQuota, } from "./quota.js";
|
|
33
34
|
import { answeredQuestionEntries, questionRecordRefusalCode, raiseEnvelopeQuestions, resolveQuestionCompanyWithFallback, } from "./questions.js";
|
|
34
35
|
import { SILENCE_MS, appendWatcherEvent, classifyLaneExit, isPidAlive, missingExpectedEnvelopeFile, readLatestEnvelopeFile, readLatestEnvelopeRecord, watcherEventsPath, watcherOutLogPath, writeWatcherDecision, } from "./watcher.js";
|
|
@@ -2252,6 +2253,9 @@ async function resumeBlockedKeepAlive(hqRoot, lane, questions, summary, deps, no
|
|
|
2252
2253
|
}
|
|
2253
2254
|
const key = keepAliveCompanyKey(company);
|
|
2254
2255
|
if (keepAliveFlags.get(key) !== true || blockedFlags.get(key) !== true) {
|
|
2256
|
+
if (keepAliveFlags.get(key) === true && blockedFlags.get(key) !== true) {
|
|
2257
|
+
recordBlockedKeepAliveFlagOff(hqRoot, lane, summary, deps);
|
|
2258
|
+
}
|
|
2255
2259
|
summary.skipped.push({ kind: "lane", id: lane.lane_id, why: "keep_alive_blocked" });
|
|
2256
2260
|
return;
|
|
2257
2261
|
}
|
|
@@ -2323,10 +2327,22 @@ async function resumeBlockedKeepAlive(hqRoot, lane, questions, summary, deps, no
|
|
|
2323
2327
|
summary.skipped.push({ kind: "lane", id: lane.lane_id, why: "keep_alive_exhausted" });
|
|
2324
2328
|
return;
|
|
2325
2329
|
}
|
|
2326
|
-
|
|
2330
|
+
const blockedEnvelope = readLatestEnvelopeRecord(hqRoot, lane.lane_id)?.envelope;
|
|
2331
|
+
const nextCheckValue = blockedEnvelope?.decision === "blocked" ? blockedEnvelope.next_check_at : undefined;
|
|
2332
|
+
const nextCheckValid = typeof nextCheckValue === "string" && isIsoDateTime(nextCheckValue) &&
|
|
2333
|
+
Date.parse(nextCheckValue) > terminalMs;
|
|
2334
|
+
const nextCheckMs = nextCheckValid
|
|
2335
|
+
? keepAliveNextCheckAtMs(nextCheckValue, now.getTime(), terminalMs)
|
|
2336
|
+
: undefined;
|
|
2337
|
+
if (nextCheckMs !== undefined && nextCheckMs > now.getTime()) {
|
|
2338
|
+
appendBlockedKeepAliveHistory(hqRoot, lane, terminalAt, now.toISOString(), "blocked_grace", deps);
|
|
2339
|
+
return;
|
|
2340
|
+
}
|
|
2341
|
+
if (!nextCheckValid && now.getTime() - terminalMs < LEAD_LANE_KEEP_ALIVE_BLOCKED_GRACE_MS) {
|
|
2327
2342
|
appendBlockedKeepAliveHistory(hqRoot, lane, terminalAt, now.toISOString(), "blocked_grace", deps);
|
|
2328
2343
|
return;
|
|
2329
2344
|
}
|
|
2345
|
+
const resumeReason = nextCheckValid ? "next_check_at_due" : "backoff";
|
|
2330
2346
|
const latest = deps.readKeepAliveRecheckLane?.(hqRoot, lane.lane_id) ?? readLane(hqRoot, lane.lane_id);
|
|
2331
2347
|
if (latest.version !== lane.version || latest.state !== "blocked" || latest.last_envelope?.decision !== "blocked" || latest.last_envelope.at !== terminalAt)
|
|
2332
2348
|
return;
|
|
@@ -2389,6 +2405,7 @@ async function resumeBlockedKeepAlive(hqRoot, lane, questions, summary, deps, no
|
|
|
2389
2405
|
at: now.toISOString(),
|
|
2390
2406
|
terminal_at: terminalAt,
|
|
2391
2407
|
resume_queued: true,
|
|
2408
|
+
reason: resumeReason,
|
|
2392
2409
|
}),
|
|
2393
2410
|
}), deps.dryRun);
|
|
2394
2411
|
if (queued.changed) {
|
|
@@ -2399,7 +2416,7 @@ async function resumeBlockedKeepAlive(hqRoot, lane, questions, summary, deps, no
|
|
|
2399
2416
|
at: now.toISOString(),
|
|
2400
2417
|
attempt: (lane.keep_alive_resume_count ?? 0) + 1,
|
|
2401
2418
|
resumed_from: "blocked",
|
|
2402
|
-
reason:
|
|
2419
|
+
reason: resumeReason,
|
|
2403
2420
|
...(accountSelection !== undefined
|
|
2404
2421
|
? { account_selection: keepAliveAccountSelectionEvidence(accountSelection) }
|
|
2405
2422
|
: {}),
|
|
@@ -2407,6 +2424,32 @@ async function resumeBlockedKeepAlive(hqRoot, lane, questions, summary, deps, no
|
|
|
2407
2424
|
}
|
|
2408
2425
|
}
|
|
2409
2426
|
}
|
|
2427
|
+
function recordBlockedKeepAliveFlagOff(hqRoot, lane, summary, deps) {
|
|
2428
|
+
const terminalAt = lane.last_envelope?.at;
|
|
2429
|
+
if (deps.dryRun || lane.last_envelope?.decision !== "blocked" || terminalAt === undefined)
|
|
2430
|
+
return;
|
|
2431
|
+
const envelope = readLatestEnvelopeRecord(hqRoot, lane.lane_id)?.envelope;
|
|
2432
|
+
const nextCheckAt = envelope?.decision === "blocked" ? envelope.next_check_at : undefined;
|
|
2433
|
+
const terminalMs = Date.parse(lane.last_envelope.at);
|
|
2434
|
+
if (typeof nextCheckAt !== "string" || !isIsoDateTime(nextCheckAt) || Date.parse(nextCheckAt) <= terminalMs)
|
|
2435
|
+
return;
|
|
2436
|
+
if (lane.status_gate_history?.some((entry) => entry.event === "keep_alive_blocked_skipped" && entry.terminal_at === terminalAt && entry.reason === "flag_off"))
|
|
2437
|
+
return;
|
|
2438
|
+
try {
|
|
2439
|
+
casLaneIf(hqRoot, lane.lane_id, (current) => current.version === lane.version && current.state === "blocked" && current.last_envelope?.at === terminalAt, (current) => ({
|
|
2440
|
+
...current,
|
|
2441
|
+
status_gate_history: appendCappedHistory(current.status_gate_history ?? [], {
|
|
2442
|
+
event: "keep_alive_blocked_skipped",
|
|
2443
|
+
at: deps.now().toISOString(),
|
|
2444
|
+
terminal_at: terminalAt,
|
|
2445
|
+
reason: "flag_off",
|
|
2446
|
+
}),
|
|
2447
|
+
}), false);
|
|
2448
|
+
}
|
|
2449
|
+
catch (err) {
|
|
2450
|
+
markDirty(summary, { kind: "lane", id: lane.lane_id, why: `blocked keep-alive skip record failed: ${err instanceof Error ? err.message : String(err)}` });
|
|
2451
|
+
}
|
|
2452
|
+
}
|
|
2410
2453
|
function appendBlockedKeepAliveHistory(hqRoot, lane, terminalAt, at, reason, deps) {
|
|
2411
2454
|
if (deps.dryRun || lane.status_gate_history?.some((entry) => entry.event === "keep_alive_blocked_grace" && entry.terminal_at === terminalAt && entry.resume_skipped_reason === reason))
|
|
2412
2455
|
return;
|
|
@@ -445,10 +445,11 @@ export interface KrFreshnessState {
|
|
|
445
445
|
stalled_fingerprint?: string;
|
|
446
446
|
}
|
|
447
447
|
export interface KeepAliveBlockedHistoryEntry {
|
|
448
|
-
event: "keep_alive_blocked_grace" | "keep_alive_blocked_resumed" | "keep_alive_blocked_escalated";
|
|
448
|
+
event: "keep_alive_blocked_grace" | "keep_alive_blocked_resumed" | "keep_alive_blocked_escalated" | "keep_alive_blocked_skipped";
|
|
449
449
|
at: string;
|
|
450
450
|
terminal_at: string;
|
|
451
451
|
resume_skipped_reason?: "open_question" | "blocked_grace";
|
|
452
|
+
reason?: "flag_off" | "backoff" | "next_check_at_due";
|
|
452
453
|
resume_queued?: boolean;
|
|
453
454
|
}
|
|
454
455
|
export interface KeepAliveResumeHistoryEntry {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type ResidentPackagePath = "auth-login" | "bot" | "childWrapper" | "coreJobRun" | "login" | "mcp" | "daemon";
|
|
1
|
+
export type ResidentPackagePath = "agent-kit" | "agent-mcp" | "auth-login" | "bot" | "childWrapper" | "coreJobRun" | "login" | "mcp" | "daemon" | "mesh-daemon" | "lanes-watch" | "monitor-wait" | "monitor-run";
|
|
2
2
|
/** Package imports reachable lazily from each resident command's runtime graph. */
|
|
3
3
|
export declare const RESIDENT_PACKAGE_MODULES: Record<ResidentPackagePath, readonly string[]>;
|
|
4
4
|
export declare class ResidentPackagePreloadError extends Error {
|
|
@@ -10,4 +10,11 @@ export declare class ResidentPackagePreloadError extends Error {
|
|
|
10
10
|
export declare function reportResidentPackagePreloadFailure(error: unknown): void;
|
|
11
11
|
/** Resolve every lazy package module before a resident command releases its lease. */
|
|
12
12
|
export declare function preloadResidentPackageModules(residentPath: ResidentPackagePath, load?: () => Promise<unknown>): Promise<void>;
|
|
13
|
+
export interface ResidentCommandLeaseOptions {
|
|
14
|
+
preload?: () => Promise<void>;
|
|
15
|
+
reportFailure?: (error: unknown) => void;
|
|
16
|
+
release?: () => void;
|
|
17
|
+
}
|
|
18
|
+
/** Start a resident loop only after its declared lazy package imports are loaded. */
|
|
19
|
+
export declare function runResidentCommandWithPackageLease<T>(residentPath: ResidentPackagePath, run: () => Promise<T>, options?: ResidentCommandLeaseOptions): Promise<T>;
|
|
13
20
|
//# sourceMappingURL=resident-package-preload.d.ts.map
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import { releaseCurrentPackageUseLease, releasePackageUseLeaseAfterResidentPreload, } from "./package-use-lease.js";
|
|
1
2
|
/** Package imports reachable lazily from each resident command's runtime graph. */
|
|
2
3
|
export const RESIDENT_PACKAGE_MODULES = {
|
|
4
|
+
"agent-kit": ["../sentry.js", "../lib/plan-limit-nag.js", "../utils/plan-gate-error.js", "open"],
|
|
5
|
+
"agent-mcp": ["../sentry.js", "../lib/plan-limit-nag.js", "../utils/plan-gate-error.js", "../commands/access.js", "open"],
|
|
3
6
|
login: [
|
|
4
7
|
"../lib/mesh/api.js",
|
|
5
8
|
"../lib/work-context/config.js",
|
|
@@ -40,8 +43,31 @@ export const RESIDENT_PACKAGE_MODULES = {
|
|
|
40
43
|
"../utils/staged-npm-install.js",
|
|
41
44
|
"open",
|
|
42
45
|
],
|
|
46
|
+
"mesh-daemon": ["../sentry.js", "../lib/plan-limit-nag.js", "../utils/plan-gate-error.js", "open"],
|
|
47
|
+
"lanes-watch": ["../sentry.js", "../lib/plan-limit-nag.js", "../utils/plan-gate-error.js", "open"],
|
|
48
|
+
"monitor-wait": ["../sentry.js", "../lib/plan-limit-nag.js", "../utils/plan-gate-error.js", "open"],
|
|
49
|
+
"monitor-run": ["../commands/lanes.js", "../lib/plan-limit-nag.js", "../utils/plan-gate-error.js", "../sentry.js", "open"],
|
|
43
50
|
};
|
|
44
51
|
const LOADERS = {
|
|
52
|
+
"agent-kit": [
|
|
53
|
+
async () => {
|
|
54
|
+
const { preloadSentrySdk } = await import("../sentry.js");
|
|
55
|
+
preloadSentrySdk();
|
|
56
|
+
},
|
|
57
|
+
() => import("../lib/plan-limit-nag.js"),
|
|
58
|
+
() => import("../utils/plan-gate-error.js"),
|
|
59
|
+
() => import("open"),
|
|
60
|
+
],
|
|
61
|
+
"agent-mcp": [
|
|
62
|
+
async () => {
|
|
63
|
+
const { preloadSentrySdk } = await import("../sentry.js");
|
|
64
|
+
preloadSentrySdk();
|
|
65
|
+
},
|
|
66
|
+
() => import("../lib/plan-limit-nag.js"),
|
|
67
|
+
() => import("../utils/plan-gate-error.js"),
|
|
68
|
+
() => import("../commands/access.js"),
|
|
69
|
+
() => import("open"),
|
|
70
|
+
],
|
|
45
71
|
login: [
|
|
46
72
|
() => import("../lib/mesh/api.js"),
|
|
47
73
|
() => import("../lib/work-context/config.js"),
|
|
@@ -101,6 +127,43 @@ const LOADERS = {
|
|
|
101
127
|
() => import("../utils/staged-npm-install.js"),
|
|
102
128
|
() => import("open"),
|
|
103
129
|
],
|
|
130
|
+
"mesh-daemon": [
|
|
131
|
+
async () => {
|
|
132
|
+
const { preloadSentrySdk } = await import("../sentry.js");
|
|
133
|
+
preloadSentrySdk();
|
|
134
|
+
},
|
|
135
|
+
() => import("../lib/plan-limit-nag.js"),
|
|
136
|
+
() => import("../utils/plan-gate-error.js"),
|
|
137
|
+
() => import("open"),
|
|
138
|
+
],
|
|
139
|
+
"lanes-watch": [
|
|
140
|
+
async () => {
|
|
141
|
+
const { preloadSentrySdk } = await import("../sentry.js");
|
|
142
|
+
preloadSentrySdk();
|
|
143
|
+
},
|
|
144
|
+
() => import("../lib/plan-limit-nag.js"),
|
|
145
|
+
() => import("../utils/plan-gate-error.js"),
|
|
146
|
+
() => import("open"),
|
|
147
|
+
],
|
|
148
|
+
"monitor-wait": [
|
|
149
|
+
async () => {
|
|
150
|
+
const { preloadSentrySdk } = await import("../sentry.js");
|
|
151
|
+
preloadSentrySdk();
|
|
152
|
+
},
|
|
153
|
+
() => import("../lib/plan-limit-nag.js"),
|
|
154
|
+
() => import("../utils/plan-gate-error.js"),
|
|
155
|
+
() => import("open"),
|
|
156
|
+
],
|
|
157
|
+
"monitor-run": [
|
|
158
|
+
() => import("../commands/lanes.js"),
|
|
159
|
+
() => import("../lib/plan-limit-nag.js"),
|
|
160
|
+
() => import("../utils/plan-gate-error.js"),
|
|
161
|
+
async () => {
|
|
162
|
+
const { preloadSentrySdk } = await import("../sentry.js");
|
|
163
|
+
preloadSentrySdk();
|
|
164
|
+
},
|
|
165
|
+
() => import("open"),
|
|
166
|
+
],
|
|
104
167
|
};
|
|
105
168
|
export class ResidentPackagePreloadError extends Error {
|
|
106
169
|
moduleSpecifier;
|
|
@@ -141,4 +204,9 @@ export async function preloadResidentPackageModules(residentPath, load) {
|
|
|
141
204
|
}
|
|
142
205
|
}));
|
|
143
206
|
}
|
|
207
|
+
/** Start a resident loop only after its declared lazy package imports are loaded. */
|
|
208
|
+
export async function runResidentCommandWithPackageLease(residentPath, run, options = {}) {
|
|
209
|
+
await releasePackageUseLeaseAfterResidentPreload(options.preload ?? (() => preloadResidentPackageModules(residentPath)), options.reportFailure ?? reportResidentPackagePreloadFailure, options.release ?? releaseCurrentPackageUseLease);
|
|
210
|
+
return run();
|
|
211
|
+
}
|
|
144
212
|
//# sourceMappingURL=resident-package-preload.js.map
|
package/docs/anywhere-runtime.md
CHANGED
|
@@ -73,7 +73,9 @@ the named-pipe transport without Unix mode or owner fields. Use `hq doctor
|
|
|
73
73
|
--json` for the same fields as structured details. The hqd health operation is
|
|
74
74
|
read-only and available while the runtime flag is off; it reports only the
|
|
75
75
|
current directory's binding and never returns authentication tokens. A Unix
|
|
76
|
-
socket that cannot be verified as user-only produces a warning.
|
|
76
|
+
socket that cannot be verified as user-only produces a warning. When a Unix
|
|
77
|
+
socket is unreachable, doctor recommends installing the daemon service if it is
|
|
78
|
+
absent or restarting it if it is installed. If an older
|
|
77
79
|
resident daemon does not support the health operation, doctor falls back to
|
|
78
80
|
`ping` for reachability, version and uptime, then warns that a restart is needed
|
|
79
81
|
for full diagnostics.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@indigoai-us/hq-cli",
|
|
3
|
-
"version": "5.342.
|
|
3
|
+
"version": "5.342.13",
|
|
4
4
|
"description": "HQ by Indigo management CLI — modules and cloud sync",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"js-yaml": "^4.1.0",
|
|
60
60
|
"mqtt": "5.15.2",
|
|
61
61
|
"open": "^10.1.0",
|
|
62
|
-
"semver": "^7.
|
|
62
|
+
"semver": "^7.8.5",
|
|
63
63
|
"simple-git": "^3.27.0",
|
|
64
64
|
"smol-toml": "1.6.1",
|
|
65
65
|
"varlock": "1.21.1"
|
|
@@ -69,11 +69,11 @@
|
|
|
69
69
|
"@types/better-sqlite3": "^7.6.13",
|
|
70
70
|
"@types/js-yaml": "^4.0.9",
|
|
71
71
|
"@types/node": "^22.0.0",
|
|
72
|
-
"@types/semver": "^7.
|
|
72
|
+
"@types/semver": "^7.8.0",
|
|
73
73
|
"@vitest/coverage-v8": "4.1.6",
|
|
74
74
|
"aedes": "1.1.2",
|
|
75
75
|
"aws-sdk-client-mock": "^4.1.0",
|
|
76
|
-
"esbuild": "0.
|
|
76
|
+
"esbuild": "0.28.2",
|
|
77
77
|
"eslint": "^10.5.0",
|
|
78
78
|
"husky": "^9.1.7",
|
|
79
79
|
"lint-staged": "^15.5.2",
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pluggable remote vault DB engine interface (vault-databases US-007).
|
|
3
|
-
*
|
|
4
|
-
* Default shipped adapter id: aurora-dsql.
|
|
5
|
-
* This module is interface + registry only — no production AWS provision here.
|
|
6
|
-
*/
|
|
7
|
-
/** Connection material written only to secrets stores — never CLI stdout. */
|
|
8
|
-
export interface RemoteConnectionSecretPayload {
|
|
9
|
-
/** Opaque secret body (e.g. JSON with host/user/password or IAM token recipe). */
|
|
10
|
-
payload: Record<string, unknown>;
|
|
11
|
-
/** Suggested vault/Secrets Manager key suffix (namespaced by company elsewhere). */
|
|
12
|
-
secretNameHint: string;
|
|
13
|
-
}
|
|
14
|
-
export interface RemoteProvisionRequest {
|
|
15
|
-
companyUid: string;
|
|
16
|
-
companySlug: string;
|
|
17
|
-
region?: string;
|
|
18
|
-
/** Engine-specific options (cluster params, etc.). */
|
|
19
|
-
options?: Record<string, unknown>;
|
|
20
|
-
}
|
|
21
|
-
export interface RemoteProvisionResult {
|
|
22
|
-
engineId: string;
|
|
23
|
-
resourceArn: string;
|
|
24
|
-
region: string;
|
|
25
|
-
status: "ready" | "provisioning" | "failed" | "existing";
|
|
26
|
-
/** Secret payload to store — callers must not print. */
|
|
27
|
-
connectionSecret: RemoteConnectionSecretPayload;
|
|
28
|
-
}
|
|
29
|
-
export interface RemoteHealth {
|
|
30
|
-
engineId: string;
|
|
31
|
-
resourceArn: string;
|
|
32
|
-
healthy: boolean;
|
|
33
|
-
status: string;
|
|
34
|
-
checkedAt: string;
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Remote Postgres-class engine adapter.
|
|
38
|
-
* Implementations live in control plane (hq-pro) for real AWS; CLI may mock.
|
|
39
|
-
*/
|
|
40
|
-
export interface RemoteDbEngine {
|
|
41
|
-
readonly id: string;
|
|
42
|
-
provision(req: RemoteProvisionRequest): Promise<RemoteProvisionResult>;
|
|
43
|
-
deprovision(companyUid: string, resourceArn: string): Promise<void>;
|
|
44
|
-
connectionSecretPayload(companyUid: string, resourceArn: string): Promise<RemoteConnectionSecretPayload>;
|
|
45
|
-
healthCheck(resourceArn: string): Promise<RemoteHealth>;
|
|
46
|
-
}
|
|
47
|
-
/** Default engine selection (PRD decision). */
|
|
48
|
-
export declare const DEFAULT_REMOTE_ENGINE_ID = "aurora-dsql";
|
|
49
|
-
export declare function registerRemoteEngine(engine: RemoteDbEngine): void;
|
|
50
|
-
export declare function getRemoteEngine(id?: string): RemoteDbEngine;
|
|
51
|
-
export declare function listRemoteEngineIds(): string[];
|
|
52
|
-
/** Clear registry (tests only). */
|
|
53
|
-
export declare function _resetRemoteEngineRegistryForTests(): void;
|
|
54
|
-
/**
|
|
55
|
-
* In-memory mock engine for unit tests — no AWS.
|
|
56
|
-
*/
|
|
57
|
-
export declare function createMockRemoteEngine(id?: string): RemoteDbEngine;
|
|
58
|
-
//# sourceMappingURL=remote-engine.d.ts.map
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pluggable remote vault DB engine interface (vault-databases US-007).
|
|
3
|
-
*
|
|
4
|
-
* Default shipped adapter id: aurora-dsql.
|
|
5
|
-
* This module is interface + registry only — no production AWS provision here.
|
|
6
|
-
*/
|
|
7
|
-
/** Default engine selection (PRD decision). */
|
|
8
|
-
export const DEFAULT_REMOTE_ENGINE_ID = "aurora-dsql";
|
|
9
|
-
const registry = new Map();
|
|
10
|
-
export function registerRemoteEngine(engine) {
|
|
11
|
-
registry.set(engine.id, engine);
|
|
12
|
-
}
|
|
13
|
-
export function getRemoteEngine(id = DEFAULT_REMOTE_ENGINE_ID) {
|
|
14
|
-
const engine = registry.get(id);
|
|
15
|
-
if (!engine) {
|
|
16
|
-
throw new Error(`remote DB engine not registered: ${id} (available: ${[...registry.keys()].join(", ") || "none"})`);
|
|
17
|
-
}
|
|
18
|
-
return engine;
|
|
19
|
-
}
|
|
20
|
-
export function listRemoteEngineIds() {
|
|
21
|
-
return [...registry.keys()];
|
|
22
|
-
}
|
|
23
|
-
/** Clear registry (tests only). */
|
|
24
|
-
export function _resetRemoteEngineRegistryForTests() {
|
|
25
|
-
registry.clear();
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* In-memory mock engine for unit tests — no AWS.
|
|
29
|
-
*/
|
|
30
|
-
export function createMockRemoteEngine(id = "mock") {
|
|
31
|
-
const resources = new Map();
|
|
32
|
-
return {
|
|
33
|
-
id,
|
|
34
|
-
async provision(req) {
|
|
35
|
-
const existing = [...resources.entries()].find(([, v]) => v.companyUid === req.companyUid);
|
|
36
|
-
if (existing) {
|
|
37
|
-
const [arn, meta] = existing;
|
|
38
|
-
return {
|
|
39
|
-
engineId: id,
|
|
40
|
-
resourceArn: arn,
|
|
41
|
-
region: meta.region,
|
|
42
|
-
status: "existing",
|
|
43
|
-
connectionSecret: {
|
|
44
|
-
payload: { mock: true, companyUid: req.companyUid },
|
|
45
|
-
secretNameHint: `db/remote/${req.companySlug}`,
|
|
46
|
-
},
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
const arn = `arn:mock:db:${req.companyUid}`;
|
|
50
|
-
const region = req.region ?? "us-east-1";
|
|
51
|
-
resources.set(arn, { companyUid: req.companyUid, region });
|
|
52
|
-
return {
|
|
53
|
-
engineId: id,
|
|
54
|
-
resourceArn: arn,
|
|
55
|
-
region,
|
|
56
|
-
status: "ready",
|
|
57
|
-
connectionSecret: {
|
|
58
|
-
payload: { mock: true, companyUid: req.companyUid },
|
|
59
|
-
secretNameHint: `db/remote/${req.companySlug}`,
|
|
60
|
-
},
|
|
61
|
-
};
|
|
62
|
-
},
|
|
63
|
-
async deprovision(companyUid, resourceArn) {
|
|
64
|
-
const meta = resources.get(resourceArn);
|
|
65
|
-
if (meta && meta.companyUid === companyUid) {
|
|
66
|
-
resources.delete(resourceArn);
|
|
67
|
-
}
|
|
68
|
-
},
|
|
69
|
-
async connectionSecretPayload(companyUid, resourceArn) {
|
|
70
|
-
return {
|
|
71
|
-
payload: { mock: true, companyUid, resourceArn },
|
|
72
|
-
secretNameHint: `db/remote/${companyUid}`,
|
|
73
|
-
};
|
|
74
|
-
},
|
|
75
|
-
async healthCheck(resourceArn) {
|
|
76
|
-
const ok = resources.has(resourceArn);
|
|
77
|
-
return {
|
|
78
|
-
engineId: id,
|
|
79
|
-
resourceArn,
|
|
80
|
-
healthy: ok,
|
|
81
|
-
status: ok ? "ready" : "missing",
|
|
82
|
-
checkedAt: new Date().toISOString(),
|
|
83
|
-
};
|
|
84
|
-
},
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
//# sourceMappingURL=remote-engine.js.map
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Remote SQL helper with injected credentials and executor (US-010).
|
|
3
|
-
*
|
|
4
|
-
* Connection string never appears in argv, logs, or stdout.
|
|
5
|
-
* The CLI command does not currently wire this helper to a remote executor.
|
|
6
|
-
*/
|
|
7
|
-
export interface RemoteSqlOptions {
|
|
8
|
-
company: string;
|
|
9
|
-
sql: string;
|
|
10
|
-
/** Injected secret material — never log. */
|
|
11
|
-
getConnectionConfig: () => Promise<Record<string, unknown>>;
|
|
12
|
-
/** Execute against remote; tests inject mock. */
|
|
13
|
-
execute?: (config: Record<string, unknown>, sql: string) => Promise<Record<string, unknown>[]>;
|
|
14
|
-
}
|
|
15
|
-
export interface RemoteSqlResult {
|
|
16
|
-
rows: Record<string, unknown>[];
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* Run SQL on remote tier using vault-bound credentials.
|
|
20
|
-
*/
|
|
21
|
-
export declare function runRemoteSql(opts: RemoteSqlOptions): Promise<RemoteSqlResult>;
|
|
22
|
-
//# sourceMappingURL=remote-sql.d.ts.map
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Remote SQL helper with injected credentials and executor (US-010).
|
|
3
|
-
*
|
|
4
|
-
* Connection string never appears in argv, logs, or stdout.
|
|
5
|
-
* The CLI command does not currently wire this helper to a remote executor.
|
|
6
|
-
*/
|
|
7
|
-
function assertNoLeak(label, value) {
|
|
8
|
-
const s = typeof value === "string" ? value : JSON.stringify(value);
|
|
9
|
-
if (/postgres:\/\//i.test(s) || /postgresql:\/\//i.test(s)) {
|
|
10
|
-
throw new Error(`${label}: connection string leak blocked`);
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Run SQL on remote tier using vault-bound credentials.
|
|
15
|
-
*/
|
|
16
|
-
export async function runRemoteSql(opts) {
|
|
17
|
-
if (!opts.sql?.trim()) {
|
|
18
|
-
throw new Error("SQL statement is required");
|
|
19
|
-
}
|
|
20
|
-
let config;
|
|
21
|
-
try {
|
|
22
|
-
config = await opts.getConnectionConfig();
|
|
23
|
-
}
|
|
24
|
-
catch (e) {
|
|
25
|
-
const msg = e instanceof Error ? e.message : "no remote binding";
|
|
26
|
-
throw new Error(`remote SQL unavailable: ${msg}`);
|
|
27
|
-
}
|
|
28
|
-
assertNoLeak("connection config", config);
|
|
29
|
-
if (!opts.execute) {
|
|
30
|
-
throw new Error("remote SQL executor not configured in this build (adapter pending live DSQL wire-up)");
|
|
31
|
-
}
|
|
32
|
-
const rows = await opts.execute(config, opts.sql);
|
|
33
|
-
assertNoLeak("remote sql rows", rows);
|
|
34
|
-
return { rows };
|
|
35
|
-
}
|
|
36
|
-
//# sourceMappingURL=remote-sql.js.map
|