@neotx/cli 0.1.0-alpha.2 → 0.1.0-alpha.4
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 +381 -0
- package/dist/{agents-Y6LREFXP.js → agents-PH3P7G7E.js} +2 -2
- package/dist/{chunk-CP54H7WA.js → chunk-3ZP3BQXB.js} +6 -11
- package/dist/chunk-3ZP3BQXB.js.map +1 -0
- package/dist/{chunk-TNJOG54I.js → chunk-F622JUDY.js} +6 -2
- package/dist/{chunk-TNJOG54I.js.map → chunk-F622JUDY.js.map} +1 -1
- package/dist/{cost-DNGKT4UC.js → cost-OQGFNBBG.js} +3 -8
- package/dist/cost-OQGFNBBG.js.map +1 -0
- package/dist/daemon/supervisor-worker.js +7 -1
- package/dist/daemon/supervisor-worker.js.map +1 -1
- package/dist/daemon/worker.js +25 -0
- package/dist/daemon/worker.js.map +1 -1
- package/dist/doctor-ZBO73UID.js +337 -0
- package/dist/doctor-ZBO73UID.js.map +1 -0
- package/dist/index.js +12 -9
- package/dist/index.js.map +1 -1
- package/dist/{init-YNSPTCA3.js → init-UYS6KS5U.js} +4 -20
- package/dist/init-UYS6KS5U.js.map +1 -0
- package/dist/log-PTHLI7ZN.js +141 -0
- package/dist/log-PTHLI7ZN.js.map +1 -0
- package/dist/{mcp-GH6CCW7A.js → mcp-XHZND5A4.js} +6 -1
- package/dist/mcp-XHZND5A4.js.map +1 -0
- package/dist/memory-6R22DFS7.js +292 -0
- package/dist/memory-6R22DFS7.js.map +1 -0
- package/dist/{run-KIU2ZE72.js → run-GJLDWPUE.js} +32 -9
- package/dist/run-GJLDWPUE.js.map +1 -0
- package/dist/{runs-CHA2JM5K.js → runs-LOYOWU55.js} +9 -10
- package/dist/runs-LOYOWU55.js.map +1 -0
- package/dist/{supervise-KIB2EYY4.js → supervise-LVCGVYA4.js} +33 -28
- package/dist/supervise-LVCGVYA4.js.map +1 -0
- package/dist/{tui-QS3RPHKH.js → tui-6USVDV75.js} +100 -33
- package/dist/tui-6USVDV75.js.map +1 -0
- package/dist/version-XVOAMGDD.js +26 -0
- package/dist/version-XVOAMGDD.js.map +1 -0
- package/package.json +22 -4
- package/dist/chunk-CP54H7WA.js.map +0 -1
- package/dist/cost-DNGKT4UC.js.map +0 -1
- package/dist/doctor-GC4NH7H6.js +0 -173
- package/dist/doctor-GC4NH7H6.js.map +0 -1
- package/dist/init-YNSPTCA3.js.map +0 -1
- package/dist/mcp-GH6CCW7A.js.map +0 -1
- package/dist/run-KIU2ZE72.js.map +0 -1
- package/dist/runs-CHA2JM5K.js.map +0 -1
- package/dist/supervise-KIB2EYY4.js.map +0 -1
- package/dist/tui-QS3RPHKH.js.map +0 -1
- /package/dist/{agents-Y6LREFXP.js.map → agents-PH3P7G7E.js.map} +0 -0
|
@@ -11,10 +11,12 @@ import { appendFile, mkdir, readFile, rm } from "fs/promises";
|
|
|
11
11
|
import path from "path";
|
|
12
12
|
import { fileURLToPath } from "url";
|
|
13
13
|
import {
|
|
14
|
+
getSupervisorActivityPath,
|
|
14
15
|
getSupervisorDir,
|
|
15
16
|
getSupervisorInboxPath,
|
|
16
17
|
getSupervisorLockPath,
|
|
17
18
|
getSupervisorStatePath,
|
|
19
|
+
isProcessAlive,
|
|
18
20
|
loadGlobalConfig,
|
|
19
21
|
supervisorDaemonStateSchema
|
|
20
22
|
} from "@neotx/core";
|
|
@@ -30,14 +32,6 @@ async function readState(name) {
|
|
|
30
32
|
return null;
|
|
31
33
|
}
|
|
32
34
|
}
|
|
33
|
-
function isProcessAlive(pid) {
|
|
34
|
-
try {
|
|
35
|
-
process.kill(pid, 0);
|
|
36
|
-
return true;
|
|
37
|
-
} catch {
|
|
38
|
-
return false;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
35
|
async function isDaemonRunning(name) {
|
|
42
36
|
const state = await readState(name);
|
|
43
37
|
if (!state || state.status === "stopped") return null;
|
|
@@ -56,9 +50,7 @@ async function handleStatus(name) {
|
|
|
56
50
|
console.log(` Port: ${state.port}`);
|
|
57
51
|
console.log(` Session: ${state.sessionId}`);
|
|
58
52
|
console.log(` Started: ${state.startedAt}`);
|
|
59
|
-
console.log(
|
|
60
|
-
` Interval: ${config.supervisor.idleIntervalMs / 1e3}s (skip up to ${config.supervisor.idleSkipMax} idle)`
|
|
61
|
-
);
|
|
53
|
+
console.log(` Timeout: ${config.supervisor.eventTimeoutMs / 1e3}s`);
|
|
62
54
|
console.log(` Heartbeats: ${state.heartbeatCount}`);
|
|
63
55
|
if (state.lastHeartbeat) {
|
|
64
56
|
console.log(` Last beat: ${state.lastHeartbeat}`);
|
|
@@ -68,7 +60,7 @@ async function handleStatus(name) {
|
|
|
68
60
|
console.log(` Status: ${state.status}`);
|
|
69
61
|
console.log("");
|
|
70
62
|
console.log(` Health: curl localhost:${state.port}/health`);
|
|
71
|
-
console.log("
|
|
63
|
+
console.log(" TUI: neo supervise");
|
|
72
64
|
console.log(" Stop: neo supervise --kill");
|
|
73
65
|
}
|
|
74
66
|
async function handleKill(name) {
|
|
@@ -112,7 +104,7 @@ async function startDaemon(name) {
|
|
|
112
104
|
const running = await isDaemonRunning(name);
|
|
113
105
|
if (running) {
|
|
114
106
|
printError(`Supervisor "${name}" is already running (PID ${running.pid}).`);
|
|
115
|
-
printError("Use --kill first, or
|
|
107
|
+
printError("Use --kill first, or run neo supervise to open TUI.");
|
|
116
108
|
process.exitCode = 1;
|
|
117
109
|
return;
|
|
118
110
|
}
|
|
@@ -140,7 +132,7 @@ async function startDaemon(name) {
|
|
|
140
132
|
console.log(` Health: curl localhost:${config.supervisor.port}/health`);
|
|
141
133
|
console.log(` Webhook: curl -X POST localhost:${config.supervisor.port}/webhook -d '{}'`);
|
|
142
134
|
console.log(` Logs: ${getSupervisorDir(name)}/daemon.log`);
|
|
143
|
-
console.log(`
|
|
135
|
+
console.log(` TUI: neo supervise`);
|
|
144
136
|
console.log(` Status: neo supervise --status`);
|
|
145
137
|
console.log(` Stop: neo supervise --kill`);
|
|
146
138
|
}
|
|
@@ -152,7 +144,7 @@ async function handleAttach(name) {
|
|
|
152
144
|
process.exitCode = 1;
|
|
153
145
|
return;
|
|
154
146
|
}
|
|
155
|
-
const { renderSupervisorTui } = await import("./tui-
|
|
147
|
+
const { renderSupervisorTui } = await import("./tui-6USVDV75.js");
|
|
156
148
|
await renderSupervisorTui(name);
|
|
157
149
|
}
|
|
158
150
|
async function handleMessage(name, text) {
|
|
@@ -162,14 +154,13 @@ async function handleMessage(name, text) {
|
|
|
162
154
|
process.exitCode = 1;
|
|
163
155
|
return;
|
|
164
156
|
}
|
|
165
|
-
const
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
}
|
|
172
|
-
await appendFile(inboxPath, `${JSON.stringify(message)}
|
|
157
|
+
const id = randomUUID();
|
|
158
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
159
|
+
const message = { id, from: "api", text, timestamp };
|
|
160
|
+
await appendFile(getSupervisorInboxPath(name), `${JSON.stringify(message)}
|
|
161
|
+
`, "utf-8");
|
|
162
|
+
const activityEntry = { id, type: "message", summary: text, timestamp };
|
|
163
|
+
await appendFile(getSupervisorActivityPath(name), `${JSON.stringify(activityEntry)}
|
|
173
164
|
`, "utf-8");
|
|
174
165
|
printSuccess(`Message sent to supervisor "${name}".`);
|
|
175
166
|
}
|
|
@@ -196,7 +187,13 @@ var supervise_default = defineCommand({
|
|
|
196
187
|
},
|
|
197
188
|
attach: {
|
|
198
189
|
type: "boolean",
|
|
199
|
-
description: "Open the TUI for a running supervisor",
|
|
190
|
+
description: "Open the TUI for a running supervisor (default when no flags given)",
|
|
191
|
+
default: false
|
|
192
|
+
},
|
|
193
|
+
detach: {
|
|
194
|
+
type: "boolean",
|
|
195
|
+
alias: "d",
|
|
196
|
+
description: "Start daemon in the background without opening the TUI",
|
|
200
197
|
default: false
|
|
201
198
|
},
|
|
202
199
|
message: {
|
|
@@ -222,16 +219,24 @@ var supervise_default = defineCommand({
|
|
|
222
219
|
await handleMessage(name, args.message);
|
|
223
220
|
return;
|
|
224
221
|
}
|
|
222
|
+
if (args.detach) {
|
|
223
|
+
const alreadyRunning2 = await isDaemonRunning(name);
|
|
224
|
+
if (alreadyRunning2) {
|
|
225
|
+
printSuccess(`Supervisor "${name}" already running (PID ${alreadyRunning2.pid}).`);
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
await startDaemon(name);
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
225
231
|
const alreadyRunning = await isDaemonRunning(name);
|
|
226
232
|
if (!alreadyRunning) {
|
|
227
233
|
await startDaemon(name);
|
|
228
|
-
await new Promise((r) => setTimeout(r,
|
|
234
|
+
await new Promise((r) => setTimeout(r, 1500));
|
|
229
235
|
}
|
|
230
|
-
|
|
231
|
-
await renderSupervisorTui(name);
|
|
236
|
+
await handleAttach(name);
|
|
232
237
|
}
|
|
233
238
|
});
|
|
234
239
|
export {
|
|
235
240
|
supervise_default as default
|
|
236
241
|
};
|
|
237
|
-
//# sourceMappingURL=supervise-
|
|
242
|
+
//# sourceMappingURL=supervise-LVCGVYA4.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/supervise.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport { randomUUID } from \"node:crypto\";\nimport { closeSync, existsSync, openSync } from \"node:fs\";\nimport { appendFile, mkdir, readFile, rm } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport {\n getSupervisorActivityPath,\n getSupervisorDir,\n getSupervisorInboxPath,\n getSupervisorLockPath,\n getSupervisorStatePath,\n isProcessAlive,\n loadGlobalConfig,\n type SupervisorDaemonState,\n supervisorDaemonStateSchema,\n} from \"@neotx/core\";\nimport { defineCommand } from \"citty\";\nimport { printError, printSuccess } from \"../output.js\";\n\nconst DEFAULT_NAME = \"supervisor\";\n\nasync function readState(name: string): Promise<SupervisorDaemonState | null> {\n const statePath = getSupervisorStatePath(name);\n if (!existsSync(statePath)) return null;\n try {\n const raw = await readFile(statePath, \"utf-8\");\n return supervisorDaemonStateSchema.parse(JSON.parse(raw));\n } catch {\n return null;\n }\n}\n\nasync function isDaemonRunning(name: string): Promise<SupervisorDaemonState | null> {\n const state = await readState(name);\n if (!state || state.status === \"stopped\") return null;\n if (!isProcessAlive(state.pid)) return null;\n return state;\n}\n\nasync function handleStatus(name: string): Promise<void> {\n const state = await isDaemonRunning(name);\n if (!state) {\n console.log(`No supervisor daemon running (name: ${name}).`);\n return;\n }\n\n const config = await loadGlobalConfig();\n printSuccess(`Supervisor \"${name}\" running`);\n console.log(` PID: ${state.pid}`);\n console.log(` Port: ${state.port}`);\n console.log(` Session: ${state.sessionId}`);\n console.log(` Started: ${state.startedAt}`);\n console.log(` Timeout: ${config.supervisor.eventTimeoutMs / 1000}s`);\n console.log(` Heartbeats: ${state.heartbeatCount}`);\n if (state.lastHeartbeat) {\n console.log(` Last beat: ${state.lastHeartbeat}`);\n }\n console.log(` Cost today: $${state.todayCostUsd?.toFixed(2) ?? \"0.00\"}`);\n console.log(` Cost total: $${state.totalCostUsd?.toFixed(2) ?? \"0.00\"}`);\n console.log(` Status: ${state.status}`);\n console.log(\"\");\n console.log(` Health: curl localhost:${state.port}/health`);\n console.log(\" TUI: neo supervise\");\n console.log(\" Stop: neo supervise --kill\");\n}\n\nasync function handleKill(name: string): Promise<void> {\n const state = await isDaemonRunning(name);\n if (!state) {\n printError(`No supervisor daemon running (name: ${name}).`);\n\n // Clean up stale lock if exists\n const lockPath = getSupervisorLockPath(name);\n if (existsSync(lockPath)) {\n await rm(lockPath, { force: true });\n }\n process.exitCode = 1;\n return;\n }\n\n // Send SIGTERM for graceful shutdown, then SIGKILL after 10s\n const pid = state.pid;\n try {\n process.kill(pid, \"SIGTERM\");\n printSuccess(`Sent SIGTERM to supervisor \"${name}\" (PID ${pid})`);\n } catch {\n printError(`Failed to send signal to PID ${pid}. Cleaning up.`);\n const lockPath = getSupervisorLockPath(name);\n await rm(lockPath, { force: true });\n return;\n }\n\n // Wait up to 10s for graceful exit, then force kill\n const deadline = Date.now() + 10_000;\n while (Date.now() < deadline) {\n await new Promise((r) => setTimeout(r, 500));\n if (!isProcessAlive(pid)) {\n printSuccess(\"Daemon stopped.\");\n return;\n }\n }\n\n // Force kill\n try {\n process.kill(pid, \"SIGKILL\");\n printSuccess(`Daemon did not exit in time — sent SIGKILL (PID ${pid}).`);\n } catch {\n // Already dead\n }\n\n // Clean up lock\n const lockPath = getSupervisorLockPath(name);\n await rm(lockPath, { force: true });\n}\n\nasync function startDaemon(name: string): Promise<void> {\n const running = await isDaemonRunning(name);\n if (running) {\n printError(`Supervisor \"${name}\" is already running (PID ${running.pid}).`);\n printError(\"Use --kill first, or run neo supervise to open TUI.\");\n process.exitCode = 1;\n return;\n }\n\n // Clean up stale lock\n const lockPath = getSupervisorLockPath(name);\n if (existsSync(lockPath)) {\n await rm(lockPath, { force: true });\n }\n\n // Resolve the worker script path and package root (for module resolution)\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n const workerPath = path.join(__dirname, \"daemon\", \"supervisor-worker.js\");\n const packageRoot = path.resolve(__dirname, \"..\");\n\n // Spawn as detached child process with stdio to log file\n const logDir = getSupervisorDir(name);\n await mkdir(logDir, { recursive: true });\n const logFd = openSync(path.join(logDir, \"daemon.log\"), \"a\");\n const child = spawn(process.execPath, [workerPath, name], {\n detached: true,\n stdio: [\"ignore\", logFd, logFd],\n cwd: packageRoot,\n env: process.env,\n });\n child.unref();\n closeSync(logFd);\n\n const config = await loadGlobalConfig();\n printSuccess(`Supervisor \"${name}\" started (PID ${child.pid})`);\n console.log(` Port: ${config.supervisor.port}`);\n console.log(` Health: curl localhost:${config.supervisor.port}/health`);\n console.log(` Webhook: curl -X POST localhost:${config.supervisor.port}/webhook -d '{}'`);\n console.log(` Logs: ${getSupervisorDir(name)}/daemon.log`);\n console.log(` TUI: neo supervise`);\n console.log(` Status: neo supervise --status`);\n console.log(` Stop: neo supervise --kill`);\n}\n\nasync function handleAttach(name: string): Promise<void> {\n const running = await isDaemonRunning(name);\n if (!running) {\n printError(`No supervisor daemon running (name: ${name}).`);\n printError(\"Start with: neo supervise\");\n process.exitCode = 1;\n return;\n }\n\n const { renderSupervisorTui } = await import(\"../tui/index.js\");\n await renderSupervisorTui(name);\n}\n\nasync function handleMessage(name: string, text: string): Promise<void> {\n const running = await isDaemonRunning(name);\n if (!running) {\n printError(`No supervisor daemon running (name: ${name}).`);\n process.exitCode = 1;\n return;\n }\n\n const id = randomUUID();\n const timestamp = new Date().toISOString();\n\n const message = { id, from: \"api\" as const, text, timestamp };\n await appendFile(getSupervisorInboxPath(name), `${JSON.stringify(message)}\\n`, \"utf-8\");\n\n // Write to activity.jsonl so the message appears in the TUI conversation\n const activityEntry = { id, type: \"message\", summary: text, timestamp };\n await appendFile(getSupervisorActivityPath(name), `${JSON.stringify(activityEntry)}\\n`, \"utf-8\");\n\n printSuccess(`Message sent to supervisor \"${name}\".`);\n}\n\nexport default defineCommand({\n meta: {\n name: \"supervise\",\n description: \"Manage the autonomous supervisor daemon\",\n },\n args: {\n name: {\n type: \"string\",\n description: \"Supervisor instance name\",\n default: DEFAULT_NAME,\n },\n status: {\n type: \"boolean\",\n description: \"Show supervisor status\",\n default: false,\n },\n kill: {\n type: \"boolean\",\n description: \"Stop the running supervisor\",\n default: false,\n },\n attach: {\n type: \"boolean\",\n description: \"Open the TUI for a running supervisor (default when no flags given)\",\n default: false,\n },\n detach: {\n type: \"boolean\",\n alias: \"d\",\n description: \"Start daemon in the background without opening the TUI\",\n default: false,\n },\n message: {\n type: \"string\",\n description: \"Send a message to the supervisor inbox\",\n },\n },\n async run({ args }) {\n const name = args.name;\n\n if (args.status) {\n await handleStatus(name);\n return;\n }\n\n if (args.kill) {\n await handleKill(name);\n return;\n }\n\n if (args.attach) {\n await handleAttach(name);\n return;\n }\n\n if (args.message) {\n await handleMessage(name, args.message);\n return;\n }\n\n // --detach: start daemon headless (no TUI)\n if (args.detach) {\n const alreadyRunning = await isDaemonRunning(name);\n if (alreadyRunning) {\n printSuccess(`Supervisor \"${name}\" already running (PID ${alreadyRunning.pid}).`);\n return;\n }\n await startDaemon(name);\n return;\n }\n\n // Default: start daemon if needed, then open TUI\n const alreadyRunning = await isDaemonRunning(name);\n if (!alreadyRunning) {\n await startDaemon(name);\n // Wait briefly for daemon to initialize before attaching\n await new Promise((r) => setTimeout(r, 1500));\n }\n await handleAttach(name);\n },\n});\n"],"mappings":"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","names":["lockPath","alreadyRunning"]}
|
|
@@ -5,10 +5,13 @@ import React from "react";
|
|
|
5
5
|
// src/tui/supervisor-tui.tsx
|
|
6
6
|
import { randomUUID } from "crypto";
|
|
7
7
|
import { appendFile, readFile } from "fs/promises";
|
|
8
|
+
import path from "path";
|
|
8
9
|
import {
|
|
9
10
|
getSupervisorActivityPath,
|
|
11
|
+
getSupervisorDir,
|
|
10
12
|
getSupervisorInboxPath,
|
|
11
|
-
getSupervisorStatePath
|
|
13
|
+
getSupervisorStatePath,
|
|
14
|
+
MemoryStore
|
|
12
15
|
} from "@neotx/core";
|
|
13
16
|
import { Box, Text, useApp, useInput, useStdout } from "ink";
|
|
14
17
|
import TextInput from "ink-text-input";
|
|
@@ -283,39 +286,80 @@ function ActivityRow({
|
|
|
283
286
|
/* @__PURE__ */ jsx(Text, { dimColor: isOld, children: formatTime(entry.timestamp) }),
|
|
284
287
|
/* @__PURE__ */ jsx(Text, { color, dimColor: isOld, bold: isLatest, children: icon }),
|
|
285
288
|
/* @__PURE__ */ jsx(Text, { color, dimColor: isOld, bold: true, children: label }),
|
|
286
|
-
/* @__PURE__ */ jsx(Text, { dimColor: isOld, bold: isLatest,
|
|
289
|
+
/* @__PURE__ */ jsx(Text, { dimColor: isOld, bold: isLatest, children: entry.summary })
|
|
287
290
|
] });
|
|
288
291
|
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
292
|
+
var TASK_STATUS_COLORS = {
|
|
293
|
+
in_progress: "#60a5fa",
|
|
294
|
+
blocked: "#f87171",
|
|
295
|
+
pending: "#6b7280",
|
|
296
|
+
done: "#4ade80"
|
|
297
|
+
};
|
|
298
|
+
var TASK_STATUS_LABELS = {
|
|
299
|
+
in_progress: "ACTIVE",
|
|
300
|
+
blocked: "BLOCK",
|
|
301
|
+
pending: "\xB7"
|
|
302
|
+
};
|
|
303
|
+
function TaskPanel({ tasks }) {
|
|
304
|
+
const active = tasks.filter((t) => t.outcome !== "done" && t.outcome !== "abandoned");
|
|
305
|
+
const doneCount = tasks.filter((t) => t.outcome === "done").length;
|
|
306
|
+
if (tasks.length === 0) return null;
|
|
307
|
+
const MAX_VISIBLE = 6;
|
|
308
|
+
const visible = active.slice(0, MAX_VISIBLE);
|
|
309
|
+
const overflow = active.length - visible.length;
|
|
299
310
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
300
311
|
/* @__PURE__ */ jsxs(Box, { paddingX: 2, gap: 1, children: [
|
|
301
312
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u251C" }),
|
|
302
|
-
/* @__PURE__ */
|
|
303
|
-
|
|
304
|
-
"
|
|
305
|
-
|
|
313
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, bold: true, children: "TASKS" }),
|
|
314
|
+
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
315
|
+
"(",
|
|
316
|
+
active.length,
|
|
317
|
+
" active, ",
|
|
318
|
+
doneCount,
|
|
319
|
+
" done)"
|
|
306
320
|
] }),
|
|
307
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2500".repeat(
|
|
321
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2500".repeat(30) })
|
|
308
322
|
] }),
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
323
|
+
visible.map((t) => {
|
|
324
|
+
const status = t.outcome ?? "pending";
|
|
325
|
+
const color = TASK_STATUS_COLORS[status] ?? "#6b7280";
|
|
326
|
+
const label = (TASK_STATUS_LABELS[status] ?? "\xB7").padEnd(6);
|
|
327
|
+
const prio = t.severity ? `[${t.severity.slice(0, 3)}] ` : "";
|
|
328
|
+
const repo = t.scope !== "global" ? path.basename(t.scope) : "";
|
|
329
|
+
const run = t.runId ? `run:${t.runId.slice(0, 4)}` : "";
|
|
330
|
+
const meta = [repo, run].filter(Boolean).join(" ");
|
|
331
|
+
return /* @__PURE__ */ jsxs(Box, { gap: 1, paddingX: 2, children: [
|
|
332
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
|
|
333
|
+
/* @__PURE__ */ jsx(Text, { color, bold: true, children: label }),
|
|
334
|
+
prio && /* @__PURE__ */ jsx(Text, { dimColor: true, children: prio.padEnd(5) }),
|
|
335
|
+
/* @__PURE__ */ jsx(Text, { wrap: "truncate", children: t.content }),
|
|
336
|
+
meta && /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
337
|
+
"(",
|
|
338
|
+
meta,
|
|
339
|
+
")"
|
|
340
|
+
] })
|
|
341
|
+
] }, t.id);
|
|
342
|
+
}),
|
|
343
|
+
overflow > 0 && /* @__PURE__ */ jsx(Box, { paddingX: 2, children: /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
344
|
+
"\u2502 ... +",
|
|
345
|
+
overflow,
|
|
346
|
+
" more pending"
|
|
347
|
+
] }) })
|
|
314
348
|
] });
|
|
315
349
|
}
|
|
350
|
+
var ACTIVITY_TYPES = /* @__PURE__ */ new Set([
|
|
351
|
+
"heartbeat",
|
|
352
|
+
"decision",
|
|
353
|
+
"action",
|
|
354
|
+
"dispatch",
|
|
355
|
+
"error",
|
|
356
|
+
"event",
|
|
357
|
+
"message"
|
|
358
|
+
]);
|
|
316
359
|
function ActivityPanel({ entries, termHeight }) {
|
|
317
|
-
const maxVisible = Math.max(5, Math.min(MAX_VISIBLE_ENTRIES, termHeight -
|
|
318
|
-
const
|
|
360
|
+
const maxVisible = Math.max(5, Math.min(MAX_VISIBLE_ENTRIES, termHeight - 10));
|
|
361
|
+
const filtered = entries.filter((e) => ACTIVITY_TYPES.has(e.type));
|
|
362
|
+
const visible = filtered.slice(-maxVisible);
|
|
319
363
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
320
364
|
/* @__PURE__ */ jsxs(Box, { paddingX: 2, gap: 1, children: [
|
|
321
365
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u251C" }),
|
|
@@ -404,15 +448,27 @@ async function readActivity(name, maxEntries) {
|
|
|
404
448
|
return [];
|
|
405
449
|
}
|
|
406
450
|
}
|
|
451
|
+
function readTasks(name) {
|
|
452
|
+
try {
|
|
453
|
+
const dir = getSupervisorDir(name);
|
|
454
|
+
const store = new MemoryStore(path.join(dir, "memory.sqlite"));
|
|
455
|
+
const tasks = store.query({ types: ["task"], limit: 20, sortBy: "createdAt" });
|
|
456
|
+
store.close();
|
|
457
|
+
return tasks;
|
|
458
|
+
} catch {
|
|
459
|
+
return [];
|
|
460
|
+
}
|
|
461
|
+
}
|
|
407
462
|
async function sendMessage(name, text) {
|
|
408
|
-
const
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
text,
|
|
412
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
413
|
-
};
|
|
463
|
+
const id = randomUUID();
|
|
464
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
465
|
+
const message = { id, from: "tui", text, timestamp };
|
|
414
466
|
const inboxPath = getSupervisorInboxPath(name);
|
|
415
467
|
await appendFile(inboxPath, `${JSON.stringify(message)}
|
|
468
|
+
`, "utf-8");
|
|
469
|
+
const activityEntry = { id, type: "message", summary: text, timestamp };
|
|
470
|
+
const activityPath = getSupervisorActivityPath(name);
|
|
471
|
+
await appendFile(activityPath, `${JSON.stringify(activityEntry)}
|
|
416
472
|
`, "utf-8");
|
|
417
473
|
}
|
|
418
474
|
function SupervisorTui({ name }) {
|
|
@@ -422,6 +478,7 @@ function SupervisorTui({ name }) {
|
|
|
422
478
|
const clock = useClock();
|
|
423
479
|
const [state, setState] = useState(null);
|
|
424
480
|
const [entries, setEntries] = useState([]);
|
|
481
|
+
const [tasks, setTasks] = useState([]);
|
|
425
482
|
const [input, setInput] = useState("");
|
|
426
483
|
const [lastSent, setLastSent] = useState("");
|
|
427
484
|
const [termHeight, setTermHeight] = useState(stdout?.rows ?? 30);
|
|
@@ -445,6 +502,7 @@ function SupervisorTui({ name }) {
|
|
|
445
502
|
if (!active) return;
|
|
446
503
|
setState(newState);
|
|
447
504
|
setEntries(newEntries);
|
|
505
|
+
setTasks(readTasks(name));
|
|
448
506
|
}
|
|
449
507
|
poll();
|
|
450
508
|
const interval = setInterval(poll, POLL_INTERVAL_MS);
|
|
@@ -471,8 +529,17 @@ function SupervisorTui({ name }) {
|
|
|
471
529
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
472
530
|
/* @__PURE__ */ jsx(HeaderBar, { state, name, frame, clock }),
|
|
473
531
|
/* @__PURE__ */ jsx(BudgetPanel, { state, dailyCap: 50, costHistory }),
|
|
474
|
-
/* @__PURE__ */ jsx(
|
|
475
|
-
/* @__PURE__ */ jsx(
|
|
532
|
+
/* @__PURE__ */ jsx(TaskPanel, { tasks }),
|
|
533
|
+
/* @__PURE__ */ jsx(
|
|
534
|
+
ActivityPanel,
|
|
535
|
+
{
|
|
536
|
+
entries,
|
|
537
|
+
termHeight: termHeight - (tasks.length > 0 ? Math.min(
|
|
538
|
+
tasks.filter((t) => t.outcome !== "done" && t.outcome !== "abandoned").length,
|
|
539
|
+
6
|
|
540
|
+
) + 2 : 0)
|
|
541
|
+
}
|
|
542
|
+
),
|
|
476
543
|
/* @__PURE__ */ jsx(InputPanel, { value: input, onChange: setInput, onSubmit: handleSubmit, lastSent }),
|
|
477
544
|
/* @__PURE__ */ jsx(Footer, {})
|
|
478
545
|
] });
|
|
@@ -486,4 +553,4 @@ async function renderSupervisorTui(name) {
|
|
|
486
553
|
export {
|
|
487
554
|
renderSupervisorTui
|
|
488
555
|
};
|
|
489
|
-
//# sourceMappingURL=tui-
|
|
556
|
+
//# sourceMappingURL=tui-6USVDV75.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/tui/index.ts","../src/tui/supervisor-tui.tsx"],"sourcesContent":["import { render } from \"ink\";\nimport React from \"react\";\nimport { SupervisorTui } from \"./supervisor-tui.js\";\n\n/**\n * Render the supervisor TUI. Returns a promise that resolves when the user exits.\n */\nexport async function renderSupervisorTui(name: string): Promise<void> {\n const { waitUntilExit } = render(React.createElement(SupervisorTui, { name }));\n await waitUntilExit();\n}\n","import { randomUUID } from \"node:crypto\";\nimport { appendFile, readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { ActivityEntry, MemoryEntry, SupervisorDaemonState } from \"@neotx/core\";\nimport {\n getSupervisorActivityPath,\n getSupervisorDir,\n getSupervisorInboxPath,\n getSupervisorStatePath,\n MemoryStore,\n} from \"@neotx/core\";\nimport { Box, Text, useApp, useInput, useStdout } from \"ink\";\nimport TextInput from \"ink-text-input\";\nimport { useCallback, useEffect, useState } from \"react\";\n\n// ─── Constants ───────────────────────────────────────────\n\nconst MAX_VISIBLE_ENTRIES = 24;\nconst POLL_INTERVAL_MS = 1_500;\nconst ANIMATION_TICK_MS = 400;\n\n// ─── Unicode Visual Elements ─────────────────────────────\n\nconst SPARK_CHARS = [\"▁\", \"▂\", \"▃\", \"▄\", \"▅\", \"▆\", \"▇\", \"█\"];\nconst BLOCK_FULL = \"█\";\nconst BLOCK_EMPTY = \"░\";\nconst PULSE_FRAMES = [\"◉\", \"◎\", \"○\", \"◎\"];\nconst IDLE_FRAMES = [\"◌\", \"◌\", \"◌\", \"◌\"];\n\nconst TYPE_ICONS: Record<string, string> = {\n heartbeat: \"♥\",\n decision: \"★\",\n action: \"⚡\",\n error: \"✖\",\n event: \"◆\",\n message: \"✉\",\n thinking: \"◇\",\n plan: \"▸\",\n dispatch: \"↗\",\n tool_use: \"⊘\",\n};\n\nconst TYPE_COLORS: Record<string, string> = {\n heartbeat: \"#6ee7b7\",\n decision: \"#fbbf24\",\n action: \"#60a5fa\",\n error: \"#f87171\",\n event: \"#c084fc\",\n message: \"#67e8f9\",\n thinking: \"#a78bfa\",\n plan: \"#34d399\",\n dispatch: \"#f472b6\",\n tool_use: \"#38bdf8\",\n};\n\nconst TYPE_LABELS: Record<string, string> = {\n heartbeat: \"BEAT\",\n decision: \"DECIDE\",\n action: \"ACTION\",\n error: \"ERROR\",\n event: \"EVENT\",\n message: \"MSG\",\n thinking: \"THINK\",\n plan: \"PLAN\",\n dispatch: \"SEND\",\n tool_use: \"TOOL\",\n};\n\n// ─── Helpers ─────────────────────────────────────────────\n\nfunction formatTime(timestamp: string): string {\n return timestamp.slice(11, 19);\n}\n\nfunction formatUptime(startedAt: string): string {\n const ms = Date.now() - new Date(startedAt).getTime();\n const seconds = Math.floor(ms / 1000);\n const minutes = Math.floor(seconds / 60);\n const hours = Math.floor(minutes / 60);\n const days = Math.floor(hours / 24);\n if (days > 0) return `${days}d ${hours % 24}h`;\n if (hours > 0) return `${hours}h ${minutes % 60}m`;\n if (minutes > 0) return `${minutes}m ${seconds % 60}s`;\n return `${seconds}s`;\n}\n\nfunction formatTimeAgo(timestamp: string): string {\n const ms = Date.now() - new Date(timestamp).getTime();\n const seconds = Math.floor(ms / 1000);\n if (seconds < 60) return `${seconds}s ago`;\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) return `${minutes}m ago`;\n const hours = Math.floor(minutes / 60);\n return `${hours}h ago`;\n}\n\nfunction buildProgressBar(ratio: number, width: number): { filled: string; empty: string } {\n const clamped = Math.max(0, Math.min(1, ratio));\n const filledCount = Math.round(clamped * width);\n return {\n filled: BLOCK_FULL.repeat(filledCount),\n empty: BLOCK_EMPTY.repeat(width - filledCount),\n };\n}\n\nfunction buildSparkline(values: number[], width: number): string {\n if (values.length === 0) return \"▁\".repeat(width);\n const recent = values.slice(-width);\n const max = Math.max(...recent, 0.001);\n return recent\n .map((v) => {\n const idx = Math.min(\n Math.floor((v / max) * (SPARK_CHARS.length - 1)),\n SPARK_CHARS.length - 1,\n );\n return SPARK_CHARS[idx];\n })\n .join(\"\");\n}\n\nfunction extractCostHistory(entries: ActivityEntry[]): number[] {\n return entries\n .filter((e) => e.type === \"heartbeat\" && e.summary.includes(\"complete\"))\n .map((e) => {\n const detail = e.detail as Record<string, unknown> | undefined;\n return typeof detail?.costUsd === \"number\" ? detail.costUsd : 0;\n });\n}\n\n// ─── Animated Hooks ──────────────────────────────────────\n\nfunction useAnimationFrame(): number {\n const [frame, setFrame] = useState(0);\n useEffect(() => {\n const interval = setInterval(() => setFrame((f) => f + 1), ANIMATION_TICK_MS);\n return () => clearInterval(interval);\n }, []);\n return frame;\n}\n\nfunction useClock(): string {\n const [time, setTime] = useState(() => new Date().toLocaleTimeString());\n useEffect(() => {\n const interval = setInterval(() => setTime(new Date().toLocaleTimeString()), 1000);\n return () => clearInterval(interval);\n }, []);\n return time;\n}\n\n// ─── Components ──────────────────────────────────────────\n\nfunction Logo() {\n return (\n <Box paddingX={1} gap={1}>\n <Text color=\"#c084fc\" bold>\n ◆\n </Text>\n <Text bold>\n <Text color=\"#c084fc\">N</Text>\n <Text color=\"#a78bfa\">E</Text>\n <Text color=\"#818cf8\">O</Text>\n </Text>\n <Text dimColor>SUPERVISOR</Text>\n </Box>\n );\n}\n\nfunction LiveIndicator({ frame, isRunning }: { frame: number; isRunning: boolean }) {\n const frames = isRunning ? PULSE_FRAMES : IDLE_FRAMES;\n const dot = frames[frame % frames.length];\n return (\n <Box paddingX={1}>\n <Text color={isRunning ? \"#4ade80\" : \"#6b7280\"} bold>\n {dot}\n </Text>\n <Text color={isRunning ? \"#4ade80\" : \"#6b7280\"} bold>\n {\" \"}\n {isRunning ? \"LIVE\" : \"IDLE\"}\n </Text>\n </Box>\n );\n}\n\nfunction HeaderBar({\n state,\n name,\n frame,\n clock,\n}: {\n state: SupervisorDaemonState | null;\n name: string;\n frame: number;\n clock: string;\n}) {\n if (!state) {\n return (\n <Box borderStyle=\"round\" borderColor=\"#6b7280\" paddingX={1} flexDirection=\"column\">\n <Box justifyContent=\"space-between\">\n <Logo />\n <Box paddingX={1}>\n <Text dimColor>{clock}</Text>\n </Box>\n </Box>\n <Box paddingX={1}>\n <Text color=\"#fbbf24\">⟳ Connecting to \"{name}\"...</Text>\n </Box>\n </Box>\n );\n }\n\n const isRunning = state.status === \"running\";\n\n return (\n <Box\n borderStyle=\"round\"\n borderColor={isRunning ? \"#6ee7b7\" : \"#f87171\"}\n paddingX={0}\n flexDirection=\"column\"\n >\n <Box justifyContent=\"space-between\">\n <Logo />\n <Box gap={2}>\n <LiveIndicator frame={frame} isRunning={isRunning} />\n <Box paddingX={1}>\n <Text dimColor>{clock}</Text>\n </Box>\n </Box>\n </Box>\n\n <Box paddingX={1} gap={1}>\n <Text dimColor>│</Text>\n <Text>\n <Text dimColor>pid</Text> <Text bold>{state.pid}</Text>\n </Text>\n <Text dimColor>·</Text>\n <Text>\n <Text dimColor>port</Text> <Text bold>:{state.port}</Text>\n </Text>\n <Text dimColor>·</Text>\n <Text>\n <Text dimColor>beats</Text>{\" \"}\n <Text bold color=\"#6ee7b7\">\n ▲{state.heartbeatCount}\n </Text>\n </Text>\n {state.lastHeartbeat && (\n <>\n <Text dimColor>·</Text>\n <Text>\n <Text dimColor>last</Text> <Text>{formatTimeAgo(state.lastHeartbeat)}</Text>\n </Text>\n </>\n )}\n <Text dimColor>·</Text>\n <Text>\n <Text dimColor>up</Text> <Text>{formatUptime(state.startedAt)}</Text>\n </Text>\n </Box>\n </Box>\n );\n}\n\nfunction BudgetPanel({\n state,\n dailyCap,\n costHistory,\n}: {\n state: SupervisorDaemonState | null;\n dailyCap: number;\n costHistory: number[];\n}) {\n if (!state) return null;\n\n const todayCost = state.todayCostUsd ?? 0;\n const totalCost = state.totalCostUsd ?? 0;\n const ratio = dailyCap > 0 ? todayCost / dailyCap : 0;\n const barWidth = 20;\n const bar = buildProgressBar(ratio, barWidth);\n const pct = Math.round(ratio * 100);\n\n const barColor = pct < 50 ? \"#4ade80\" : pct < 80 ? \"#fbbf24\" : \"#f87171\";\n\n const sparkline = buildSparkline(costHistory, 12);\n\n return (\n <Box paddingX={2} gap={2}>\n <Box gap={1}>\n <Text dimColor>budget</Text>\n <Text color={barColor}>{bar.filled}</Text>\n <Text dimColor>{bar.empty}</Text>\n <Text bold color={barColor}>\n {pct}%\n </Text>\n <Text dimColor>\n (${todayCost.toFixed(2)}/${dailyCap})\n </Text>\n </Box>\n <Text dimColor>│</Text>\n <Box gap={1}>\n <Text dimColor>total</Text>\n <Text bold>${totalCost.toFixed(2)}</Text>\n </Box>\n <Text dimColor>│</Text>\n <Box gap={1}>\n <Text dimColor>cost/beat</Text>\n <Text color=\"#818cf8\">{sparkline}</Text>\n </Box>\n </Box>\n );\n}\n\nfunction ActivityRow({\n entry,\n isLatest,\n isOld,\n}: {\n entry: ActivityEntry;\n isLatest: boolean;\n isOld: boolean;\n}) {\n const icon = TYPE_ICONS[entry.type] ?? \"·\";\n const color = TYPE_COLORS[entry.type] ?? \"#9ca3af\";\n const label = (TYPE_LABELS[entry.type] ?? (entry.type as string).toUpperCase()).padEnd(7);\n\n return (\n <Box gap={1} paddingX={2}>\n <Text dimColor={isOld}>{isLatest ? \"│\" : \"│\"}</Text>\n <Text dimColor={isOld}>{formatTime(entry.timestamp)}</Text>\n <Text color={color} dimColor={isOld} bold={isLatest}>\n {icon}\n </Text>\n <Text color={color} dimColor={isOld} bold>\n {label}\n </Text>\n <Text dimColor={isOld} bold={isLatest}>\n {entry.summary}\n </Text>\n </Box>\n );\n}\n\nconst TASK_STATUS_COLORS: Record<string, string> = {\n in_progress: \"#60a5fa\",\n blocked: \"#f87171\",\n pending: \"#6b7280\",\n done: \"#4ade80\",\n};\n\nconst TASK_STATUS_LABELS: Record<string, string> = {\n in_progress: \"ACTIVE\",\n blocked: \"BLOCK\",\n pending: \"·\",\n};\n\nfunction TaskPanel({ tasks }: { tasks: MemoryEntry[] }) {\n const active = tasks.filter((t) => t.outcome !== \"done\" && t.outcome !== \"abandoned\");\n const doneCount = tasks.filter((t) => t.outcome === \"done\").length;\n\n if (tasks.length === 0) return null;\n\n const MAX_VISIBLE = 6;\n const visible = active.slice(0, MAX_VISIBLE);\n const overflow = active.length - visible.length;\n\n return (\n <Box flexDirection=\"column\">\n <Box paddingX={2} gap={1}>\n <Text dimColor>├</Text>\n <Text dimColor bold>\n TASKS\n </Text>\n <Text dimColor>\n ({active.length} active, {doneCount} done)\n </Text>\n <Text dimColor>{\"─\".repeat(30)}</Text>\n </Box>\n {visible.map((t) => {\n const status = t.outcome ?? \"pending\";\n const color = TASK_STATUS_COLORS[status] ?? \"#6b7280\";\n const label = (TASK_STATUS_LABELS[status] ?? \"·\").padEnd(6);\n const prio = t.severity ? `[${t.severity.slice(0, 3)}] ` : \"\";\n const repo = t.scope !== \"global\" ? path.basename(t.scope) : \"\";\n const run = t.runId ? `run:${t.runId.slice(0, 4)}` : \"\";\n const meta = [repo, run].filter(Boolean).join(\" \");\n\n return (\n <Box key={t.id} gap={1} paddingX={2}>\n <Text dimColor>│</Text>\n <Text color={color} bold>\n {label}\n </Text>\n {prio && <Text dimColor>{prio.padEnd(5)}</Text>}\n <Text wrap=\"truncate\">{t.content}</Text>\n {meta && <Text dimColor>({meta})</Text>}\n </Box>\n );\n })}\n {overflow > 0 && (\n <Box paddingX={2}>\n <Text dimColor>│ ... +{overflow} more pending</Text>\n </Box>\n )}\n </Box>\n );\n}\n\n/** Types shown in the activity feed — plan/thinking are internal, not shown */\nconst ACTIVITY_TYPES = new Set([\n \"heartbeat\",\n \"decision\",\n \"action\",\n \"dispatch\",\n \"error\",\n \"event\",\n \"message\",\n]);\n\nfunction ActivityPanel({ entries, termHeight }: { entries: ActivityEntry[]; termHeight: number }) {\n // Reserve lines for header (5), budget (1), separator (1), input (2), footer (1) = 10\n const maxVisible = Math.max(5, Math.min(MAX_VISIBLE_ENTRIES, termHeight - 10));\n const filtered = entries.filter((e) => ACTIVITY_TYPES.has(e.type));\n const visible = filtered.slice(-maxVisible);\n\n return (\n <Box flexDirection=\"column\">\n <Box paddingX={2} gap={1}>\n <Text dimColor>├</Text>\n <Text dimColor bold>\n ACTIVITY\n </Text>\n <Text dimColor>{\"─\".repeat(40)}</Text>\n </Box>\n\n {visible.length === 0 ? (\n <Box paddingX={2}>\n <Text dimColor>│ Waiting for heartbeats...</Text>\n </Box>\n ) : (\n visible.map((entry, idx) => (\n <ActivityRow\n key={entry.id}\n entry={entry}\n isLatest={idx === visible.length - 1}\n isOld={idx < visible.length - 5}\n />\n ))\n )}\n\n <Box paddingX={2}>\n <Text dimColor>│</Text>\n </Box>\n </Box>\n );\n}\n\nfunction InputPanel({\n value,\n onChange,\n onSubmit,\n lastSent,\n}: {\n value: string;\n onChange: (v: string) => void;\n onSubmit: (v: string) => void;\n lastSent: string;\n}) {\n return (\n <Box flexDirection=\"column\">\n <Box paddingX={2} gap={1}>\n <Text dimColor>└</Text>\n <Text bold color=\"#60a5fa\">\n ❯\n </Text>\n <TextInput\n value={value}\n onChange={onChange}\n onSubmit={onSubmit}\n placeholder=\"message the supervisor...\"\n />\n </Box>\n <Box paddingX={2} gap={1}>\n <Text dimColor> </Text>\n {lastSent ? <Text color=\"#6b7280\">✓ \"{lastSent}\"</Text> : null}\n </Box>\n </Box>\n );\n}\n\nfunction Footer() {\n return (\n <Box paddingX={2} gap={1} justifyContent=\"center\">\n <Text dimColor>\n <Text bold>esc</Text> quit\n </Text>\n <Text dimColor>·</Text>\n <Text dimColor>\n <Text bold>enter</Text> send\n </Text>\n <Text dimColor>·</Text>\n <Text dimColor>daemon keeps running</Text>\n </Box>\n );\n}\n\n// ─── Data Fetching ───────────────────────────────────────\n\nasync function readState(name: string): Promise<SupervisorDaemonState | null> {\n try {\n const raw = await readFile(getSupervisorStatePath(name), \"utf-8\");\n return JSON.parse(raw) as SupervisorDaemonState;\n } catch {\n return null;\n }\n}\n\nasync function readActivity(name: string, maxEntries: number): Promise<ActivityEntry[]> {\n try {\n const content = await readFile(getSupervisorActivityPath(name), \"utf-8\");\n const lines = content.trim().split(\"\\n\").filter(Boolean);\n const lastLines = lines.slice(-maxEntries);\n const entries: ActivityEntry[] = [];\n for (const line of lastLines) {\n try {\n entries.push(JSON.parse(line) as ActivityEntry);\n } catch {\n // Skip malformed\n }\n }\n return entries;\n } catch {\n return [];\n }\n}\n\nfunction readTasks(name: string): MemoryEntry[] {\n try {\n const dir = getSupervisorDir(name);\n const store = new MemoryStore(path.join(dir, \"memory.sqlite\"));\n const tasks = store.query({ types: [\"task\"], limit: 20, sortBy: \"createdAt\" });\n store.close();\n return tasks;\n } catch {\n return [];\n }\n}\n\nasync function sendMessage(name: string, text: string): Promise<void> {\n const id = randomUUID();\n const timestamp = new Date().toISOString();\n\n const message = { id, from: \"tui\" as const, text, timestamp };\n const inboxPath = getSupervisorInboxPath(name);\n await appendFile(inboxPath, `${JSON.stringify(message)}\\n`, \"utf-8\");\n\n // Write to activity.jsonl so the message appears in the TUI conversation\n const activityEntry = { id, type: \"message\", summary: text, timestamp };\n const activityPath = getSupervisorActivityPath(name);\n await appendFile(activityPath, `${JSON.stringify(activityEntry)}\\n`, \"utf-8\");\n}\n\n// ─── Main Component ──────────────────────────────────────\n\nexport function SupervisorTui({ name }: { name: string }) {\n const { exit } = useApp();\n const { stdout } = useStdout();\n const frame = useAnimationFrame();\n const clock = useClock();\n\n const [state, setState] = useState<SupervisorDaemonState | null>(null);\n const [entries, setEntries] = useState<ActivityEntry[]>([]);\n const [tasks, setTasks] = useState<MemoryEntry[]>([]);\n const [input, setInput] = useState(\"\");\n const [lastSent, setLastSent] = useState(\"\");\n const [termHeight, setTermHeight] = useState(stdout?.rows ?? 30);\n\n // Track terminal resize\n useEffect(() => {\n function onResize() {\n if (stdout) setTermHeight(stdout.rows);\n }\n stdout?.on(\"resize\", onResize);\n return () => {\n stdout?.off(\"resize\", onResize);\n };\n }, [stdout]);\n\n // Poll state and activity\n useEffect(() => {\n let active = true;\n\n async function poll() {\n if (!active) return;\n const [newState, newEntries] = await Promise.all([\n readState(name),\n readActivity(name, MAX_VISIBLE_ENTRIES),\n ]);\n if (!active) return;\n setState(newState);\n setEntries(newEntries);\n setTasks(readTasks(name));\n }\n\n poll();\n const interval = setInterval(poll, POLL_INTERVAL_MS);\n return () => {\n active = false;\n clearInterval(interval);\n };\n }, [name]);\n\n useInput((_input, key) => {\n if (key.escape) {\n exit();\n }\n });\n\n const handleSubmit = useCallback(\n (text: string) => {\n if (!text.trim()) return;\n sendMessage(name, text.trim());\n setLastSent(text.trim());\n setInput(\"\");\n },\n [name],\n );\n\n const costHistory = extractCostHistory(entries);\n\n return (\n <Box flexDirection=\"column\">\n <HeaderBar state={state} name={name} frame={frame} clock={clock} />\n <BudgetPanel state={state} dailyCap={50} costHistory={costHistory} />\n <TaskPanel tasks={tasks} />\n <ActivityPanel\n entries={entries}\n termHeight={\n termHeight -\n (tasks.length > 0\n ? Math.min(\n tasks.filter((t) => t.outcome !== \"done\" && t.outcome !== \"abandoned\").length,\n 6,\n ) + 2\n : 0)\n }\n />\n <InputPanel value={input} onChange={setInput} onSubmit={handleSubmit} lastSent={lastSent} />\n <Footer />\n </Box>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// src/commands/version.ts
|
|
2
|
+
import { readFile } from "fs/promises";
|
|
3
|
+
import { dirname, join } from "path";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
import { defineCommand } from "citty";
|
|
6
|
+
async function getVersion() {
|
|
7
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const pkgPath = join(__dirname, "..", "..", "package.json");
|
|
9
|
+
const content = await readFile(pkgPath, "utf-8");
|
|
10
|
+
const pkg = JSON.parse(content);
|
|
11
|
+
return pkg.version;
|
|
12
|
+
}
|
|
13
|
+
var version_default = defineCommand({
|
|
14
|
+
meta: {
|
|
15
|
+
name: "version",
|
|
16
|
+
description: "Display the current neo version"
|
|
17
|
+
},
|
|
18
|
+
async run() {
|
|
19
|
+
const version = await getVersion();
|
|
20
|
+
console.log(`neo v${version}`);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
export {
|
|
24
|
+
version_default as default
|
|
25
|
+
};
|
|
26
|
+
//# sourceMappingURL=version-XVOAMGDD.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/version.ts"],"sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { defineCommand } from \"citty\";\n\nasync function getVersion(): Promise<string> {\n const __dirname = dirname(fileURLToPath(import.meta.url));\n const pkgPath = join(__dirname, \"..\", \"..\", \"package.json\");\n const content = await readFile(pkgPath, \"utf-8\");\n const pkg = JSON.parse(content) as { version: string };\n return pkg.version;\n}\n\nexport default defineCommand({\n meta: {\n name: \"version\",\n description: \"Display the current neo version\",\n },\n async run() {\n const version = await getVersion();\n console.log(`neo v${version}`);\n },\n});\n"],"mappings":";AAAA,SAAS,gBAAgB;AACzB,SAAS,SAAS,YAAY;AAC9B,SAAS,qBAAqB;AAC9B,SAAS,qBAAqB;AAE9B,eAAe,aAA8B;AAC3C,QAAM,YAAY,QAAQ,cAAc,YAAY,GAAG,CAAC;AACxD,QAAM,UAAU,KAAK,WAAW,MAAM,MAAM,cAAc;AAC1D,QAAM,UAAU,MAAM,SAAS,SAAS,OAAO;AAC/C,QAAM,MAAM,KAAK,MAAM,OAAO;AAC9B,SAAO,IAAI;AACb;AAEA,IAAO,kBAAQ,cAAc;AAAA,EAC3B,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,aAAa;AAAA,EACf;AAAA,EACA,MAAM,MAAM;AACV,UAAM,UAAU,MAAM,WAAW;AACjC,YAAQ,IAAI,QAAQ,OAAO,EAAE;AAAA,EAC/B;AACF,CAAC;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neotx/cli",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.4",
|
|
4
4
|
"description": "CLI for the Neo orchestration framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -29,25 +29,43 @@
|
|
|
29
29
|
"engines": {
|
|
30
30
|
"node": ">=22"
|
|
31
31
|
},
|
|
32
|
+
"man": [
|
|
33
|
+
"../../man/man1/neo.1",
|
|
34
|
+
"../../man/man1/neo-agents.1",
|
|
35
|
+
"../../man/man1/neo-cost.1",
|
|
36
|
+
"../../man/man1/neo-doctor.1",
|
|
37
|
+
"../../man/man1/neo-init.1",
|
|
38
|
+
"../../man/man1/neo-log.1",
|
|
39
|
+
"../../man/man1/neo-logs.1",
|
|
40
|
+
"../../man/man1/neo-mcp.1",
|
|
41
|
+
"../../man/man1/neo-memory.1",
|
|
42
|
+
"../../man/man1/neo-repos.1",
|
|
43
|
+
"../../man/man1/neo-run.1",
|
|
44
|
+
"../../man/man1/neo-runs.1",
|
|
45
|
+
"../../man/man1/neo-supervise.1",
|
|
46
|
+
"../../man/man1/neo-version.1"
|
|
47
|
+
],
|
|
32
48
|
"dependencies": {
|
|
33
49
|
"citty": "^0.2.1",
|
|
34
50
|
"ink": "^6.8.0",
|
|
35
51
|
"ink-text-input": "^6.0.0",
|
|
36
52
|
"react": "^19.2.4",
|
|
37
53
|
"yaml": "^2.8.2",
|
|
38
|
-
"@neotx/agents": "0.1.0-alpha.
|
|
39
|
-
"@neotx/core": "0.1.0-alpha.
|
|
54
|
+
"@neotx/agents": "0.1.0-alpha.4",
|
|
55
|
+
"@neotx/core": "0.1.0-alpha.4"
|
|
40
56
|
},
|
|
41
57
|
"devDependencies": {
|
|
42
58
|
"@anthropic-ai/claude-agent-sdk": "^0.1.0",
|
|
43
59
|
"@types/node": "^25.5.0",
|
|
44
60
|
"@types/react": "^19.2.14",
|
|
61
|
+
"@vitest/coverage-v8": "^3.0.0",
|
|
45
62
|
"tsup": "^8.5.1"
|
|
46
63
|
},
|
|
47
64
|
"scripts": {
|
|
48
65
|
"build": "tsup",
|
|
49
66
|
"typecheck": "tsc --noEmit",
|
|
50
67
|
"lint": "biome check src/",
|
|
51
|
-
"test": "vitest run"
|
|
68
|
+
"test": "vitest run",
|
|
69
|
+
"install-man": "mkdir -p /usr/local/share/man/man1 && cp ../../man/man1/*.1 /usr/local/share/man/man1/ && mandb 2>/dev/null || true"
|
|
52
70
|
}
|
|
53
71
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/repo-filter.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { readdir, readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { PersistedRun } from \"@neotx/core\";\nimport { getRunsDir, listReposFromGlobalConfig, toRepoSlug } from \"@neotx/core\";\n\nexport interface RepoFilter {\n mode: \"cwd\" | \"all\" | \"named\";\n repoSlug?: string;\n repoPath?: string;\n}\n\n/**\n * Resolve which repos to query based on --all / --repo flags.\n * Default: CWD-based (finds the matching registered repo slug, or uses basename).\n */\nexport async function resolveRepoFilter(args: {\n all?: boolean | undefined;\n repo?: string | undefined;\n}): Promise<RepoFilter> {\n if (args.all) return { mode: \"all\" };\n\n if (args.repo) {\n const repo = args.repo;\n // Could be a name/slug or a path\n const repos = await listReposFromGlobalConfig();\n const match = repos.find(\n (r) => toRepoSlug(r) === repo || path.resolve(r.path) === path.resolve(repo),\n );\n if (match) {\n return { mode: \"named\", repoSlug: toRepoSlug(match), repoPath: match.path };\n }\n // Treat as path, derive slug\n return { mode: \"named\", repoSlug: toRepoSlug({ path: repo }), repoPath: repo };\n }\n\n // Default: CWD\n const cwd = process.cwd();\n const repos = await listReposFromGlobalConfig();\n const match = repos.find((r) => path.resolve(r.path) === cwd);\n const slug = match ? toRepoSlug(match) : toRepoSlug({ path: cwd });\n return { mode: \"cwd\", repoSlug: slug, repoPath: cwd };\n}\n\n/**\n * Load persisted runs, filtered by RepoFilter.\n */\nexport async function loadRunsFiltered(filter: RepoFilter): Promise<PersistedRun[]> {\n const runsDir = getRunsDir();\n if (!existsSync(runsDir)) return [];\n\n const runs: PersistedRun[] = [];\n\n if (filter.mode === \"all\") {\n // Scan all slug subdirs + legacy flat files\n const entries = await readdir(runsDir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory()) {\n await loadRunsFromDir(path.join(runsDir, entry.name), runs);\n } else if (entry.name.endsWith(\".json\")) {\n await loadRunFile(path.join(runsDir, entry.name), runs);\n }\n }\n } else {\n // Specific slug dir\n const slugDir = path.join(runsDir, filter.repoSlug ?? \"unknown\");\n await loadRunsFromDir(slugDir, runs);\n // Also check legacy flat files matching this repo\n await loadLegacyRuns(runsDir, filter.repoPath, runs);\n }\n\n runs.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));\n return runs;\n}\n\nasync function loadRunsFromDir(dir: string, runs: PersistedRun[]): Promise<void> {\n if (!existsSync(dir)) return;\n const files = await readdir(dir);\n for (const file of files) {\n if (!file.endsWith(\".json\")) continue;\n await loadRunFile(path.join(dir, file), runs);\n }\n}\n\nasync function loadRunFile(filePath: string, runs: PersistedRun[]): Promise<void> {\n try {\n const content = await readFile(filePath, \"utf-8\");\n runs.push(JSON.parse(content) as PersistedRun);\n } catch {\n // Skip corrupt files\n }\n}\n\nasync function loadLegacyRuns(\n runsDir: string,\n repoPath: string | undefined,\n runs: PersistedRun[],\n): Promise<void> {\n if (!repoPath) return;\n const resolvedRepo = path.resolve(repoPath);\n\n try {\n const entries = await readdir(runsDir, { withFileTypes: true });\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith(\".json\")) continue;\n const filePath = path.join(runsDir, entry.name);\n const content = await readFile(filePath, \"utf-8\");\n const run = JSON.parse(content) as PersistedRun;\n if (path.resolve(run.repo) === resolvedRepo) {\n // Avoid duplicates\n if (!runs.some((r) => r.runId === run.runId)) {\n runs.push(run);\n }\n }\n }\n } catch {\n // Non-critical\n }\n}\n"],"mappings":"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","names":["repos","match"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/cost.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { readdir, readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { CostEntry } from \"@neotx/core\";\nimport { getJournalsDir, toRepoSlug } from \"@neotx/core\";\nimport { defineCommand } from \"citty\";\nimport { printError, printJson, printTable } from \"../output.js\";\nimport { resolveRepoFilter } from \"../repo-filter.js\";\n\nasync function readCostEntries(journalDir: string): Promise<CostEntry[]> {\n if (!existsSync(journalDir)) return [];\n const files = await readdir(journalDir);\n const costFiles = files\n .filter((f) => f.startsWith(\"cost-\"))\n .sort()\n .reverse();\n const entries: CostEntry[] = [];\n\n for (const file of costFiles) {\n const content = await readFile(path.join(journalDir, file), \"utf-8\");\n for (const line of content.trim().split(\"\\n\")) {\n if (!line.trim()) continue;\n entries.push(JSON.parse(line) as CostEntry);\n }\n }\n\n return entries;\n}\n\nfunction isToday(timestamp: string): boolean {\n const d = new Date(timestamp);\n const now = new Date();\n return (\n d.getUTCFullYear() === now.getUTCFullYear() &&\n d.getUTCMonth() === now.getUTCMonth() &&\n d.getUTCDate() === now.getUTCDate()\n );\n}\n\nexport default defineCommand({\n meta: {\n name: \"cost\",\n description: \"Show cost breakdown from journals (today, by agent, by run)\",\n },\n args: {\n all: {\n type: \"boolean\",\n description: \"Show costs from all repos\",\n default: false,\n },\n repo: {\n type: \"string\",\n description: \"Filter by repo name or path\",\n },\n short: {\n type: \"boolean\",\n description: \"Compact output for supervisor agents (saves tokens)\",\n default: false,\n },\n output: {\n type: \"string\",\n description: \"Output format: json\",\n },\n },\n async run({ args }) {\n const jsonOutput = args.output === \"json\";\n const journalDir = getJournalsDir();\n let entries = await readCostEntries(journalDir);\n\n if (entries.length === 0) {\n printError(\"No cost data found.\");\n process.exitCode = 1;\n return;\n }\n\n // Filter by repo unless --all\n const filter = await resolveRepoFilter({ all: args.all, repo: args.repo });\n if (filter.mode !== \"all\") {\n const slug = filter.repoSlug;\n entries = entries.filter((e) => {\n if (!e.repo) return false;\n return toRepoSlug({ path: e.repo }) === slug;\n });\n }\n\n const todayEntries = entries.filter((e) => isToday(e.timestamp));\n const todayTotal = todayEntries.reduce((sum, e) => sum + e.costUsd, 0);\n const allTimeTotal = entries.reduce((sum, e) => sum + e.costUsd, 0);\n\n // Breakdown by agent (today)\n const byAgent = new Map<string, { cost: number; runs: number }>();\n for (const e of todayEntries) {\n const prev = byAgent.get(e.agent) ?? { cost: 0, runs: 0 };\n byAgent.set(e.agent, { cost: prev.cost + e.costUsd, runs: prev.runs + 1 });\n }\n\n // Breakdown by repo (today, only in --all mode)\n const byRepo = new Map<string, { cost: number; runs: number }>();\n if (filter.mode === \"all\") {\n for (const e of todayEntries) {\n const repo = e.repo ?? \"unknown\";\n const prev = byRepo.get(repo) ?? { cost: 0, runs: 0 };\n byRepo.set(repo, { cost: prev.cost + e.costUsd, runs: prev.runs + 1 });\n }\n }\n\n if (jsonOutput) {\n printJson({\n today: {\n total: todayTotal,\n sessions: todayEntries.length,\n byAgent: Object.fromEntries(byAgent),\n ...(byRepo.size > 0 ? { byRepo: Object.fromEntries(byRepo) } : {}),\n },\n allTime: {\n total: allTimeTotal,\n sessions: entries.length,\n },\n });\n return;\n }\n\n if (args.short) {\n const agents = [...byAgent.entries()]\n .map(([name, data]) => `${name}=$${data.cost.toFixed(4)}`)\n .join(\" \");\n console.log(`today=$${todayTotal.toFixed(4)} sessions=${todayEntries.length} ${agents}`);\n return;\n }\n\n console.log(`Today: $${todayTotal.toFixed(4)} (${todayEntries.length} sessions)`);\n console.log(`All time: $${allTimeTotal.toFixed(4)} (${entries.length} sessions)`);\n\n if (byAgent.size > 0) {\n console.log(\"\");\n printTable(\n [\"AGENT\", \"COST TODAY\", \"SESSIONS\"],\n [...byAgent.entries()]\n .sort((a, b) => b[1].cost - a[1].cost)\n .map(([name, data]) => [name, `$${data.cost.toFixed(4)}`, String(data.runs)]),\n );\n }\n\n if (byRepo.size > 0) {\n console.log(\"\");\n printTable(\n [\"REPO\", \"COST TODAY\", \"SESSIONS\"],\n [...byRepo.entries()]\n .sort((a, b) => b[1].cost - a[1].cost)\n .map(([repo, data]) => [repo, `$${data.cost.toFixed(4)}`, String(data.runs)]),\n );\n }\n },\n});\n"],"mappings":"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","names":[]}
|