@addai/node 0.4.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.
Files changed (133) hide show
  1. package/README.md +96 -0
  2. package/dist/agent-auth.d.ts +9 -0
  3. package/dist/agent-auth.js +96 -0
  4. package/dist/attachments.d.ts +20 -0
  5. package/dist/attachments.js +164 -0
  6. package/dist/capabilities.d.ts +64 -0
  7. package/dist/capabilities.js +406 -0
  8. package/dist/claude-binary.d.ts +16 -0
  9. package/dist/claude-binary.js +145 -0
  10. package/dist/claude-config.d.ts +11 -0
  11. package/dist/claude-config.js +144 -0
  12. package/dist/claude-print.d.ts +35 -0
  13. package/dist/claude-print.js +312 -0
  14. package/dist/claude-spawn.d.ts +40 -0
  15. package/dist/claude-spawn.js +220 -0
  16. package/dist/cli.d.ts +2 -0
  17. package/dist/cli.js +137 -0
  18. package/dist/codex-binary.d.ts +4 -0
  19. package/dist/codex-binary.js +70 -0
  20. package/dist/codex-spawn.d.ts +47 -0
  21. package/dist/codex-spawn.js +320 -0
  22. package/dist/command-runner.d.ts +9 -0
  23. package/dist/command-runner.js +592 -0
  24. package/dist/config.d.ts +7 -0
  25. package/dist/config.js +38 -0
  26. package/dist/control-server.d.ts +5 -0
  27. package/dist/control-server.js +292 -0
  28. package/dist/diskguard.d.ts +96 -0
  29. package/dist/diskguard.js +435 -0
  30. package/dist/error-classify.d.ts +20 -0
  31. package/dist/error-classify.js +159 -0
  32. package/dist/events.d.ts +26 -0
  33. package/dist/events.js +97 -0
  34. package/dist/gemini-binary.d.ts +4 -0
  35. package/dist/gemini-binary.js +64 -0
  36. package/dist/gemini-spawn.d.ts +52 -0
  37. package/dist/gemini-spawn.js +369 -0
  38. package/dist/git-identity.d.ts +18 -0
  39. package/dist/git-identity.js +104 -0
  40. package/dist/grok-binary.d.ts +4 -0
  41. package/dist/grok-binary.js +70 -0
  42. package/dist/grok-spawn.d.ts +41 -0
  43. package/dist/grok-spawn.js +495 -0
  44. package/dist/guardrails.d.ts +32 -0
  45. package/dist/guardrails.js +125 -0
  46. package/dist/hang-watchdog.d.ts +17 -0
  47. package/dist/hang-watchdog.js +48 -0
  48. package/dist/harness-registry.d.ts +55 -0
  49. package/dist/harness-registry.js +180 -0
  50. package/dist/heartbeat.d.ts +13 -0
  51. package/dist/heartbeat.js +95 -0
  52. package/dist/index.d.ts +12 -0
  53. package/dist/index.js +440 -0
  54. package/dist/install.d.ts +63 -0
  55. package/dist/install.js +467 -0
  56. package/dist/jsonl-tail.d.ts +20 -0
  57. package/dist/jsonl-tail.js +192 -0
  58. package/dist/kimi-binary.d.ts +4 -0
  59. package/dist/kimi-binary.js +68 -0
  60. package/dist/kimi-spawn.d.ts +47 -0
  61. package/dist/kimi-spawn.js +319 -0
  62. package/dist/lockfile.d.ts +30 -0
  63. package/dist/lockfile.js +169 -0
  64. package/dist/mcp-config.d.ts +23 -0
  65. package/dist/mcp-config.js +133 -0
  66. package/dist/memory-capture.d.ts +20 -0
  67. package/dist/memory-capture.js +45 -0
  68. package/dist/memory-pack.d.ts +34 -0
  69. package/dist/memory-pack.js +171 -0
  70. package/dist/model-filter.d.ts +4 -0
  71. package/dist/model-filter.js +34 -0
  72. package/dist/node-health.d.ts +18 -0
  73. package/dist/node-health.js +74 -0
  74. package/dist/pairing.d.ts +20 -0
  75. package/dist/pairing.js +129 -0
  76. package/dist/paths.d.ts +9 -0
  77. package/dist/paths.js +48 -0
  78. package/dist/precompact-hook.d.ts +27 -0
  79. package/dist/precompact-hook.js +136 -0
  80. package/dist/projects.d.ts +25 -0
  81. package/dist/projects.js +255 -0
  82. package/dist/prompt-keystrokes.d.ts +3 -0
  83. package/dist/prompt-keystrokes.js +56 -0
  84. package/dist/pty-helper.d.ts +26 -0
  85. package/dist/pty-helper.js +156 -0
  86. package/dist/request-pump.d.ts +12 -0
  87. package/dist/request-pump.js +249 -0
  88. package/dist/run-retry.d.ts +28 -0
  89. package/dist/run-retry.js +81 -0
  90. package/dist/self-update.d.ts +75 -0
  91. package/dist/self-update.js +192 -0
  92. package/dist/session-runner.d.ts +130 -0
  93. package/dist/session-runner.js +2271 -0
  94. package/dist/sleep-detector.d.ts +11 -0
  95. package/dist/sleep-detector.js +32 -0
  96. package/dist/state-migrate.d.ts +8 -0
  97. package/dist/state-migrate.js +91 -0
  98. package/dist/store.d.ts +5 -0
  99. package/dist/store.js +82 -0
  100. package/dist/supabase-client.d.ts +15 -0
  101. package/dist/supabase-client.js +119 -0
  102. package/dist/time-context.d.ts +10 -0
  103. package/dist/time-context.js +71 -0
  104. package/dist/tokens.d.ts +4 -0
  105. package/dist/tokens.js +33 -0
  106. package/dist/tui/app.d.ts +35 -0
  107. package/dist/tui/app.js +82 -0
  108. package/dist/tui/console-capture.d.ts +13 -0
  109. package/dist/tui/console-capture.js +62 -0
  110. package/dist/tui/dashboard.d.ts +42 -0
  111. package/dist/tui/dashboard.js +205 -0
  112. package/dist/tui/data.d.ts +57 -0
  113. package/dist/tui/data.js +62 -0
  114. package/dist/tui/harnesses.d.ts +16 -0
  115. package/dist/tui/harnesses.js +354 -0
  116. package/dist/tui/render.d.ts +21 -0
  117. package/dist/tui/render.js +116 -0
  118. package/dist/tui/requests.d.ts +18 -0
  119. package/dist/tui/requests.js +127 -0
  120. package/dist/tui/run.d.ts +13 -0
  121. package/dist/tui/run.js +178 -0
  122. package/dist/tui/transcript.d.ts +39 -0
  123. package/dist/tui/transcript.js +234 -0
  124. package/dist/tui.d.ts +1 -0
  125. package/dist/tui.js +314 -0
  126. package/dist/types.d.ts +65 -0
  127. package/dist/types.js +3 -0
  128. package/dist/win.d.ts +67 -0
  129. package/dist/win.js +317 -0
  130. package/package.json +60 -0
  131. package/scripts/fix-pty-helper.js +28 -0
  132. package/scripts/precompact-capture.js +292 -0
  133. package/scripts/smoke-test.sh +74 -0
