@cairn-tool/cairn 3.3.1 → 3.5.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/README.md +6 -4
- package/dist/agent/manifest.d.ts +10 -0
- package/dist/agent/manifest.js +23 -0
- package/dist/agent/manifest.js.map +1 -1
- package/dist/agent/parser.js +1 -15
- package/dist/agent/parser.js.map +1 -1
- package/dist/agent/render.js +47 -3
- package/dist/agent/render.js.map +1 -1
- package/dist/cli.js +48 -3
- package/dist/cli.js.map +1 -1
- package/dist/commands/qa-list.d.ts +7 -0
- package/dist/commands/qa-list.js +69 -0
- package/dist/commands/qa-list.js.map +1 -0
- package/dist/commands/qa-run.d.ts +3 -0
- package/dist/commands/qa-run.js +106 -0
- package/dist/commands/qa-run.js.map +1 -0
- package/dist/commands/qa-summary.d.ts +6 -0
- package/dist/commands/qa-summary.js +38 -0
- package/dist/commands/qa-summary.js.map +1 -0
- package/dist/commands/scripts.d.ts +16 -1
- package/dist/commands/scripts.js +27 -4
- package/dist/commands/scripts.js.map +1 -1
- package/dist/config-schema.d.ts +2 -0
- package/dist/config-schema.js +4 -0
- package/dist/config-schema.js.map +1 -1
- package/dist/config.d.ts +12 -2
- package/dist/config.js +36 -7
- package/dist/config.js.map +1 -1
- package/dist/contract/registry.js +46 -3
- package/dist/contract/registry.js.map +1 -1
- package/dist/contract/schemas/index.js +2 -0
- package/dist/contract/schemas/index.js.map +1 -1
- package/dist/contract/schemas/qa.d.ts +2 -0
- package/dist/contract/schemas/qa.js +111 -0
- package/dist/contract/schemas/qa.js.map +1 -0
- package/dist/qa/agent.d.ts +25 -0
- package/dist/qa/agent.js +142 -0
- package/dist/qa/agent.js.map +1 -0
- package/dist/qa/agents/claude-code.d.ts +2 -0
- package/dist/qa/agents/claude-code.js +121 -0
- package/dist/qa/agents/claude-code.js.map +1 -0
- package/dist/qa/agents/cursor.d.ts +5 -0
- package/dist/qa/agents/cursor.js +143 -0
- package/dist/qa/agents/cursor.js.map +1 -0
- package/dist/qa/agents/index.d.ts +32 -0
- package/dist/qa/agents/index.js +83 -0
- package/dist/qa/agents/index.js.map +1 -0
- package/dist/qa/agents/types.d.ts +61 -0
- package/dist/qa/agents/types.js +18 -0
- package/dist/qa/agents/types.js.map +1 -0
- package/dist/qa/async.d.ts +27 -0
- package/dist/qa/async.js +91 -0
- package/dist/qa/async.js.map +1 -0
- package/dist/qa/casefile.d.ts +35 -0
- package/dist/qa/casefile.js +181 -0
- package/dist/qa/casefile.js.map +1 -0
- package/dist/qa/cases.d.ts +51 -0
- package/dist/qa/cases.js +144 -0
- package/dist/qa/cases.js.map +1 -0
- package/dist/qa/config.d.ts +50 -0
- package/dist/qa/config.js +105 -0
- package/dist/qa/config.js.map +1 -0
- package/dist/qa/demo.d.ts +10 -0
- package/dist/qa/demo.js +194 -0
- package/dist/qa/demo.js.map +1 -0
- package/dist/qa/format.d.ts +38 -0
- package/dist/qa/format.js +208 -0
- package/dist/qa/format.js.map +1 -0
- package/dist/qa/harness.d.ts +68 -0
- package/dist/qa/harness.js +528 -0
- package/dist/qa/harness.js.map +1 -0
- package/dist/qa/keys.d.ts +12 -0
- package/dist/qa/keys.js +96 -0
- package/dist/qa/keys.js.map +1 -0
- package/dist/qa/layout.d.ts +102 -0
- package/dist/qa/layout.js +278 -0
- package/dist/qa/layout.js.map +1 -0
- package/dist/qa/lock.d.ts +5 -0
- package/dist/qa/lock.js +114 -0
- package/dist/qa/lock.js.map +1 -0
- package/dist/qa/options.d.ts +38 -0
- package/dist/qa/options.js +165 -0
- package/dist/qa/options.js.map +1 -0
- package/dist/qa/outcome.d.ts +30 -0
- package/dist/qa/outcome.js +162 -0
- package/dist/qa/outcome.js.map +1 -0
- package/dist/qa/reports.d.ts +51 -0
- package/dist/qa/reports.js +121 -0
- package/dist/qa/reports.js.map +1 -0
- package/dist/qa/schedule.d.ts +55 -0
- package/dist/qa/schedule.js +145 -0
- package/dist/qa/schedule.js.map +1 -0
- package/dist/qa/screen.d.ts +31 -0
- package/dist/qa/screen.js +146 -0
- package/dist/qa/screen.js.map +1 -0
- package/dist/qa/slot.d.ts +28 -0
- package/dist/qa/slot.js +126 -0
- package/dist/qa/slot.js.map +1 -0
- package/dist/qa/tracker.d.ts +36 -0
- package/dist/qa/tracker.js +357 -0
- package/dist/qa/tracker.js.map +1 -0
- package/dist/query/execute.js +0 -0
- package/dist/query/execute.js.map +1 -1
- package/dist/scripts/resolve.js +1 -4
- package/dist/scripts/resolve.js.map +1 -1
- package/dist/usage/providers/cursor.js +1 -1
- package/dist/usage/providers/cursor.js.map +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keys.js","sourceRoot":"","sources":["../../src/qa/keys.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type { Usage } from "./outcome.js";
|
|
2
|
+
import type { SlotStatus } from "./slot.js";
|
|
3
|
+
/** Everything the pane renderer needs from a Slot — plain data, no processes or filesystem. */
|
|
4
|
+
export interface PaneView {
|
|
5
|
+
/** The stable slot id. Zoom pins to this, not to a position in a list that comes and goes. */
|
|
6
|
+
slot: number;
|
|
7
|
+
name: string | null;
|
|
8
|
+
/** The model this case is running under — panes no longer share one. */
|
|
9
|
+
model: string;
|
|
10
|
+
status: SlotStatus;
|
|
11
|
+
elapsedSeconds: number;
|
|
12
|
+
tools: number;
|
|
13
|
+
streamedBytes: number;
|
|
14
|
+
usage: Usage | null;
|
|
15
|
+
note: string;
|
|
16
|
+
transcript: readonly string[];
|
|
17
|
+
thinking: string;
|
|
18
|
+
}
|
|
19
|
+
export interface TableRow {
|
|
20
|
+
name: string;
|
|
21
|
+
status: string;
|
|
22
|
+
outcome: string;
|
|
23
|
+
duration: string;
|
|
24
|
+
tools: string;
|
|
25
|
+
inn: string;
|
|
26
|
+
out: string;
|
|
27
|
+
cache_r: string;
|
|
28
|
+
cache_w: string;
|
|
29
|
+
}
|
|
30
|
+
export interface ViewModel {
|
|
31
|
+
mode: "panes" | "table";
|
|
32
|
+
/** The slot id being zoomed, or null. */
|
|
33
|
+
zoom: number | null;
|
|
34
|
+
scroll: number;
|
|
35
|
+
running: number;
|
|
36
|
+
parallel: number;
|
|
37
|
+
queued: number;
|
|
38
|
+
paused: boolean;
|
|
39
|
+
draining: boolean;
|
|
40
|
+
/** Only the slots worth showing — normally the running ones. */
|
|
41
|
+
panes: PaneView[];
|
|
42
|
+
/** Shown in place of the panes when `panes` is empty. */
|
|
43
|
+
panesNote: string;
|
|
44
|
+
table: TableRow[];
|
|
45
|
+
totals: TableRow;
|
|
46
|
+
statusLeft: string;
|
|
47
|
+
}
|
|
48
|
+
/** Python: field(text, width, color) */
|
|
49
|
+
export declare function padField(text: string, width: number, color?: string): string;
|
|
50
|
+
/** Python: field_right(text, width, color) */
|
|
51
|
+
export declare function padFieldRight(text: string, width: number, color?: string): string;
|
|
52
|
+
/**
|
|
53
|
+
* `ordinal` is the pane's 1-based position on screen, which is also its zoom key — the slot id
|
|
54
|
+
* would drift from the digit you press as panes come and go.
|
|
55
|
+
*/
|
|
56
|
+
export declare function headerFor(pane: PaneView, ordinal?: number, width?: number): string;
|
|
57
|
+
export declare function composeStatus(left: string, right: string, width: number): string;
|
|
58
|
+
/** `panes` is how many are on screen right now — the zoom keys are 1..panes. */
|
|
59
|
+
export declare function shortcutsFor(mode: "panes" | "table", zoom: number | null, panes?: number): string;
|
|
60
|
+
export declare function paneBody(pane: PaneView, bodyRows: number, width: number): string[];
|
|
61
|
+
export interface PanesOptions {
|
|
62
|
+
running: number;
|
|
63
|
+
parallel: number;
|
|
64
|
+
queued: number;
|
|
65
|
+
paused: boolean;
|
|
66
|
+
draining: boolean;
|
|
67
|
+
note: string;
|
|
68
|
+
zoom: number | null;
|
|
69
|
+
}
|
|
70
|
+
export declare function layoutPanes(panes: readonly PaneView[], opts: PanesOptions, rows: number, cols: number): string[];
|
|
71
|
+
export declare const COL_TC = 7;
|
|
72
|
+
export declare const COL_STATUS = 7;
|
|
73
|
+
export declare const COL_OUTCOME = 10;
|
|
74
|
+
export declare const COL_DURATION = 8;
|
|
75
|
+
export declare const COL_TOOLS = 5;
|
|
76
|
+
export declare const COL_IO = 6;
|
|
77
|
+
export declare const COL_CACHE = 7;
|
|
78
|
+
export declare const TABLE_HEAD: TableRow;
|
|
79
|
+
export declare const BLANK_METRICS: {
|
|
80
|
+
readonly duration: "—";
|
|
81
|
+
readonly tools: "—";
|
|
82
|
+
readonly inn: "—";
|
|
83
|
+
readonly out: "—";
|
|
84
|
+
readonly cache_r: "—";
|
|
85
|
+
readonly cache_w: "—";
|
|
86
|
+
};
|
|
87
|
+
export declare function composeTableLine(row: TableRow, color?: boolean): string;
|
|
88
|
+
export declare function tableHeader(cols: number): string;
|
|
89
|
+
export declare function formatTableRow(row: TableRow, cols: number): string;
|
|
90
|
+
export interface TableLayout {
|
|
91
|
+
lines: string[];
|
|
92
|
+
scroll: number;
|
|
93
|
+
page: number;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Header frozen on row 0; the Total row sits immediately below the last case row rather than
|
|
97
|
+
* being pushed to the bottom of the window. Once the table is long enough to fill the view the
|
|
98
|
+
* last case row already reaches the bottom, so the Total row stays pinned there as before.
|
|
99
|
+
*/
|
|
100
|
+
export declare function layoutTable(rowsData: readonly TableRow[], totals: TableRow, scroll: number, rows: number, cols: number): TableLayout;
|
|
101
|
+
/** The whole screen: body rows plus the reverse-video status bar on the last line. */
|
|
102
|
+
export declare function frame(vm: ViewModel, rows: number, cols: number): TableLayout;
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import { BOLD, DIM, GREEN, RED, RESET, YELLOW, clipAnsi, displayWidth, flatten, fmtBytes, fmtDuration, invertBar, padClip, pyStrip, wrapAnsi, } from "./format.js";
|
|
2
|
+
import { DONE_FAIL, DONE_OK, MAX_ZOOM_KEYS } from "./config.js";
|
|
3
|
+
import { OUTCOME_COLOR, STATUS_COLOR, usageFragment } from "./outcome.js";
|
|
4
|
+
/** Python: field(text, width, color) */
|
|
5
|
+
export function padField(text, width, color = "") {
|
|
6
|
+
let body;
|
|
7
|
+
if (displayWidth(text) > width) {
|
|
8
|
+
const clipped = clipAnsi(text, width);
|
|
9
|
+
body = clipped + " ".repeat(Math.max(0, width - displayWidth(clipped)));
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
body = text + " ".repeat(width - displayWidth(text));
|
|
13
|
+
}
|
|
14
|
+
return color ? `${color}${body}${RESET}` : body;
|
|
15
|
+
}
|
|
16
|
+
/** Python: field_right(text, width, color) */
|
|
17
|
+
export function padFieldRight(text, width, color = "") {
|
|
18
|
+
let body;
|
|
19
|
+
if (displayWidth(text) > width) {
|
|
20
|
+
const clipped = clipAnsi(text, width);
|
|
21
|
+
body = " ".repeat(Math.max(0, width - displayWidth(clipped))) + clipped;
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
body = " ".repeat(width - displayWidth(text)) + text;
|
|
25
|
+
}
|
|
26
|
+
return color ? `${color}${body}${RESET}` : body;
|
|
27
|
+
}
|
|
28
|
+
const padLeftNum = (n, width) => String(n).padStart(width, " ");
|
|
29
|
+
const padRightStr = (s, width) => s.padEnd(width, " ");
|
|
30
|
+
/** Minimum run-state text kept before the model is dropped, and the gap that separates them. */
|
|
31
|
+
const HEADER_MIN_LEFT = 16;
|
|
32
|
+
const HEADER_GAP = 2;
|
|
33
|
+
/**
|
|
34
|
+
* Right-align `model` on the same line, clipping the left text if the two would collide.
|
|
35
|
+
* On a narrow terminal the model is dropped entirely rather than crowding out the run state
|
|
36
|
+
* or butting up against it with no separator.
|
|
37
|
+
*/
|
|
38
|
+
function withModel(left, model, width) {
|
|
39
|
+
if (!model || width <= 0)
|
|
40
|
+
return left;
|
|
41
|
+
const right = `${model} `;
|
|
42
|
+
const rightW = displayWidth(right);
|
|
43
|
+
const budget = width - rightW - HEADER_GAP;
|
|
44
|
+
if (budget < HEADER_MIN_LEFT)
|
|
45
|
+
return left;
|
|
46
|
+
const leftC = clipAnsi(left, budget);
|
|
47
|
+
const gap = Math.max(HEADER_GAP, width - displayWidth(leftC) - rightW);
|
|
48
|
+
return `${leftC}${" ".repeat(gap)}${right}`;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* `ordinal` is the pane's 1-based position on screen, which is also its zoom key — the slot id
|
|
52
|
+
* would drift from the digit you press as panes come and go.
|
|
53
|
+
*/
|
|
54
|
+
export function headerFor(pane, ordinal = 1, width = 0) {
|
|
55
|
+
const model = pane.model;
|
|
56
|
+
const n = padLeftNum(ordinal, 2);
|
|
57
|
+
// An idle pane is not running anything, so there is no model to name.
|
|
58
|
+
if (pane.name === null)
|
|
59
|
+
return ` ${n} — idle`;
|
|
60
|
+
const name = padRightStr(pane.name, 7);
|
|
61
|
+
const elapsed = fmtDuration(pane.elapsedSeconds);
|
|
62
|
+
const status = pane.status;
|
|
63
|
+
let left;
|
|
64
|
+
if (status === "run") {
|
|
65
|
+
left =
|
|
66
|
+
` ${n} ${name} ${YELLOW}* run${RESET} ${elapsed} ` +
|
|
67
|
+
`${pane.tools} tools ${fmtBytes(pane.streamedBytes)}`;
|
|
68
|
+
}
|
|
69
|
+
else if (status === DONE_OK) {
|
|
70
|
+
const usage = pane.usage ? usageFragment(pane.usage) : "tokens n/a";
|
|
71
|
+
left = ` ${n} ${name} ${GREEN}ok ${RESET} ${elapsed} ${usage}`;
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
const color = DONE_FAIL.has(status) ? RED : DIM;
|
|
75
|
+
const extra = pane.usage ? usageFragment(pane.usage) : pane.note || "";
|
|
76
|
+
left = ` ${n} ${name} ${color}${padRightStr(status, 7)}${RESET} ${elapsed} ${extra}`;
|
|
77
|
+
}
|
|
78
|
+
return withModel(left, model, width);
|
|
79
|
+
}
|
|
80
|
+
export function composeStatus(left, right, width) {
|
|
81
|
+
const rightW = displayWidth(right);
|
|
82
|
+
if (rightW >= width)
|
|
83
|
+
return invertBar(right, width);
|
|
84
|
+
const leftC = clipAnsi(left, width - rightW);
|
|
85
|
+
const padding = Math.max(0, width - displayWidth(leftC) - rightW);
|
|
86
|
+
return invertBar(leftC + " ".repeat(padding) + right, width);
|
|
87
|
+
}
|
|
88
|
+
/** `panes` is how many are on screen right now — the zoom keys are 1..panes. */
|
|
89
|
+
export function shortcutsFor(mode, zoom, panes = 0) {
|
|
90
|
+
if (mode === "table")
|
|
91
|
+
return "q quit d drain p pause r panes ↑↓ PgUp/PgDn Home/End";
|
|
92
|
+
if (zoom !== null)
|
|
93
|
+
return "q quit d drain p pause r table 0/esc unzoom";
|
|
94
|
+
const keys = Math.min(panes, MAX_ZOOM_KEYS);
|
|
95
|
+
if (keys < 2)
|
|
96
|
+
return "q quit d drain p pause r table";
|
|
97
|
+
return `q quit d drain p pause r table 1-${keys} zoom`;
|
|
98
|
+
}
|
|
99
|
+
export function paneBody(pane, bodyRows, width) {
|
|
100
|
+
if (bodyRows <= 0)
|
|
101
|
+
return [];
|
|
102
|
+
const indent = " ";
|
|
103
|
+
const inner = Math.max(8, width - displayWidth(indent));
|
|
104
|
+
const items = [...pane.transcript];
|
|
105
|
+
if (pyStrip(pane.thinking) !== "") {
|
|
106
|
+
items.push(`${DIM}> ${RESET}${flatten(pane.thinking)} …`);
|
|
107
|
+
}
|
|
108
|
+
let wrapped = [];
|
|
109
|
+
for (const raw of items) {
|
|
110
|
+
const chunks = wrapAnsi(raw, inner);
|
|
111
|
+
if (chunks.length === 0)
|
|
112
|
+
continue;
|
|
113
|
+
for (const chunk of chunks)
|
|
114
|
+
wrapped.push(indent + chunk);
|
|
115
|
+
}
|
|
116
|
+
if (wrapped.length > bodyRows)
|
|
117
|
+
wrapped = wrapped.slice(-bodyRows);
|
|
118
|
+
const out = wrapped.map((line) => padClip(line, width));
|
|
119
|
+
while (out.length < bodyRows)
|
|
120
|
+
out.push(" ".repeat(width));
|
|
121
|
+
return out;
|
|
122
|
+
}
|
|
123
|
+
export function layoutPanes(panes, opts, rows, cols) {
|
|
124
|
+
const width = Math.max(1, cols);
|
|
125
|
+
const flags = [];
|
|
126
|
+
if (opts.paused)
|
|
127
|
+
flags.push(`${YELLOW}PAUSED${RESET}`);
|
|
128
|
+
if (opts.draining)
|
|
129
|
+
flags.push(`${YELLOW}DRAINING${RESET}`);
|
|
130
|
+
const suffix = flags.length > 0 ? ` ${flags.join(" ")}` : "";
|
|
131
|
+
// No single model to name any more — each pane header carries its own, right-aligned.
|
|
132
|
+
const counts = `${BOLD}${opts.running}/${opts.parallel}${RESET} agents ${opts.queued} queued`;
|
|
133
|
+
const title = invertBar(` cairn qa ${counts} Run Everything${suffix}`, width);
|
|
134
|
+
const remaining = Math.max(0, rows - 1);
|
|
135
|
+
// Zoom names a slot, not a position: the list is filtered and reorders as cases finish.
|
|
136
|
+
const zoomed = opts.zoom === null ? undefined : panes.find((p) => p.slot === opts.zoom);
|
|
137
|
+
const chosen = zoomed !== undefined ? [zoomed] : [...panes];
|
|
138
|
+
const lines = [title];
|
|
139
|
+
if (chosen.length === 0) {
|
|
140
|
+
lines.push(padClip(`${DIM} ${opts.note}${RESET}`, width));
|
|
141
|
+
while (lines.length < rows)
|
|
142
|
+
lines.push(" ".repeat(width));
|
|
143
|
+
return lines.slice(0, rows);
|
|
144
|
+
}
|
|
145
|
+
const n = chosen.length;
|
|
146
|
+
const compact = remaining < n * 3;
|
|
147
|
+
if (compact) {
|
|
148
|
+
lines.push(padClip(`${DIM}grow the window for live transcripts (need ~${1 + n * 3} rows)${RESET}`, width));
|
|
149
|
+
for (const [i, pane] of chosen.entries()) {
|
|
150
|
+
lines.push(invertBar(headerFor(pane, i + 1, width), width));
|
|
151
|
+
}
|
|
152
|
+
while (lines.length < rows)
|
|
153
|
+
lines.push(" ".repeat(width));
|
|
154
|
+
return lines.slice(0, rows);
|
|
155
|
+
}
|
|
156
|
+
const paneH = Math.floor(remaining / n);
|
|
157
|
+
const leftover = remaining - paneH * n;
|
|
158
|
+
for (const [i, pane] of chosen.entries()) {
|
|
159
|
+
const h = paneH + (i < leftover ? 1 : 0);
|
|
160
|
+
lines.push(invertBar(headerFor(pane, i + 1, width), width));
|
|
161
|
+
lines.push(...paneBody(pane, Math.max(0, h - 1), width));
|
|
162
|
+
}
|
|
163
|
+
while (lines.length < rows)
|
|
164
|
+
lines.push(" ".repeat(width));
|
|
165
|
+
return lines.slice(0, rows);
|
|
166
|
+
}
|
|
167
|
+
export const COL_TC = 7;
|
|
168
|
+
export const COL_STATUS = 7;
|
|
169
|
+
export const COL_OUTCOME = 10;
|
|
170
|
+
export const COL_DURATION = 8;
|
|
171
|
+
export const COL_TOOLS = 5;
|
|
172
|
+
export const COL_IO = 6;
|
|
173
|
+
export const COL_CACHE = 7;
|
|
174
|
+
export const TABLE_HEAD = {
|
|
175
|
+
name: "TC",
|
|
176
|
+
status: "Status",
|
|
177
|
+
outcome: "Outcome",
|
|
178
|
+
duration: "Duration",
|
|
179
|
+
tools: "Tools",
|
|
180
|
+
inn: "In",
|
|
181
|
+
out: "Out",
|
|
182
|
+
cache_r: "Cache-r",
|
|
183
|
+
cache_w: "Cache-w",
|
|
184
|
+
};
|
|
185
|
+
export const BLANK_METRICS = {
|
|
186
|
+
duration: "—",
|
|
187
|
+
tools: "—",
|
|
188
|
+
inn: "—",
|
|
189
|
+
out: "—",
|
|
190
|
+
cache_r: "—",
|
|
191
|
+
cache_w: "—",
|
|
192
|
+
};
|
|
193
|
+
export function composeTableLine(row, color = true) {
|
|
194
|
+
const st = row.status ?? "";
|
|
195
|
+
const oc = row.outcome ?? "";
|
|
196
|
+
return (" " +
|
|
197
|
+
padField(row.name ?? "", COL_TC) +
|
|
198
|
+
" " +
|
|
199
|
+
padField(st, COL_STATUS, color ? (STATUS_COLOR[st] ?? "") : "") +
|
|
200
|
+
" " +
|
|
201
|
+
padField(oc, COL_OUTCOME, color ? (OUTCOME_COLOR[oc] ?? "") : "") +
|
|
202
|
+
" " +
|
|
203
|
+
padFieldRight(row.duration ?? "", COL_DURATION) +
|
|
204
|
+
" " +
|
|
205
|
+
padFieldRight(row.tools ?? "", COL_TOOLS) +
|
|
206
|
+
" " +
|
|
207
|
+
padFieldRight(row.inn ?? "", COL_IO) +
|
|
208
|
+
" " +
|
|
209
|
+
padFieldRight(row.out ?? "", COL_IO) +
|
|
210
|
+
" " +
|
|
211
|
+
padFieldRight(row.cache_r ?? "", COL_CACHE) +
|
|
212
|
+
" " +
|
|
213
|
+
padFieldRight(row.cache_w ?? "", COL_CACHE));
|
|
214
|
+
}
|
|
215
|
+
export function tableHeader(cols) {
|
|
216
|
+
return invertBar(composeTableLine(TABLE_HEAD, false), cols);
|
|
217
|
+
}
|
|
218
|
+
export function formatTableRow(row, cols) {
|
|
219
|
+
return padClip(composeTableLine(row, true), cols);
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Header frozen on row 0; the Total row sits immediately below the last case row rather than
|
|
223
|
+
* being pushed to the bottom of the window. Once the table is long enough to fill the view the
|
|
224
|
+
* last case row already reaches the bottom, so the Total row stays pinned there as before.
|
|
225
|
+
*/
|
|
226
|
+
export function layoutTable(rowsData, totals, scroll, rows, cols) {
|
|
227
|
+
const width = Math.max(1, cols);
|
|
228
|
+
const header = tableHeader(width);
|
|
229
|
+
const footer = invertBar(composeTableLine(totals, false), width);
|
|
230
|
+
if (rows <= 0)
|
|
231
|
+
return { lines: [], scroll: 0, page: 1 };
|
|
232
|
+
if (rows === 1)
|
|
233
|
+
return { lines: [header], scroll: 0, page: 1 };
|
|
234
|
+
if (rows === 2)
|
|
235
|
+
return { lines: [header, footer], scroll: 0, page: 1 };
|
|
236
|
+
const view = rows - 2;
|
|
237
|
+
const maxScroll = Math.max(0, rowsData.length - view);
|
|
238
|
+
const clamped = Math.max(0, Math.min(scroll, maxScroll));
|
|
239
|
+
const window = rowsData.slice(clamped, clamped + view);
|
|
240
|
+
const lines = [header];
|
|
241
|
+
for (const row of window)
|
|
242
|
+
lines.push(formatTableRow(row, width));
|
|
243
|
+
lines.push(footer);
|
|
244
|
+
while (lines.length < rows)
|
|
245
|
+
lines.push(" ".repeat(width));
|
|
246
|
+
return { lines: lines.slice(0, rows), scroll: clamped, page: view };
|
|
247
|
+
}
|
|
248
|
+
/** The whole screen: body rows plus the reverse-video status bar on the last line. */
|
|
249
|
+
export function frame(vm, rows, cols) {
|
|
250
|
+
const width = Math.max(1, cols);
|
|
251
|
+
const bodyRows = Math.max(0, rows - 1);
|
|
252
|
+
let lines;
|
|
253
|
+
let scroll = vm.scroll;
|
|
254
|
+
let page = 10;
|
|
255
|
+
if (vm.mode === "table") {
|
|
256
|
+
const laid = layoutTable(vm.table, vm.totals, vm.scroll, bodyRows, width);
|
|
257
|
+
lines = laid.lines;
|
|
258
|
+
scroll = laid.scroll;
|
|
259
|
+
page = laid.page;
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
lines = layoutPanes(vm.panes, {
|
|
263
|
+
running: vm.running,
|
|
264
|
+
parallel: vm.parallel,
|
|
265
|
+
queued: vm.queued,
|
|
266
|
+
paused: vm.paused,
|
|
267
|
+
draining: vm.draining,
|
|
268
|
+
note: vm.panesNote,
|
|
269
|
+
zoom: vm.zoom,
|
|
270
|
+
}, bodyRows, width);
|
|
271
|
+
}
|
|
272
|
+
const out = lines.slice(0, bodyRows);
|
|
273
|
+
while (out.length < bodyRows)
|
|
274
|
+
out.push(" ".repeat(width));
|
|
275
|
+
out.push(composeStatus(vm.statusLeft, shortcutsFor(vm.mode, vm.zoom, vm.panes.length), width));
|
|
276
|
+
return { lines: out, scroll, page };
|
|
277
|
+
}
|
|
278
|
+
//# sourceMappingURL=layout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layout.js","sourceRoot":"","sources":["../../src/qa/layout.ts"],"names":[],"mappings":"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"}
|
package/dist/qa/lock.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { closeSync, constants, ftruncateSync, mkdirSync, openSync, readFileSync, unlinkSync, writeSync, } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { UserError } from "./config.js";
|
|
4
|
+
/**
|
|
5
|
+
* darwin/BSD O_EXLOCK. Node does not export it, but passes numeric flags straight to open(2),
|
|
6
|
+
* so this gives a true kernel flock(LOCK_EX|LOCK_NB) — auto-released on death, exactly like the
|
|
7
|
+
* Python's fcntl.flock. On Linux 0x20 is an undefined bit that open(2) SILENTLY IGNORES, which
|
|
8
|
+
* would produce a lock that does nothing, so the platform gate is load-bearing.
|
|
9
|
+
*/
|
|
10
|
+
const O_EXLOCK = 0x20;
|
|
11
|
+
const KERNEL_LOCK = process.platform === "darwin" || process.platform.endsWith("bsd");
|
|
12
|
+
function readPid(path) {
|
|
13
|
+
try {
|
|
14
|
+
const n = Number(readFileSync(path, "utf8").trim());
|
|
15
|
+
// Reject 0 and negatives: process.kill(0, …) and kill(-n, …) hit our own process group.
|
|
16
|
+
return Number.isInteger(n) && n > 1 ? n : null;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function isAlive(pid) {
|
|
23
|
+
try {
|
|
24
|
+
process.kill(pid, 0);
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
catch (err) {
|
|
28
|
+
return err.code === "EPERM";
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function held(path) {
|
|
32
|
+
const pid = readPid(path);
|
|
33
|
+
throw new UserError(`another harness already holds ${path} (pid ${pid ?? "?"})`);
|
|
34
|
+
}
|
|
35
|
+
export function acquireLock(runsDir) {
|
|
36
|
+
const path = join(runsDir, ".harness.lock");
|
|
37
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
38
|
+
if (KERNEL_LOCK) {
|
|
39
|
+
let fd;
|
|
40
|
+
try {
|
|
41
|
+
fd = openSync(path, constants.O_RDWR | constants.O_CREAT | constants.O_NONBLOCK | O_EXLOCK, 0o644);
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
const code = err.code;
|
|
45
|
+
if (code !== "EAGAIN" && code !== "EWOULDBLOCK")
|
|
46
|
+
throw err;
|
|
47
|
+
held(path);
|
|
48
|
+
}
|
|
49
|
+
ftruncateSync(fd, 0);
|
|
50
|
+
writeSync(fd, String(process.pid), 0);
|
|
51
|
+
let released = false;
|
|
52
|
+
return {
|
|
53
|
+
path,
|
|
54
|
+
release() {
|
|
55
|
+
if (released)
|
|
56
|
+
return;
|
|
57
|
+
released = true;
|
|
58
|
+
// The kernel drops the lock on close; leave the file, as flock does.
|
|
59
|
+
try {
|
|
60
|
+
closeSync(fd);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
/* already closed */
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
// Portable fallback: O_EXCL plus a liveness probe standing in for the kernel's auto-release.
|
|
69
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
70
|
+
let fd;
|
|
71
|
+
try {
|
|
72
|
+
fd = openSync(path, "wx", 0o644);
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
if (err.code !== "EEXIST")
|
|
76
|
+
throw err;
|
|
77
|
+
const pid = readPid(path);
|
|
78
|
+
if (pid !== null && pid !== process.pid && isAlive(pid))
|
|
79
|
+
held(path);
|
|
80
|
+
try {
|
|
81
|
+
unlinkSync(path);
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
/* another starter already reclaimed it */
|
|
85
|
+
}
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
writeSync(fd, String(process.pid));
|
|
89
|
+
let released = false;
|
|
90
|
+
return {
|
|
91
|
+
path,
|
|
92
|
+
release() {
|
|
93
|
+
if (released)
|
|
94
|
+
return;
|
|
95
|
+
released = true;
|
|
96
|
+
try {
|
|
97
|
+
closeSync(fd);
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
/* already closed */
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
if (readPid(path) === process.pid)
|
|
104
|
+
unlinkSync(path);
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
/* gone, or stolen after a stale-detection window */
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
held(path);
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=lock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lock.js","sourceRoot":"","sources":["../../src/qa/lock.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { type Case } from "./cases.js";
|
|
2
|
+
import { type Options } from "./config.js";
|
|
3
|
+
export declare const NO_CASES = "no eligible cases (every _plans/tc-N.yaml already has a tc-N/ folder)";
|
|
4
|
+
export interface QaCliOptions {
|
|
5
|
+
format?: string;
|
|
6
|
+
envelope?: boolean;
|
|
7
|
+
parallel?: string;
|
|
8
|
+
model?: string | string[];
|
|
9
|
+
repo?: string;
|
|
10
|
+
runsDir?: string;
|
|
11
|
+
only?: string;
|
|
12
|
+
limit?: string;
|
|
13
|
+
dryRun?: boolean;
|
|
14
|
+
showPrompts?: boolean;
|
|
15
|
+
noTui?: boolean;
|
|
16
|
+
timeoutMin?: string;
|
|
17
|
+
agent?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface PreparedRun {
|
|
20
|
+
options: Options;
|
|
21
|
+
cases: Case[];
|
|
22
|
+
allCases: Case[];
|
|
23
|
+
}
|
|
24
|
+
export declare function refuseWindows(): void;
|
|
25
|
+
export declare function parseModelFlags(values: string[] | undefined, defaults: Record<string, string>): {
|
|
26
|
+
label: string;
|
|
27
|
+
models: Record<string, string>;
|
|
28
|
+
};
|
|
29
|
+
export declare function buildRunOptions(raw: QaCliOptions, format: string): PreparedRun;
|
|
30
|
+
/**
|
|
31
|
+
* A relative --runs-dir resolves against --repo when one is given, and against the cwd
|
|
32
|
+
* otherwise — the same rule in every subcommand. `qa run` resolving against --repo while
|
|
33
|
+
* `qa list` resolved against the cwd meant identical flags named different directories
|
|
34
|
+
* depending on where you stood.
|
|
35
|
+
*/
|
|
36
|
+
export declare function resolveRunsDir(raw: string | undefined, flag?: string, repo?: string): string;
|
|
37
|
+
export declare function printDryRun(prepared: PreparedRun): string;
|
|
38
|
+
export declare function caseStatus(kase: Case): "pending" | "done";
|