@indigoai-us/hq-cli 5.134.0 → 5.135.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 +22 -0
- package/dist/command-catalog.generated.d.ts +40 -0
- package/dist/command-catalog.generated.js +52 -0
- package/dist/commands/lanes.d.ts +4 -2
- package/dist/commands/lanes.js +150 -2
- package/dist/lib/lanes/config.d.ts +7 -0
- package/dist/lib/lanes/config.js +28 -1
- package/dist/lib/lanes/index.d.ts +5 -1
- package/dist/lib/lanes/index.js +3 -1
- package/dist/lib/lanes/monitor.d.ts +48 -0
- package/dist/lib/lanes/monitor.js +409 -0
- package/dist/lib/lanes/spawn.d.ts +10 -1
- package/dist/lib/lanes/spawn.js +60 -5
- package/dist/lib/lanes/types.d.ts +6 -0
- package/dist/lib/lanes/types.js +1 -1
- package/dist/lib/lanes/watch.d.ts +22 -0
- package/dist/lib/lanes/watch.js +122 -0
- package/package.json +1 -1
package/dist/lib/lanes/types.js
CHANGED
|
@@ -203,7 +203,7 @@ export const LANE_FIELD_WRITERS = {
|
|
|
203
203
|
priority: ["command"], worker: ["creator"], worker_path: ["creator"], worker_pack: ["creator"],
|
|
204
204
|
provider: ["creator"], account: ["creator", "spawn", "command"], account_history: ["command"], pending_resume: ["command", "spawn"], host: ["creator"], origin_host: ["creator"], origin_host_source: ["creator"],
|
|
205
205
|
version: ["creator"], created_at: ["creator"], updated_at: ["creator"], attribution_history: ["command"],
|
|
206
|
-
last_envelope: ["watcher", "reconcile"], model: ["creator", "spawn", "command"], effort: ["creator", "spawn"], effort_source: ["creator", "spawn"],
|
|
206
|
+
last_envelope: ["watcher", "reconcile"], model: ["creator", "spawn", "command"], effort: ["creator", "spawn"], effort_source: ["creator", "spawn"], fast_mode: ["spawn"],
|
|
207
207
|
last_message_at: ["command"], last_compaction: ["spawn"], story_complete: ["watcher", "reconcile"], story_complete_reason: ["watcher", "reconcile"], story_status: ["watcher", "reconcile"],
|
|
208
208
|
overdue: ["reconcile"], overdue_at: ["reconcile"], last_host_report_at: ["watcher", "spawn", "reconcile"], stalled: ["watcher"], stalled_at: ["watcher"], stalled_reason: ["watcher"],
|
|
209
209
|
admitted_reason: ["reconcile"], terminal_reason: ["command", "watcher", "spawn", "reconcile"], terminal_details: ["command", "spawn", "watcher", "reconcile"],
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type LaneRecord, type QuestionRecord } from "./types.js";
|
|
2
|
+
export declare const DEFAULT_LANE_WATCH_INTERVAL_MS = 5000;
|
|
3
|
+
export declare const MAX_LANE_WATCH_INTERVAL_MS = 30000;
|
|
4
|
+
export declare const MAX_WATCHED_LANES = 64;
|
|
5
|
+
export declare const MAX_WATCH_OUTPUT_LINES = 512;
|
|
6
|
+
export interface LaneWatchDeps {
|
|
7
|
+
readLane?: (hqRoot: string, laneId: string) => LaneRecord | null;
|
|
8
|
+
listQuestionIds?: (hqRoot: string) => string[];
|
|
9
|
+
readQuestion?: (hqRoot: string, questionId: string) => QuestionRecord | null;
|
|
10
|
+
sleep?: (ms: number) => Promise<void>;
|
|
11
|
+
intervalMs?: number;
|
|
12
|
+
maxOutputLines?: number;
|
|
13
|
+
writeLine?: (line: string) => void;
|
|
14
|
+
writeError?: (line: string) => void;
|
|
15
|
+
}
|
|
16
|
+
export interface LaneWatchResult {
|
|
17
|
+
polls: number;
|
|
18
|
+
lines: number;
|
|
19
|
+
suppressed: boolean;
|
|
20
|
+
}
|
|
21
|
+
export declare function runLaneWatch(hqRoot: string, laneIds: readonly string[], deps?: LaneWatchDeps): Promise<LaneWatchResult>;
|
|
22
|
+
//# sourceMappingURL=watch.d.ts.map
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { listQuestionIds, readLane, readQuestion, } from "./store.js";
|
|
2
|
+
import { LaneNotFoundError, LanesError } from "./errors.js";
|
|
3
|
+
import { isTerminalLaneState } from "./types.js";
|
|
4
|
+
export const DEFAULT_LANE_WATCH_INTERVAL_MS = 5_000;
|
|
5
|
+
export const MAX_LANE_WATCH_INTERVAL_MS = 30_000;
|
|
6
|
+
export const MAX_WATCHED_LANES = 64;
|
|
7
|
+
export const MAX_WATCH_OUTPUT_LINES = 512;
|
|
8
|
+
function defaultSleep(ms) {
|
|
9
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
10
|
+
}
|
|
11
|
+
function readableError(err) {
|
|
12
|
+
return err instanceof Error ? err.message : String(err);
|
|
13
|
+
}
|
|
14
|
+
function boundedInterval(intervalMs) {
|
|
15
|
+
const value = intervalMs ?? DEFAULT_LANE_WATCH_INTERVAL_MS;
|
|
16
|
+
if (!Number.isFinite(value))
|
|
17
|
+
return DEFAULT_LANE_WATCH_INTERVAL_MS;
|
|
18
|
+
return Math.min(MAX_LANE_WATCH_INTERVAL_MS, Math.max(100, Math.floor(value)));
|
|
19
|
+
}
|
|
20
|
+
function validateLaneIds(laneIds) {
|
|
21
|
+
const unique = [...new Set(laneIds.map((laneId) => laneId.trim()))];
|
|
22
|
+
if (unique.some((laneId) => laneId.length === 0)) {
|
|
23
|
+
throw new LanesError("watch_invalid_lane_id", "Lane ids must not be empty");
|
|
24
|
+
}
|
|
25
|
+
if (unique.length === 0) {
|
|
26
|
+
throw new LanesError("watch_no_lanes", "hq lanes watch requires at least one lane id");
|
|
27
|
+
}
|
|
28
|
+
if (unique.length > MAX_WATCHED_LANES) {
|
|
29
|
+
throw new LanesError("watch_too_many_lanes", `hq lanes watch accepts at most ${MAX_WATCHED_LANES} lane ids`, { max: MAX_WATCHED_LANES });
|
|
30
|
+
}
|
|
31
|
+
return unique;
|
|
32
|
+
}
|
|
33
|
+
export async function runLaneWatch(hqRoot, laneIds, deps = {}) {
|
|
34
|
+
const watchedIds = validateLaneIds(laneIds);
|
|
35
|
+
const readLaneRecord = deps.readLane ?? readLane;
|
|
36
|
+
const listQuestions = deps.listQuestionIds ?? listQuestionIds;
|
|
37
|
+
const readQuestionRecord = deps.readQuestion ?? readQuestion;
|
|
38
|
+
const sleep = deps.sleep ?? defaultSleep;
|
|
39
|
+
const writeLine = deps.writeLine ?? ((line) => console.log(line));
|
|
40
|
+
const writeError = deps.writeError ?? ((line) => process.stderr.write(`${line}\n`));
|
|
41
|
+
const maxOutputLines = Math.max(1, Math.floor(deps.maxOutputLines ?? MAX_WATCH_OUTPUT_LINES));
|
|
42
|
+
const intervalMs = boundedInterval(deps.intervalMs);
|
|
43
|
+
const previousStates = new Map();
|
|
44
|
+
const previousQuestionStatuses = new Map();
|
|
45
|
+
let polls = 0;
|
|
46
|
+
let lines = 0;
|
|
47
|
+
let suppressed = false;
|
|
48
|
+
const emit = (line) => {
|
|
49
|
+
if (lines < maxOutputLines - 1) {
|
|
50
|
+
writeLine(line);
|
|
51
|
+
lines += 1;
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (!suppressed) {
|
|
55
|
+
writeLine("lane watch: output limit reached; further events suppressed");
|
|
56
|
+
lines = maxOutputLines;
|
|
57
|
+
suppressed = true;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
const unreadable = (kind, id, err) => {
|
|
61
|
+
const message = `${kind} ${id}: unreadable record (${readableError(err)})`;
|
|
62
|
+
writeError(`lane watch: ${message}`);
|
|
63
|
+
throw new LanesError("watch_record_unreadable", message, {
|
|
64
|
+
kind,
|
|
65
|
+
id,
|
|
66
|
+
cause: readableError(err),
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
while (true) {
|
|
70
|
+
polls += 1;
|
|
71
|
+
const records = [];
|
|
72
|
+
for (const laneId of watchedIds) {
|
|
73
|
+
let record = null;
|
|
74
|
+
try {
|
|
75
|
+
record = readLaneRecord(hqRoot, laneId);
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
unreadable("lane", laneId, err);
|
|
79
|
+
}
|
|
80
|
+
if (!record) {
|
|
81
|
+
unreadable("lane", laneId, new LaneNotFoundError("lane", laneId));
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
records.push(record);
|
|
85
|
+
if (previousStates.get(laneId) !== record.state) {
|
|
86
|
+
emit(`lane ${laneId}: state=${record.state}${isTerminalLaneState(record.state) ? " (terminal)" : ""}`);
|
|
87
|
+
previousStates.set(laneId, record.state);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
let questionIds = [];
|
|
91
|
+
try {
|
|
92
|
+
questionIds = listQuestions(hqRoot);
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
unreadable("question", "<index>", err);
|
|
96
|
+
}
|
|
97
|
+
const watchedSet = new Set(watchedIds);
|
|
98
|
+
for (const questionId of questionIds) {
|
|
99
|
+
let record = null;
|
|
100
|
+
try {
|
|
101
|
+
record = readQuestionRecord(hqRoot, questionId);
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
unreadable("question", questionId, err);
|
|
105
|
+
}
|
|
106
|
+
if (!record) {
|
|
107
|
+
unreadable("question", questionId, new LaneNotFoundError("question", questionId));
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (watchedSet.has(record.lane_id) &&
|
|
111
|
+
previousQuestionStatuses.get(questionId) !== record.status) {
|
|
112
|
+
emit(`lane ${record.lane_id}: question=${record.question_id} status=${record.status}`);
|
|
113
|
+
}
|
|
114
|
+
previousQuestionStatuses.set(questionId, record.status);
|
|
115
|
+
}
|
|
116
|
+
if (records.every((record) => isTerminalLaneState(record.state))) {
|
|
117
|
+
return { polls, lines, suppressed };
|
|
118
|
+
}
|
|
119
|
+
await sleep(intervalMs);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=watch.js.map
|