@@ -0,0 +1,178 @@
1
+ "use strict";
2
+ // Terminal wiring for the console UI: raw-mode keys in, frames out.
3
+ //
4
+ // Everything decision-shaped lives in app.ts and the screens; this file
5
+ // owns only the things that need a real TTY.
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ exports.FEED_ROWS = void 0;
41
+ exports.shouldRenderTui = shouldRenderTui;
42
+ exports.runDashboard = runDashboard;
43
+ const readline = __importStar(require("readline"));
44
+ const app_1 = require("./app");
45
+ const data_1 = require("./data");
46
+ const dashboard_1 = require("./dashboard");
47
+ Object.defineProperty(exports, "FEED_ROWS", { enumerable: true, get: function () { return dashboard_1.FEED_ROWS; } });
48
+ const requests_1 = require("./requests");
49
+ const transcript_1 = require("./transcript");
50
+ const harnesses_1 = require("./harnesses");
51
+ const render_1 = require("./render");
52
+ const console_capture_1 = require("./console-capture");
53
+ const store_1 = require("../store");
54
+ const request_pump_1 = require("../request-pump");
55
+ function shouldRenderTui(env) {
56
+ return env.isTTY && env.stdinTTY && !env.noTui;
57
+ }
58
+ async function runDashboard(opts) {
59
+ const pairing = (0, store_1.readPairing)();
60
+ const data = (0, data_1.createDataLayer)({ token: pairing?.daemonToken ?? '' });
61
+ // In daemon mode the pump is running in THIS process and logs straight to
62
+ // the stdout we are painting on. Capture it for as long as we own the
63
+ // screen — including while the harness screen is up — and replay on exit.
64
+ const logs = (0, console_capture_1.captureConsole)();
65
+ const state = {
66
+ self: null, stats: null, requests: [], sel: 0, spin: 0,
67
+ pid: opts.pid, startedAt: opts.startedAt,
68
+ inflight: 0, paired: (0, store_1.isPaired)(), viewerMode: opts.viewerMode,
69
+ offline: false, now: Date.now(), version: opts.version,
70
+ lastLog: null, logCount: 0,
71
+ };
72
+ let quitting = false;
73
+ let inputAttached = false;
74
+ // While the harness screen owns the terminal, this one must go completely
75
+ // silent. Its poll and spin timers keep firing otherwise and repaint the
76
+ // dashboard straight over the harness panel — the screens flicker against
77
+ // each other and the harness rows never survive a frame.
78
+ let suspended = false;
79
+ const app = (0, app_1.createApp)({ id: 'boot', title: '+Ai Node', render: () => [] }, { onQuit: () => teardown(), onRedraw: () => draw() });
80
+ const openTranscript = (r) => app.push((0, transcript_1.createTranscriptScreen)({ data, host: app, request: r }));
81
+ app.replace((0, dashboard_1.createDashboardScreen)({
82
+ data, host: app, state,
83
+ openTranscript,
84
+ openRequests: () => app.push((0, requests_1.createRequestsScreen)({ data, host: app, openTranscript })),
85
+ openHarnesses: () => { void openHarnesses(); },
86
+ }));
87
+ // The harness screen owns the whole terminal (it hands the TTY to CLIs
88
+ // for login), so we detach entirely and rebuild our input when it exits.
89
+ async function openHarnesses() {
90
+ suspended = true;
91
+ detachInput();
92
+ (0, render_1.altOff)();
93
+ try {
94
+ await (0, harnesses_1.runHarnessesTui)({ embedded: true });
95
+ }
96
+ finally {
97
+ suspended = false;
98
+ (0, render_1.altOn)();
99
+ attachInput();
100
+ draw();
101
+ }
102
+ }
103
+ function draw() {
104
+ if (quitting || suspended)
105
+ return;
106
+ const latest = logs.last();
107
+ state.lastLog = latest ? latest.text.replace(/\s+/g, ' ') : null;
108
+ state.logCount = logs.count();
109
+ (0, render_1.home)();
110
+ process.stdout.write(app.frame((0, render_1.termWidth)()).join('\n') + '\n');
111
+ }
112
+ function onKeypress(_s, key) {
113
+ void app.handleKey(key);
114
+ }
115
+ function attachInput() {
116
+ if (inputAttached)
117
+ return;
118
+ readline.emitKeypressEvents(process.stdin);
119
+ if (process.stdin.isTTY)
120
+ process.stdin.setRawMode(true);
121
+ process.stdin.resume();
122
+ process.stdin.on('keypress', onKeypress);
123
+ inputAttached = true;
124
+ }
125
+ function detachInput() {
126
+ if (!inputAttached)
127
+ return;
128
+ process.stdin.off('keypress', onKeypress);
129
+ if (process.stdin.isTTY)
130
+ process.stdin.setRawMode(false);
131
+ process.stdin.pause();
132
+ inputAttached = false;
133
+ }
134
+ function teardown() {
135
+ if (quitting)
136
+ return;
137
+ quitting = true;
138
+ clearInterval(pollTimer);
139
+ clearInterval(spinTimer);
140
+ detachInput();
141
+ (0, render_1.altOff)();
142
+ // Give the operator back everything the daemon said while the UI had
143
+ // the screen — otherwise running under the dashboard swallows the log.
144
+ logs.restoreAndReplay();
145
+ process.exit(0);
146
+ }
147
+ // Each screen declares its own cadence via pollMs(); this ticks often
148
+ // enough to honour the shortest one without polling on every tick.
149
+ let lastPoll = 0;
150
+ const pollTimer = setInterval(() => {
151
+ if (suspended)
152
+ return;
153
+ const screen = app.current();
154
+ const due = screen.pollMs?.() ?? 5000;
155
+ if (Date.now() - lastPoll < due)
156
+ return;
157
+ lastPoll = Date.now();
158
+ state.inflight = (0, request_pump_1.inflightCount)();
159
+ void screen.poll?.().catch(() => { });
160
+ }, 250);
161
+ // Redraw on a spin tick only while something is actually moving, so an
162
+ // idle node isn't repainting the terminal ten times a second.
163
+ const spinTimer = setInterval(() => {
164
+ if (suspended)
165
+ return;
166
+ state.spin++;
167
+ if (state.stats?.active || state.inflight > 0)
168
+ draw();
169
+ }, 120);
170
+ // Resizing must not leave half-width panels behind.
171
+ process.stdout.on('resize', draw);
172
+ (0, render_1.altOn)();
173
+ attachInput();
174
+ state.inflight = (0, request_pump_1.inflightCount)();
175
+ await app.current().poll?.();
176
+ draw();
177
+ await new Promise(() => { });
178
+ }
@@ -0,0 +1,39 @@
1
+ import type { DataLayer, RequestRow } from './data';
2
+ import type { AppHost, Screen } from './app';
3
+ export interface Message {
4
+ role: 'user' | 'assistant' | 'thinking' | 'tool_use' | 'tool_result';
5
+ text?: string;
6
+ name?: string;
7
+ input?: unknown;
8
+ content?: unknown;
9
+ isError?: boolean;
10
+ ts?: string | null;
11
+ }
12
+ export interface TranscriptState {
13
+ request: RequestRow;
14
+ messages: Message[];
15
+ scroll: number;
16
+ follow: boolean;
17
+ loading: boolean;
18
+ viewportRows: number;
19
+ }
20
+ interface RawEvent {
21
+ type: string;
22
+ payload?: Record<string, unknown> | null;
23
+ emitted_at?: string | null;
24
+ }
25
+ export declare function wrapText(s: string, width: number): string[];
26
+ /**
27
+ * Turn the raw event feed (newest first, as the RPC returns it) into an
28
+ * ordered message list. Unknown and protocol-level events are dropped —
29
+ * a transcript is a conversation, not a wire log.
30
+ */
31
+ export declare function messagesFromEvents(events: RawEvent[], prompt: string | null): Message[];
32
+ export declare function messageLines(m: Message, width: number): string[];
33
+ export declare function renderTranscript(st: TranscriptState, width: number): string[];
34
+ export declare function createTranscriptScreen(deps: {
35
+ data: DataLayer;
36
+ host: AppHost;
37
+ request: RequestRow;
38
+ }): Screen;
39
+ export {};
@@ -0,0 +1,234 @@
1
+ "use strict";
2
+ // One request, in full: the conversation as it happened.
3
+ //
4
+ // While the run is live this auto-follows the tail. Scrolling up detaches
5
+ // following (nobody wants the view yanked away mid-read); G re-attaches.
6
+ //
7
+ // The event stream is delta-shaped — a sentence arrives as a dozen
8
+ // assistant_text events — so consecutive deltas of the same kind are
9
+ // coalesced before anything is rendered.
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.wrapText = wrapText;
12
+ exports.messagesFromEvents = messagesFromEvents;
13
+ exports.messageLines = messageLines;
14
+ exports.renderTranscript = renderTranscript;
15
+ exports.createTranscriptScreen = createTranscriptScreen;
16
+ const render_1 = require("./render");
17
+ const ACTIVE = new Set(['pending', 'starting', 'running']);
18
+ function wrapText(s, width) {
19
+ const out = [];
20
+ for (const paragraph of String(s ?? '').split('\n')) {
21
+ let line = '';
22
+ for (const word of paragraph.split(' ')) {
23
+ let w = word;
24
+ while (w.length > width) {
25
+ if (line) {
26
+ out.push(line);
27
+ line = '';
28
+ }
29
+ out.push(w.slice(0, width));
30
+ w = w.slice(width);
31
+ }
32
+ if (!line)
33
+ line = w;
34
+ else if (line.length + 1 + w.length <= width)
35
+ line += ` ${w}`;
36
+ else {
37
+ out.push(line);
38
+ line = w;
39
+ }
40
+ }
41
+ out.push(line);
42
+ }
43
+ return out;
44
+ }
45
+ function compact(value, max) {
46
+ if (value == null)
47
+ return '';
48
+ let s;
49
+ if (typeof value === 'string')
50
+ s = value;
51
+ else if (Array.isArray(value)) {
52
+ // tool_result content is [{type:'text', text:'…'}]
53
+ s = value
54
+ .map(v => (v && typeof v === 'object' && 'text' in v
55
+ ? String(v.text)
56
+ : JSON.stringify(v)))
57
+ .join(' ');
58
+ }
59
+ else
60
+ s = JSON.stringify(value);
61
+ return s.replace(/\s+/g, ' ').slice(0, max);
62
+ }
63
+ /**
64
+ * Turn the raw event feed (newest first, as the RPC returns it) into an
65
+ * ordered message list. Unknown and protocol-level events are dropped —
66
+ * a transcript is a conversation, not a wire log.
67
+ */
68
+ function messagesFromEvents(events, prompt) {
69
+ const msgs = [];
70
+ if (prompt)
71
+ msgs.push({ role: 'user', text: prompt });
72
+ const ordered = [...(events ?? [])].reverse(); // oldest first
73
+ for (const ev of ordered) {
74
+ const p = (ev?.payload ?? {});
75
+ const last = msgs[msgs.length - 1];
76
+ if (ev.type === 'assistant_text' || ev.type === 'thinking') {
77
+ const role = ev.type === 'thinking' ? 'thinking' : 'assistant';
78
+ const delta = String(p.delta ?? '');
79
+ if (!delta)
80
+ continue;
81
+ if (last && last.role === role)
82
+ last.text = `${last.text ?? ''}${delta}`;
83
+ else
84
+ msgs.push({ role, text: delta, ts: ev.emitted_at ?? null });
85
+ continue;
86
+ }
87
+ if (ev.type === 'tool_use') {
88
+ msgs.push({
89
+ role: 'tool_use',
90
+ name: String(p.name ?? '?'),
91
+ input: p.input,
92
+ ts: ev.emitted_at ?? null,
93
+ });
94
+ continue;
95
+ }
96
+ if (ev.type === 'tool_result') {
97
+ msgs.push({
98
+ role: 'tool_result',
99
+ content: p.content,
100
+ isError: p.isError === true,
101
+ ts: ev.emitted_at ?? null,
102
+ });
103
+ continue;
104
+ }
105
+ // Codex reports shell work as item.started / item.completed.
106
+ if (ev.type.startsWith('claude:codex.item.')) {
107
+ const item = (p.raw?.item ?? {});
108
+ if (item.type !== 'command_execution')
109
+ continue;
110
+ if (ev.type.endsWith('started')) {
111
+ msgs.push({ role: 'tool_use', name: 'command', input: item.command, ts: ev.emitted_at ?? null });
112
+ }
113
+ else {
114
+ msgs.push({
115
+ role: 'tool_result',
116
+ content: item.aggregated_output,
117
+ isError: typeof item.exit_code === 'number' && item.exit_code !== 0,
118
+ ts: ev.emitted_at ?? null,
119
+ });
120
+ }
121
+ continue;
122
+ }
123
+ // everything else (claude:system, ready, session_end, turn_complete,
124
+ // rate-limit notices) is protocol noise
125
+ }
126
+ return msgs;
127
+ }
128
+ function messageLines(m, width) {
129
+ const label = 6;
130
+ const body = Math.max(10, width - label - 3);
131
+ const head = (tag, colour) => colour(tag.padEnd(label));
132
+ if (m.role === 'tool_use') {
133
+ return [` ${head('tool', render_1.cyan)} ${(0, render_1.cyan)('⏺')} ${(0, render_1.bold)(m.name ?? '?')} ${(0, render_1.dim)(compact(m.input, Math.max(8, body - 12)))}`];
134
+ }
135
+ if (m.role === 'tool_result') {
136
+ const tag = m.isError ? (0, render_1.red)('error ') : (0, render_1.grey)('result');
137
+ return [` ${tag} ${(0, render_1.dim)(compact(m.content, body))}`];
138
+ }
139
+ const colour = m.role === 'user' ? render_1.green : m.role === 'thinking' ? render_1.grey : render_1.dim;
140
+ const tag = m.role === 'user' ? 'you' : m.role === 'thinking' ? 'think' : 'agent';
141
+ const wrapped = wrapText(m.text ?? '', body);
142
+ return wrapped.map((l, i) => ` ${i === 0 ? head(tag, colour) : ' '.repeat(label)} ${m.role === 'thinking' ? (0, render_1.grey)(l) : l}`);
143
+ }
144
+ function renderTranscript(st, width) {
145
+ const r = st.request;
146
+ const started = new Date(r.picked_up_at ?? r.created_at).getTime();
147
+ const dur = r.finished_at
148
+ ? (0, render_1.fmtDuration)(new Date(r.finished_at).getTime() - started)
149
+ : (0, render_1.fmtDuration)(Date.now() - started);
150
+ const out = [];
151
+ out.push(` ${(0, render_1.cyan)('▸')} ${(0, render_1.bold)(r.entity_name ?? '—')} ${(0, render_1.dim)('·')} ${(0, render_1.dim)(`${r.agent}/${r.mode}`)} ${(0, render_1.dim)('·')} ${(0, render_1.dim)(r.status)} ${(0, render_1.dim)(dur)}`);
152
+ out.push('');
153
+ if (st.loading && st.messages.length === 0) {
154
+ out.push((0, render_1.dim)(' loading transcript…'));
155
+ }
156
+ else if (st.messages.length === 0) {
157
+ out.push((0, render_1.dim)(' nothing recorded for this request'));
158
+ }
159
+ else {
160
+ const all = st.messages.flatMap(m => messageLines(m, width));
161
+ const rows = Math.max(1, st.viewportRows);
162
+ const start = st.follow
163
+ ? Math.max(0, all.length - rows)
164
+ : Math.min(st.scroll, Math.max(0, all.length - 1));
165
+ out.push(...all.slice(start, start + rows));
166
+ }
167
+ out.push('');
168
+ const live = ACTIVE.has(r.status) && st.follow ? (0, render_1.cyan)('following') : '';
169
+ out.push((0, render_1.dim)(' ↑↓ PgUp PgDn scroll G follow q back ') + live);
170
+ return out.map(l => (0, render_1.truncate)(l, width));
171
+ }
172
+ function createTranscriptScreen(deps) {
173
+ const st = {
174
+ request: deps.request, messages: [], scroll: 0,
175
+ follow: true, loading: true,
176
+ viewportRows: Math.max(8, (process.stdout.rows ?? 30) - 10),
177
+ };
178
+ return {
179
+ id: `transcript:${deps.request.id}`,
180
+ title: 'Transcript',
181
+ render: (width) => renderTranscript(st, width),
182
+ pollMs: () => (ACTIVE.has(st.request.status) ? 1000 : 15_000),
183
+ async poll() {
184
+ const detail = await deps.data.detail(deps.request.id);
185
+ st.loading = false;
186
+ if (!detail) {
187
+ deps.host.redraw();
188
+ return;
189
+ }
190
+ if (detail.request)
191
+ st.request = detail.request;
192
+ st.messages = messagesFromEvents(detail.events ?? [], st.request.prompt);
193
+ deps.host.redraw();
194
+ },
195
+ onKey(key) {
196
+ const page = Math.max(1, st.viewportRows - 2);
197
+ if (key.name === 'up' || key.name === 'k') {
198
+ st.follow = false;
199
+ st.scroll = Math.max(0, st.scroll - 1);
200
+ deps.host.redraw();
201
+ return;
202
+ }
203
+ if (key.name === 'down' || key.name === 'j') {
204
+ st.follow = false;
205
+ st.scroll += 1;
206
+ deps.host.redraw();
207
+ return;
208
+ }
209
+ if (key.name === 'pageup') {
210
+ st.follow = false;
211
+ st.scroll = Math.max(0, st.scroll - page);
212
+ deps.host.redraw();
213
+ return;
214
+ }
215
+ if (key.name === 'pagedown') {
216
+ st.follow = false;
217
+ st.scroll += page;
218
+ deps.host.redraw();
219
+ return;
220
+ }
221
+ if (key.name === 'g' && !key.shift) {
222
+ st.follow = false;
223
+ st.scroll = 0;
224
+ deps.host.redraw();
225
+ return;
226
+ }
227
+ if (key.name === 'g' && key.shift) {
228
+ st.follow = true;
229
+ deps.host.redraw();
230
+ return;
231
+ }
232
+ },
233
+ };
234
+ }
package/dist/tui.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare function runHarnessesTui(): Promise<void>;