@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,292 @@
1
+ "use strict";
2
+ // Local HTTP control surface, bound only to 127.0.0.1. The CodeFlows
3
+ // desktop polls this to detect whether the daemon is running and to
4
+ // surface stats / sessions / projects for THIS computer's runtime
5
+ // without needing the user to sign in.
6
+ //
7
+ // The daemon authenticates as the runtime on the user's behalf via
8
+ // its pairing token. Routes that hit Supabase fail gracefully (return
9
+ // 503 with `error: 'not_paired'`) when the daemon is unpaired.
10
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ var desc = Object.getOwnPropertyDescriptor(m, k);
13
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
14
+ desc = { enumerable: true, get: function() { return m[k]; } };
15
+ }
16
+ Object.defineProperty(o, k2, desc);
17
+ }) : (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ o[k2] = m[k];
20
+ }));
21
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
22
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
23
+ }) : function(o, v) {
24
+ o["default"] = v;
25
+ });
26
+ var __importStar = (this && this.__importStar) || (function () {
27
+ var ownKeys = function(o) {
28
+ ownKeys = Object.getOwnPropertyNames || function (o) {
29
+ var ar = [];
30
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
31
+ return ar;
32
+ };
33
+ return ownKeys(o);
34
+ };
35
+ return function (mod) {
36
+ if (mod && mod.__esModule) return mod;
37
+ var result = {};
38
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
39
+ __setModuleDefault(result, mod);
40
+ return result;
41
+ };
42
+ })();
43
+ Object.defineProperty(exports, "__esModule", { value: true });
44
+ exports.startControlServer = startControlServer;
45
+ const http = __importStar(require("http"));
46
+ const fs = __importStar(require("fs"));
47
+ const store_1 = require("./store");
48
+ const supabase_client_1 = require("./supabase-client");
49
+ const events_1 = require("./events");
50
+ const request_pump_1 = require("./request-pump");
51
+ const VERSION = (() => {
52
+ try {
53
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
54
+ const pkg = require('../package.json');
55
+ if (typeof pkg.version === 'string' && pkg.version.length > 0)
56
+ return pkg.version;
57
+ }
58
+ catch { /* fall through */ }
59
+ return '0.0.0';
60
+ })();
61
+ const STARTED_AT = Date.now();
62
+ let server = null;
63
+ let resolvedPort = 0;
64
+ function send(res, code, body) {
65
+ res.statusCode = code;
66
+ res.setHeader('content-type', 'application/json');
67
+ res.setHeader('cache-control', 'no-store');
68
+ res.end(JSON.stringify(body));
69
+ }
70
+ function token() {
71
+ const p = (0, store_1.readPairing)();
72
+ return p?.daemonToken ?? null;
73
+ }
74
+ async function rpcOr503(res, fn, params) {
75
+ const t = token();
76
+ if (!t) {
77
+ send(res, 503, { error: 'not_paired' });
78
+ return null;
79
+ }
80
+ try {
81
+ return await (0, supabase_client_1.rpc)(fn, { p_token: t, ...params });
82
+ }
83
+ catch (err) {
84
+ const code = err instanceof supabase_client_1.RpcError ? err.status : 500;
85
+ send(res, code === 401 ? 401 : 502, { error: err.message });
86
+ return null;
87
+ }
88
+ }
89
+ async function handleRequest(req, res) {
90
+ const url = new URL(req.url ?? '/', `http://${req.headers.host}`);
91
+ const path = url.pathname;
92
+ const method = req.method ?? 'GET';
93
+ if (path === '/status' && method === 'GET') {
94
+ return send(res, 200, {
95
+ ok: true,
96
+ version: VERSION,
97
+ pid: process.pid,
98
+ paired: (0, store_1.isPaired)(),
99
+ startedAt: STARTED_AT,
100
+ });
101
+ }
102
+ // Runtime metadata (name, hostname, capabilities) for THIS runtime.
103
+ if (path === '/self' && method === 'GET') {
104
+ const data = await rpcOr503(res, 'runtime_self', {});
105
+ if (data == null)
106
+ return;
107
+ return send(res, 200, { runtime: data });
108
+ }
109
+ // Time-window request counts. Augment with in-process pump health
110
+ // (inflight count) so the UI can show "pump is busy" / "pump is stuck"
111
+ // without needing a separate endpoint.
112
+ if (path === '/stats' && method === 'GET') {
113
+ const data = await rpcOr503(res, 'runtime_self_stats', {});
114
+ if (data == null)
115
+ return;
116
+ const inflight = (0, request_pump_1.inflightCount)();
117
+ return send(res, 200, { ...data, inflight });
118
+ }
119
+ // Recent requests (most recent first). ?limit=20 caps the count.
120
+ if (path === '/requests' && method === 'GET') {
121
+ const limit = Math.min(100, Math.max(1, Number(url.searchParams.get('limit') ?? 50)));
122
+ const data = await rpcOr503(res, 'runtime_self_recent_requests', { p_limit: limit });
123
+ if (data == null)
124
+ return;
125
+ return send(res, 200, { requests: data ?? [] });
126
+ }
127
+ // Full detail for one request (row + recent events + project).
128
+ const reqDetail = /^\/requests\/([0-9a-f-]{36})$/i.exec(path);
129
+ if (reqDetail && method === 'GET') {
130
+ const data = await rpcOr503(res, 'runtime_self_request_detail', { p_request_id: reqDetail[1] });
131
+ if (data == null)
132
+ return;
133
+ if (data === null)
134
+ return send(res, 404, { error: 'not_found' });
135
+ return send(res, 200, data);
136
+ }
137
+ // Full transcript for one request — normalized message list. For
138
+ // Claude TUI we parse the on-disk JSONL (which captures the whole
139
+ // conversation across turns). For agents without a JSONL we
140
+ // synthesise from the request row's prompt + accumulated
141
+ // assistant_text + emitted runtime_events.
142
+ const reqTranscript = /^\/requests\/([0-9a-f-]{36})\/transcript$/i.exec(path);
143
+ if (reqTranscript && method === 'GET') {
144
+ const detail = await rpcOr503(res, 'runtime_self_request_detail', { p_request_id: reqTranscript[1] });
145
+ if (detail == null)
146
+ return;
147
+ const r = detail.request;
148
+ if (!r)
149
+ return send(res, 404, { error: 'not_found' });
150
+ const messages = [];
151
+ // 1) The user's prompt is always the first message of THIS request.
152
+ if (typeof r.prompt === 'string' && r.prompt) {
153
+ messages.push({
154
+ role: 'user', text: r.prompt,
155
+ ts: r.created_at, source: 'request',
156
+ });
157
+ }
158
+ // 2) If there's a JSONL on disk, read it and normalise. This captures
159
+ // multi-turn TUI history. We skip lines we can't parse.
160
+ const jsonlPath = r.jsonl_path ?? null;
161
+ if (jsonlPath && fs.existsSync(jsonlPath)) {
162
+ try {
163
+ const text = fs.readFileSync(jsonlPath, 'utf-8');
164
+ const lines = text.split(/\r?\n/).filter(Boolean);
165
+ for (const raw of lines) {
166
+ let line = null;
167
+ try {
168
+ line = JSON.parse(raw);
169
+ }
170
+ catch {
171
+ continue;
172
+ }
173
+ if (!line)
174
+ continue;
175
+ const ts = line.timestamp
176
+ ?? line.ts
177
+ ?? null;
178
+ // Claude TUI: lines have a `type` field; reuse normalizeJsonlLine
179
+ // for the same content extraction the runtime uses for events.
180
+ const type = typeof line.type === 'string' ? line.type : '';
181
+ if (type === 'user') {
182
+ const message = line.message;
183
+ const content = message?.content;
184
+ if (typeof content === 'string') {
185
+ messages.push({ role: 'user', text: content, ts, source: 'jsonl' });
186
+ }
187
+ else if (Array.isArray(content)) {
188
+ for (const block of content) {
189
+ if (block?.type === 'text' && typeof block.text === 'string') {
190
+ messages.push({ role: 'user', text: block.text, ts, source: 'jsonl' });
191
+ }
192
+ else if (block?.type === 'tool_result') {
193
+ messages.push({
194
+ role: 'tool_result',
195
+ id: String(block.tool_use_id ?? ''),
196
+ content: block.content,
197
+ isError: block.is_error === true,
198
+ ts, source: 'jsonl',
199
+ });
200
+ }
201
+ }
202
+ }
203
+ }
204
+ else if (type === 'assistant') {
205
+ for (const ev of (0, events_1.normalizeJsonlLine)(line)) {
206
+ if (ev.type === 'assistant_text') {
207
+ messages.push({ role: 'assistant', text: ev.delta ?? '', ts, source: 'jsonl' });
208
+ }
209
+ else if (ev.type === 'tool_use') {
210
+ const tu = ev;
211
+ messages.push({ role: 'tool_use', id: tu.id, name: tu.name, input: tu.input, ts, source: 'jsonl' });
212
+ }
213
+ }
214
+ }
215
+ }
216
+ }
217
+ catch (err) {
218
+ // Read failure shouldn't kill the whole response — fall through.
219
+ console.error(`[control-server] transcript read failed: ${err.message}`);
220
+ }
221
+ }
222
+ else {
223
+ // No JSONL — for claude --print / codex / kimi / gemini we have
224
+ // (a) accumulated assistant_text, and (b) runtime_events for tool
225
+ // use. The text is one message; tool uses become their own rows.
226
+ if (typeof r.assistant_text === 'string' && r.assistant_text) {
227
+ messages.push({
228
+ role: 'assistant', text: r.assistant_text,
229
+ ts: r.finished_at ?? r.picked_up_at ?? r.created_at,
230
+ source: 'request',
231
+ });
232
+ }
233
+ // Interleave tool_use events that were emitted during the run.
234
+ const events = Array.isArray(detail.events) ? detail.events : [];
235
+ for (const ev of [...events].reverse()) {
236
+ if (ev.type === 'tool_use') {
237
+ const p = (ev.payload ?? {});
238
+ messages.push({ role: 'tool_use', id: p.id, name: p.name, input: p.input, ts: ev.emitted_at, source: 'event' });
239
+ }
240
+ else if (ev.type === 'tool_result') {
241
+ const p = (ev.payload ?? {});
242
+ messages.push({ role: 'tool_result', id: p.id, content: p.content, isError: p.isError === true, ts: ev.emitted_at, source: 'event' });
243
+ }
244
+ }
245
+ }
246
+ return send(res, 200, {
247
+ requestId: reqTranscript[1],
248
+ agent: r.agent ?? null,
249
+ mode: r.mode ?? null,
250
+ status: r.status ?? null,
251
+ jsonlPath,
252
+ messages,
253
+ });
254
+ }
255
+ // Projects on THIS runtime.
256
+ if (path === '/projects' && method === 'GET') {
257
+ const data = await rpcOr503(res, 'runtime_self_projects', {});
258
+ if (data == null)
259
+ return;
260
+ return send(res, 200, { projects: data ?? [] });
261
+ }
262
+ if (path === '/' && method === 'GET') {
263
+ return send(res, 200, {
264
+ service: 'ainode',
265
+ version: VERSION,
266
+ endpoints: [
267
+ 'GET /status', 'GET /self',
268
+ 'GET /stats', 'GET /requests', 'GET /requests/:id',
269
+ 'GET /requests/:id/transcript', 'GET /projects',
270
+ ],
271
+ });
272
+ }
273
+ return send(res, 404, { error: 'not_found' });
274
+ }
275
+ async function startControlServer() {
276
+ if (server)
277
+ throw new Error('control server already running');
278
+ server = http.createServer(handleRequest);
279
+ await new Promise((resolve, reject) => {
280
+ server.once('error', reject);
281
+ server.listen(0, '127.0.0.1', () => resolve());
282
+ });
283
+ const addr = server.address();
284
+ resolvedPort = typeof addr === 'object' && addr ? addr.port : 0;
285
+ return {
286
+ port: resolvedPort,
287
+ async close() {
288
+ await new Promise(resolve => server.close(() => resolve()));
289
+ server = null;
290
+ },
291
+ };
292
+ }
@@ -0,0 +1,96 @@
1
+ export interface DiskGuardConfig {
2
+ enabled: boolean;
3
+ highWaterPct: number;
4
+ lowWaterPct: number;
5
+ minAgeMs: number;
6
+ checkIntervalMs: number;
7
+ dryRun: boolean;
8
+ /** Filesystem root the scratch dirs live under (os.tmpdir()). */
9
+ tempRoot: string;
10
+ /** Dir-name prefixes eligible for eviction. */
11
+ prefixes: string[];
12
+ }
13
+ /** One scratch dir considered for eviction. */
14
+ export interface Candidate {
15
+ path: string;
16
+ /** Last-activity timestamp (ms) — lock heartbeat if present, else mtime.
17
+ * Used to sort oldest-first. */
18
+ activityMs: number;
19
+ /** now - mtime (ms). Guards against evicting freshly-created dirs. */
20
+ ageMs: number;
21
+ /** Best-effort recursive size in bytes. Only populated for dry-run
22
+ * (measuring thousands of dirs every live sweep is too expensive). */
23
+ sizeBytes: number;
24
+ /** True if a live session (in-process or a live-PID lock) owns this dir. */
25
+ active: boolean;
26
+ }
27
+ export interface SweepResult {
28
+ /** True if a sweep actually evaluated candidates (i.e. usage readable
29
+ * and at/over the high-water mark). */
30
+ ran: boolean;
31
+ usedBefore: number | null;
32
+ usedAfter: number | null;
33
+ /** Paths evicted (or, in dry-run, that WOULD be evicted). */
34
+ evicted: string[];
35
+ bytesFreed: number;
36
+ skippedActive: number;
37
+ skippedYoung: number;
38
+ reason: string;
39
+ }
40
+ export declare function loadDiskGuardConfig(env?: NodeJS.ProcessEnv): DiskGuardConfig;
41
+ export interface EvictionDeps {
42
+ listCandidates: () => Promise<Candidate[]>;
43
+ /** Current used-percentage of the volume, or null if unreadable. */
44
+ readUsedPct: () => Promise<number | null>;
45
+ /** Delete a scratch dir. Only called in live (non-dry-run) mode. */
46
+ deleteDir: (p: string) => Promise<void>;
47
+ /** Volume size in bytes — used to report bytes-freed and to estimate the
48
+ * dry-run stopping point. Optional. */
49
+ totalBytes?: number;
50
+ }
51
+ export interface EvictionOptions {
52
+ highWaterPct: number;
53
+ lowWaterPct: number;
54
+ minAgeMs: number;
55
+ dryRun: boolean;
56
+ }
57
+ export declare function runEvictionLoop(deps: EvictionDeps, opts: EvictionOptions): Promise<SweepResult>;
58
+ /** Single-flight wrapper: while a call is in flight, concurrent callers get
59
+ * the same promise instead of starting a second sweep (the mutex). */
60
+ export declare function makeSingleFlight<T>(): (fn: () => Promise<T>) => Promise<T>;
61
+ /** Remove a scratch dir. `recursive+force` unlinks symlink ENTRIES without
62
+ * following them, so the ~/.codex targets behind `sessions`/`auth.json`
63
+ * are never touched. ENOENT is swallowed by `force`. */
64
+ export declare function removeSessionDir(dir: string): Promise<void>;
65
+ /** Handle returned by beginSession — dispose it (with removeDir=true) when
66
+ * the run ends to stop the heartbeat, drop the active mark, and delete the
67
+ * scratch dir. Idempotent. */
68
+ export interface SessionHandle {
69
+ dispose(removeDir: boolean): Promise<void>;
70
+ }
71
+ export declare class DiskGuard {
72
+ readonly config: DiskGuardConfig;
73
+ private timer;
74
+ private readonly runExclusive;
75
+ /** Realpath'd scratch dirs owned by a live in-process session. */
76
+ private readonly activeDirs;
77
+ constructor(overrides?: Partial<DiskGuardConfig>);
78
+ /** Start the periodic sweeper. No-op (beyond a log line) if disabled;
79
+ * cleanup-on-exit via beginSession still works regardless. */
80
+ start(): void;
81
+ stop(): void;
82
+ /** Run one sweep. Concurrent calls share a single in-flight sweep. */
83
+ sweep(reason: string): Promise<SweepResult>;
84
+ private runSweep;
85
+ /** Register a live session: mark its dir active in-process and write a
86
+ * PID lock (with a periodic heartbeat) so a DIFFERENT runtime process
87
+ * can tell the dir is still owned and not evict it. */
88
+ beginSession(dir: string): SessionHandle;
89
+ private readUsedPct;
90
+ private readTotalBytes;
91
+ private listCandidates;
92
+ /** Decide whether a scratch dir is owned by a live session, and compute
93
+ * its last-activity time for oldest-first ordering. */
94
+ private classifyActivity;
95
+ }
96
+ export declare function getDiskGuard(): DiskGuard;