tui-devtools 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,98 @@
1
+ # tui-devtools
2
+
3
+ DevTools for Ink TUI apps — component tree, state inspection, console capture.
4
+
5
+ Built for AI agents that need to debug terminal UI applications programmatically.
6
+
7
+ ## Why
8
+
9
+ Tools like `agent-tui` can automate TUI apps (press keys, take screenshots), but they can only see the **screen output**. When something goes wrong, there's no way to inspect component state, view console errors, or understand the component hierarchy.
10
+
11
+ `tui-devtools` bridges this gap for **Ink (React)** apps by connecting to React DevTools protocol.
12
+
13
+ ## Quick Start
14
+
15
+ ```bash
16
+ # Install
17
+ npm install -g tui-devtools
18
+
19
+ # 1. Start the DevTools server
20
+ tui-devtools start
21
+
22
+ # 2. Run your Ink app with DEV=true
23
+ DEV=true npx my-ink-app
24
+
25
+ # 3. Inspect
26
+ tui-devtools tree # Component hierarchy
27
+ tui-devtools inspect CommandMode # Props & state of a component
28
+ tui-devtools logs # Console output
29
+ tui-devtools logs --level error # Only errors
30
+
31
+ # 4. Cleanup
32
+ tui-devtools stop
33
+ ```
34
+
35
+ ## Commands
36
+
37
+ | Command | Description |
38
+ |---------|-------------|
39
+ | `start` | Start DevTools server daemon |
40
+ | `stop` | Stop daemon |
41
+ | `status` | Show connection status |
42
+ | `tree` | Print component tree |
43
+ | `inspect <name>` | Show props/state/hooks for a component |
44
+ | `find <name>` | Find components by name |
45
+ | `logs` | Show captured console logs |
46
+
47
+ ## With agent-tui (AI Agent Workflow)
48
+
49
+ ```bash
50
+ # Start devtools first
51
+ tui-devtools -s myapp start
52
+
53
+ # Start the TUI app
54
+ agent-tui run -s myapp "DEV=true npx my-ink-app"
55
+
56
+ # Visual: what's on screen
57
+ agent-tui -s myapp screenshot
58
+
59
+ # Structural: what's in React
60
+ tui-devtools -s myapp tree
61
+
62
+ # Debug: what went wrong
63
+ tui-devtools -s myapp logs --level error
64
+ tui-devtools -s myapp inspect MyComponent --json
65
+ ```
66
+
67
+ ## How It Works
68
+
69
+ ```
70
+ Ink App (DEV=true) tui-devtools daemon
71
+ ───────────────── ──────────────────
72
+ react-devtools-core WebSocket server (:8097)
73
+ connectToDevTools() ──WebSocket──► Parse fiber tree operations
74
+ Store component tree + logs
75
+
76
+ Unix socket IPC
77
+
78
+ CLI commands ◄── AI agent
79
+ ```
80
+
81
+ ## Options
82
+
83
+ ```
84
+ -s, --session <name> Session name (default: "default")
85
+ -p, --port <port> DevTools port (default: 8097)
86
+ --json JSON output for all commands
87
+ ```
88
+
89
+ ## Requirements
90
+
91
+ - Node.js >= 18
92
+ - Target app must use **Ink** (React-based TUI framework)
93
+ - `react-devtools-core` must be installed in the target app
94
+ - Run the app with `DEV=true` environment variable
95
+
96
+ ## License
97
+
98
+ MIT
@@ -0,0 +1,409 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ getLogPath,
4
+ getPidPath,
5
+ getSocketPath,
6
+ isDaemonRunning,
7
+ sendIpcRequest
8
+ } from "../chunk-UV6LYAWB.js";
9
+
10
+ // bin/tui-devtools.ts
11
+ import { program } from "commander";
12
+ import { fork } from "child_process";
13
+ import { fileURLToPath } from "url";
14
+ import path from "path";
15
+ import fs from "fs";
16
+ var __filename = fileURLToPath(import.meta.url);
17
+ var __dirname = path.dirname(__filename);
18
+ var DEFAULT_PORT = 8097;
19
+ var DEFAULT_SESSION = "default";
20
+ program.name("tui-devtools").description("DevTools for Ink TUI apps \u2014 component tree, state inspection, console capture").version("0.1.0").option("-s, --session <name>", "Session name for daemon isolation", DEFAULT_SESSION).option("-p, --port <port>", "DevTools server port", String(DEFAULT_PORT)).option("--json", "JSON output");
21
+ program.command("start").description("Start the DevTools server daemon").action(async () => {
22
+ const opts = program.opts();
23
+ const session = opts.session;
24
+ const port = parseInt(opts.port, 10);
25
+ if (isDaemonRunning(session)) {
26
+ console.log(`Daemon already running (session: ${session})`);
27
+ return;
28
+ }
29
+ const child = fork(__filename, ["__daemon__", session, String(port)], {
30
+ detached: true,
31
+ stdio: "ignore"
32
+ });
33
+ child.unref();
34
+ const socketPath = getSocketPath(session);
35
+ for (let i = 0; i < 20; i++) {
36
+ await new Promise((r) => setTimeout(r, 200));
37
+ if (fs.existsSync(socketPath)) {
38
+ console.log(`DevTools server started (session: ${session}, port: ${port})`);
39
+ console.log(` Logs: ${getLogPath(session)}`);
40
+ console.log(`
41
+ Start your Ink app with: DEV=true your-app`);
42
+ return;
43
+ }
44
+ }
45
+ console.error("Failed to start daemon \u2014 check logs:", getLogPath(session));
46
+ process.exit(1);
47
+ });
48
+ program.command("stop").description("Stop the daemon").action(async () => {
49
+ const session = program.opts().session;
50
+ const pidPath = getPidPath(session);
51
+ try {
52
+ const pid = parseInt(fs.readFileSync(pidPath, "utf-8").trim(), 10);
53
+ process.kill(pid, "SIGTERM");
54
+ console.log(`Daemon stopped (session: ${session})`);
55
+ } catch {
56
+ console.log(`No daemon running (session: ${session})`);
57
+ }
58
+ });
59
+ program.command("status").description("Show connection status").action(async () => {
60
+ const opts = program.opts();
61
+ const session = opts.session;
62
+ const json = opts.json;
63
+ if (!isDaemonRunning(session)) {
64
+ if (json) {
65
+ console.log(JSON.stringify({ running: false }));
66
+ } else {
67
+ console.log(`Daemon not running (session: ${session})`);
68
+ }
69
+ return;
70
+ }
71
+ try {
72
+ const res = await sendIpcRequest(session, { command: "status" });
73
+ if (json) {
74
+ console.log(JSON.stringify(res.data));
75
+ } else {
76
+ const d = res.data;
77
+ console.log(`Session: ${session}`);
78
+ console.log(`Connected: ${d.connected ? "yes" : "no"}`);
79
+ console.log(`Components: ${d.nodeCount}`);
80
+ console.log(`Roots: ${d.rootCount}`);
81
+ console.log(`Logs: ${d.logCount}`);
82
+ }
83
+ } catch (e) {
84
+ console.error(`Error: ${e.message}`);
85
+ }
86
+ });
87
+ program.command("tree").description("Print component tree").option("-d, --depth <n>", "Max tree depth", "20").action(async (cmdOpts) => {
88
+ const opts = program.opts();
89
+ const session = opts.session;
90
+ const json = opts.json;
91
+ const depth = parseInt(cmdOpts.depth, 10);
92
+ if (!isDaemonRunning(session)) {
93
+ console.error("Daemon not running. Start with: tui-devtools start");
94
+ process.exit(1);
95
+ }
96
+ try {
97
+ const res = await sendIpcRequest(session, {
98
+ command: "tree",
99
+ args: { depth, json }
100
+ });
101
+ if (res.ok) {
102
+ if (json) {
103
+ console.log(JSON.stringify(res.data, null, 2));
104
+ } else {
105
+ console.log(res.data || "(empty tree \u2014 is the app connected?)");
106
+ }
107
+ } else {
108
+ console.error(`Error: ${res.error}`);
109
+ }
110
+ } catch (e) {
111
+ console.error(`Error: ${e.message}`);
112
+ }
113
+ });
114
+ program.command("inspect [component]").description("Inspect a component's props, state, and hooks").option("--id <id>", "Inspect by fiber ID").action(async (component, cmdOpts) => {
115
+ const opts = program.opts();
116
+ const session = opts.session;
117
+ const json = opts.json;
118
+ const id = cmdOpts.id ? parseInt(cmdOpts.id, 10) : void 0;
119
+ if (!component && id == null) {
120
+ console.error("Usage: tui-devtools inspect <component-name> or --id <id>");
121
+ process.exit(1);
122
+ }
123
+ if (!isDaemonRunning(session)) {
124
+ console.error("Daemon not running. Start with: tui-devtools start");
125
+ process.exit(1);
126
+ }
127
+ try {
128
+ const res = await sendIpcRequest(session, {
129
+ command: "inspect",
130
+ args: { name: component, id }
131
+ });
132
+ if (res.ok) {
133
+ if (json) {
134
+ console.log(JSON.stringify(res.data, null, 2));
135
+ } else {
136
+ const d = res.data;
137
+ console.log(`Component: ${d.displayName} [${d.id}]`);
138
+ console.log(`Type: ${d.type}`);
139
+ if (d.key) console.log(`Key: ${d.key}`);
140
+ console.log(`Parent: ${d.parentId}`);
141
+ console.log(`Children: ${d.childIds?.length ?? 0}`);
142
+ if (d.props) {
143
+ console.log(`
144
+ Props:`);
145
+ console.log(JSON.stringify(d.props, null, 2));
146
+ }
147
+ if (d.state) {
148
+ console.log(`
149
+ State:`);
150
+ console.log(JSON.stringify(d.state, null, 2));
151
+ }
152
+ if (d.inspectData) {
153
+ console.log(`
154
+ Inspect Data:`);
155
+ console.log(JSON.stringify(d.inspectData, null, 2));
156
+ }
157
+ }
158
+ } else {
159
+ console.error(`Error: ${res.error}`);
160
+ }
161
+ } catch (e) {
162
+ console.error(`Error: ${e.message}`);
163
+ }
164
+ });
165
+ program.command("find <name>").description("Find components by name").action(async (name) => {
166
+ const opts = program.opts();
167
+ const session = opts.session;
168
+ const json = opts.json;
169
+ if (!isDaemonRunning(session)) {
170
+ console.error("Daemon not running. Start with: tui-devtools start");
171
+ process.exit(1);
172
+ }
173
+ try {
174
+ const res = await sendIpcRequest(session, {
175
+ command: "find",
176
+ args: { name }
177
+ });
178
+ if (res.ok) {
179
+ const nodes = res.data;
180
+ if (json) {
181
+ console.log(JSON.stringify(nodes, null, 2));
182
+ } else if (nodes.length === 0) {
183
+ console.log(`No components matching "${name}"`);
184
+ } else {
185
+ for (const n of nodes) {
186
+ console.log(` [${n.id}] ${n.displayName} (${n.type})${n.key ? ` key="${n.key}"` : ""}`);
187
+ }
188
+ }
189
+ } else {
190
+ console.error(`Error: ${res.error}`);
191
+ }
192
+ } catch (e) {
193
+ console.error(`Error: ${e.message}`);
194
+ }
195
+ });
196
+ program.command("logs").description("Show captured console logs").option("-l, --level <level>", "Filter by level (log|warn|error|info|debug)").option("-t, --tail <n>", "Show last N entries", "50").action(async (cmdOpts) => {
197
+ const opts = program.opts();
198
+ const session = opts.session;
199
+ const json = opts.json;
200
+ if (!isDaemonRunning(session)) {
201
+ console.error("Daemon not running. Start with: tui-devtools start");
202
+ process.exit(1);
203
+ }
204
+ try {
205
+ const res = await sendIpcRequest(session, {
206
+ command: "logs",
207
+ args: {
208
+ level: cmdOpts.level,
209
+ tail: parseInt(cmdOpts.tail, 10)
210
+ }
211
+ });
212
+ if (res.ok) {
213
+ const logs = res.data;
214
+ if (json) {
215
+ console.log(JSON.stringify(logs, null, 2));
216
+ } else if (logs.length === 0) {
217
+ console.log("(no logs captured)");
218
+ } else {
219
+ for (const entry of logs) {
220
+ const ts = new Date(entry.timestamp).toISOString().slice(11, 19);
221
+ const level = entry.level.toUpperCase().padEnd(5);
222
+ const msg = entry.args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
223
+ console.log(`${ts} [${level}] ${msg}`);
224
+ }
225
+ }
226
+ } else {
227
+ console.error(`Error: ${res.error}`);
228
+ }
229
+ } catch (e) {
230
+ console.error(`Error: ${e.message}`);
231
+ }
232
+ });
233
+ function ensureDaemon(session) {
234
+ if (!isDaemonRunning(session)) {
235
+ console.error("Daemon not running. Start with: tui-devtools start");
236
+ process.exit(1);
237
+ }
238
+ }
239
+ program.command("run <command...>").description("Run a TUI application in a virtual terminal").option("-d, --cwd <dir>", "Working directory").option("--cols <n>", "Terminal columns", "120").option("--rows <n>", "Terminal rows", "40").option("--sid <id>", "PTY session ID", "default").action(async (commandParts, cmdOpts) => {
240
+ const opts = program.opts();
241
+ const session = opts.session;
242
+ const json = opts.json;
243
+ const command = commandParts.join(" ");
244
+ ensureDaemon(session);
245
+ try {
246
+ const res = await sendIpcRequest(session, {
247
+ command: "run",
248
+ args: {
249
+ command,
250
+ sessionId: cmdOpts.sid,
251
+ cwd: cmdOpts.cwd,
252
+ cols: parseInt(cmdOpts.cols, 10),
253
+ rows: parseInt(cmdOpts.rows, 10)
254
+ }
255
+ });
256
+ if (res.ok) {
257
+ const info = res.data;
258
+ if (json) {
259
+ console.log(JSON.stringify(info));
260
+ } else {
261
+ console.log(`Session started: ${info.id}`);
262
+ console.log(` PID: ${info.pid}`);
263
+ }
264
+ } else {
265
+ console.error(`Error: ${res.error}`);
266
+ process.exit(1);
267
+ }
268
+ } catch (e) {
269
+ console.error(`Error: ${e.message}`);
270
+ process.exit(1);
271
+ }
272
+ });
273
+ program.command("screenshot").description("Capture current terminal screen").option("--sid <id>", "PTY session ID", "default").option("--strip-ansi", "Strip ANSI color codes").action(async (cmdOpts) => {
274
+ const opts = program.opts();
275
+ const session = opts.session;
276
+ const json = opts.json;
277
+ ensureDaemon(session);
278
+ try {
279
+ const res = await sendIpcRequest(session, {
280
+ command: "screenshot",
281
+ args: { sessionId: cmdOpts.sid, stripAnsi: cmdOpts.stripAnsi }
282
+ });
283
+ if (res.ok) {
284
+ const data = res.data;
285
+ if (json) {
286
+ console.log(JSON.stringify(data));
287
+ } else {
288
+ console.log(`Screenshot:${data.running ? "" : " [stopped]"}`);
289
+ console.log(data.screenshot);
290
+ }
291
+ } else {
292
+ console.error(`Error: ${res.error}`);
293
+ }
294
+ } catch (e) {
295
+ console.error(`Error: ${e.message}`);
296
+ }
297
+ });
298
+ program.command("press <keys...>").description("Send key press(es) to the terminal").option("--sid <id>", "PTY session ID", "default").action(async (keys, cmdOpts) => {
299
+ const opts = program.opts();
300
+ const session = opts.session;
301
+ ensureDaemon(session);
302
+ try {
303
+ const res = await sendIpcRequest(session, {
304
+ command: "press",
305
+ args: { sessionId: cmdOpts.sid, keys }
306
+ });
307
+ if (res.ok) {
308
+ console.log("\u2713 Key pressed");
309
+ } else {
310
+ console.error(`Error: ${res.error}`);
311
+ }
312
+ } catch (e) {
313
+ console.error(`Error: ${e.message}`);
314
+ }
315
+ });
316
+ program.command("type <text>").description("Type text into the terminal").option("--sid <id>", "PTY session ID", "default").action(async (text, cmdOpts) => {
317
+ const opts = program.opts();
318
+ const session = opts.session;
319
+ ensureDaemon(session);
320
+ try {
321
+ const res = await sendIpcRequest(session, {
322
+ command: "type",
323
+ args: { sessionId: cmdOpts.sid, text }
324
+ });
325
+ if (res.ok) {
326
+ console.log("\u2713 Text typed");
327
+ } else {
328
+ console.error(`Error: ${res.error}`);
329
+ }
330
+ } catch (e) {
331
+ console.error(`Error: ${e.message}`);
332
+ }
333
+ });
334
+ program.command("wait <text>").description("Wait for text to appear on screen").option("--sid <id>", "PTY session ID", "default").option("--timeout <ms>", "Timeout in milliseconds", "30000").action(async (text, cmdOpts) => {
335
+ const opts = program.opts();
336
+ const session = opts.session;
337
+ const json = opts.json;
338
+ ensureDaemon(session);
339
+ try {
340
+ const res = await sendIpcRequest(session, {
341
+ command: "wait",
342
+ args: { sessionId: cmdOpts.sid, text, timeout: parseInt(cmdOpts.timeout, 10) }
343
+ });
344
+ if (res.ok) {
345
+ const data = res.data;
346
+ if (json) {
347
+ console.log(JSON.stringify(data));
348
+ } else {
349
+ console.log(data.found ? `\u2713 Found: "${text}"` : `\u2717 Timeout waiting for: "${text}"`);
350
+ }
351
+ } else {
352
+ console.error(`Error: ${res.error}`);
353
+ }
354
+ } catch (e) {
355
+ console.error(`Error: ${e.message}`);
356
+ }
357
+ });
358
+ program.command("kill-session").description("Kill a PTY session").option("--sid <id>", "PTY session ID", "default").action(async (cmdOpts) => {
359
+ const opts = program.opts();
360
+ const session = opts.session;
361
+ ensureDaemon(session);
362
+ try {
363
+ const res = await sendIpcRequest(session, {
364
+ command: "kill-session",
365
+ args: { sessionId: cmdOpts.sid }
366
+ });
367
+ if (res.ok) {
368
+ console.log(`Session killed: ${cmdOpts.sid}`);
369
+ } else {
370
+ console.error(`Error: ${res.error}`);
371
+ }
372
+ } catch (e) {
373
+ console.error(`Error: ${e.message}`);
374
+ }
375
+ });
376
+ program.command("sessions").description("List PTY sessions").action(async () => {
377
+ const opts = program.opts();
378
+ const session = opts.session;
379
+ const json = opts.json;
380
+ ensureDaemon(session);
381
+ try {
382
+ const res = await sendIpcRequest(session, { command: "sessions" });
383
+ if (res.ok) {
384
+ const sessions = res.data;
385
+ if (json) {
386
+ console.log(JSON.stringify(sessions, null, 2));
387
+ } else if (sessions.length === 0) {
388
+ console.log("No active PTY sessions");
389
+ } else {
390
+ for (const s of sessions) {
391
+ console.log(` ${s.id} \u2014 ${s.command} [${s.running ? "running" : "stopped"}] ${s.cols}x${s.rows} pid:${s.pid}`);
392
+ }
393
+ }
394
+ } else {
395
+ console.error(`Error: ${res.error}`);
396
+ }
397
+ } catch (e) {
398
+ console.error(`Error: ${e.message}`);
399
+ }
400
+ });
401
+ if (process.argv[2] === "__daemon__") {
402
+ const session = process.argv[3] ?? DEFAULT_SESSION;
403
+ const port = parseInt(process.argv[4] ?? String(DEFAULT_PORT), 10);
404
+ const { startDaemon } = await import("../daemon-27VEN2X5.js");
405
+ await startDaemon(session, port);
406
+ } else {
407
+ program.parse();
408
+ }
409
+ //# sourceMappingURL=tui-devtools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../bin/tui-devtools.ts"],"sourcesContent":["/**\n * tui-devtools CLI — DevTools for Ink TUI apps\n *\n * Usage:\n * tui-devtools start Start DevTools server daemon\n * tui-devtools status Show connection status\n * tui-devtools tree Print component tree\n * tui-devtools inspect <name> Inspect a component's props/state\n * tui-devtools logs Show captured console logs\n * tui-devtools stop Stop daemon\n */\n\nimport { program } from 'commander';\nimport { fork } from 'node:child_process';\nimport { fileURLToPath } from 'node:url';\nimport path from 'node:path';\nimport fs from 'node:fs';\nimport {\n sendIpcRequest,\n isDaemonRunning,\n getPidPath,\n getLogPath,\n getSocketPath,\n} from '../src/daemon.js';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\nconst DEFAULT_PORT = 8097;\nconst DEFAULT_SESSION = 'default';\n\nprogram\n .name('tui-devtools')\n .description('DevTools for Ink TUI apps — component tree, state inspection, console capture')\n .version('0.1.0')\n .option('-s, --session <name>', 'Session name for daemon isolation', DEFAULT_SESSION)\n .option('-p, --port <port>', 'DevTools server port', String(DEFAULT_PORT))\n .option('--json', 'JSON output');\n\n// ─── start ───\nprogram\n .command('start')\n .description('Start the DevTools server daemon')\n .action(async () => {\n const opts = program.opts();\n const session = opts.session as string;\n const port = parseInt(opts.port as string, 10);\n\n if (isDaemonRunning(session)) {\n console.log(`Daemon already running (session: ${session})`);\n return;\n }\n\n // Fork daemon process\n const child = fork(__filename, ['__daemon__', session, String(port)], {\n detached: true,\n stdio: 'ignore',\n });\n child.unref();\n\n // Wait for socket to appear\n const socketPath = getSocketPath(session);\n for (let i = 0; i < 20; i++) {\n await new Promise(r => setTimeout(r, 200));\n if (fs.existsSync(socketPath)) {\n console.log(`DevTools server started (session: ${session}, port: ${port})`);\n console.log(` Logs: ${getLogPath(session)}`);\n console.log(`\\nStart your Ink app with: DEV=true your-app`);\n return;\n }\n }\n console.error('Failed to start daemon — check logs:', getLogPath(session));\n process.exit(1);\n });\n\n// ─── stop ───\nprogram\n .command('stop')\n .description('Stop the daemon')\n .action(async () => {\n const session = program.opts().session as string;\n const pidPath = getPidPath(session);\n\n try {\n const pid = parseInt(fs.readFileSync(pidPath, 'utf-8').trim(), 10);\n process.kill(pid, 'SIGTERM');\n console.log(`Daemon stopped (session: ${session})`);\n } catch {\n console.log(`No daemon running (session: ${session})`);\n }\n });\n\n// ─── status ───\nprogram\n .command('status')\n .description('Show connection status')\n .action(async () => {\n const opts = program.opts();\n const session = opts.session as string;\n const json = opts.json as boolean;\n\n if (!isDaemonRunning(session)) {\n if (json) {\n console.log(JSON.stringify({ running: false }));\n } else {\n console.log(`Daemon not running (session: ${session})`);\n }\n return;\n }\n\n try {\n const res = await sendIpcRequest(session, { command: 'status' });\n if (json) {\n console.log(JSON.stringify(res.data));\n } else {\n const d = res.data as Record<string, unknown>;\n console.log(`Session: ${session}`);\n console.log(`Connected: ${d.connected ? 'yes' : 'no'}`);\n console.log(`Components: ${d.nodeCount}`);\n console.log(`Roots: ${d.rootCount}`);\n console.log(`Logs: ${d.logCount}`);\n }\n } catch (e) {\n console.error(`Error: ${(e as Error).message}`);\n }\n });\n\n// ─── tree ───\nprogram\n .command('tree')\n .description('Print component tree')\n .option('-d, --depth <n>', 'Max tree depth', '20')\n .action(async (cmdOpts) => {\n const opts = program.opts();\n const session = opts.session as string;\n const json = opts.json as boolean;\n const depth = parseInt(cmdOpts.depth, 10);\n\n if (!isDaemonRunning(session)) {\n console.error('Daemon not running. Start with: tui-devtools start');\n process.exit(1);\n }\n\n try {\n const res = await sendIpcRequest(session, {\n command: 'tree',\n args: { depth, json },\n });\n if (res.ok) {\n if (json) {\n console.log(JSON.stringify(res.data, null, 2));\n } else {\n console.log(res.data || '(empty tree — is the app connected?)');\n }\n } else {\n console.error(`Error: ${res.error}`);\n }\n } catch (e) {\n console.error(`Error: ${(e as Error).message}`);\n }\n });\n\n// ─── inspect ───\nprogram\n .command('inspect [component]')\n .description('Inspect a component\\'s props, state, and hooks')\n .option('--id <id>', 'Inspect by fiber ID')\n .action(async (component, cmdOpts) => {\n const opts = program.opts();\n const session = opts.session as string;\n const json = opts.json as boolean;\n const id = cmdOpts.id ? parseInt(cmdOpts.id, 10) : undefined;\n\n if (!component && id == null) {\n console.error('Usage: tui-devtools inspect <component-name> or --id <id>');\n process.exit(1);\n }\n\n if (!isDaemonRunning(session)) {\n console.error('Daemon not running. Start with: tui-devtools start');\n process.exit(1);\n }\n\n try {\n const res = await sendIpcRequest(session, {\n command: 'inspect',\n args: { name: component, id },\n });\n if (res.ok) {\n if (json) {\n console.log(JSON.stringify(res.data, null, 2));\n } else {\n const d = res.data as Record<string, unknown>;\n console.log(`Component: ${d.displayName} [${d.id}]`);\n console.log(`Type: ${d.type}`);\n if (d.key) console.log(`Key: ${d.key}`);\n console.log(`Parent: ${d.parentId}`);\n console.log(`Children: ${(d.childIds as number[])?.length ?? 0}`);\n if (d.props) {\n console.log(`\\nProps:`);\n console.log(JSON.stringify(d.props, null, 2));\n }\n if (d.state) {\n console.log(`\\nState:`);\n console.log(JSON.stringify(d.state, null, 2));\n }\n if (d.inspectData) {\n console.log(`\\nInspect Data:`);\n console.log(JSON.stringify(d.inspectData, null, 2));\n }\n }\n } else {\n console.error(`Error: ${res.error}`);\n }\n } catch (e) {\n console.error(`Error: ${(e as Error).message}`);\n }\n });\n\n// ─── find ───\nprogram\n .command('find <name>')\n .description('Find components by name')\n .action(async (name) => {\n const opts = program.opts();\n const session = opts.session as string;\n const json = opts.json as boolean;\n\n if (!isDaemonRunning(session)) {\n console.error('Daemon not running. Start with: tui-devtools start');\n process.exit(1);\n }\n\n try {\n const res = await sendIpcRequest(session, {\n command: 'find',\n args: { name },\n });\n if (res.ok) {\n const nodes = res.data as Array<{ id: number; displayName: string; type: string; key: string | null }>;\n if (json) {\n console.log(JSON.stringify(nodes, null, 2));\n } else if (nodes.length === 0) {\n console.log(`No components matching \"${name}\"`);\n } else {\n for (const n of nodes) {\n console.log(` [${n.id}] ${n.displayName} (${n.type})${n.key ? ` key=\"${n.key}\"` : ''}`);\n }\n }\n } else {\n console.error(`Error: ${res.error}`);\n }\n } catch (e) {\n console.error(`Error: ${(e as Error).message}`);\n }\n });\n\n// ─── logs ───\nprogram\n .command('logs')\n .description('Show captured console logs')\n .option('-l, --level <level>', 'Filter by level (log|warn|error|info|debug)')\n .option('-t, --tail <n>', 'Show last N entries', '50')\n .action(async (cmdOpts) => {\n const opts = program.opts();\n const session = opts.session as string;\n const json = opts.json as boolean;\n\n if (!isDaemonRunning(session)) {\n console.error('Daemon not running. Start with: tui-devtools start');\n process.exit(1);\n }\n\n try {\n const res = await sendIpcRequest(session, {\n command: 'logs',\n args: {\n level: cmdOpts.level,\n tail: parseInt(cmdOpts.tail, 10),\n },\n });\n if (res.ok) {\n const logs = res.data as Array<{ level: string; args: unknown[]; timestamp: number }>;\n if (json) {\n console.log(JSON.stringify(logs, null, 2));\n } else if (logs.length === 0) {\n console.log('(no logs captured)');\n } else {\n for (const entry of logs) {\n const ts = new Date(entry.timestamp).toISOString().slice(11, 19);\n const level = entry.level.toUpperCase().padEnd(5);\n const msg = entry.args.map(a => typeof a === 'string' ? a : JSON.stringify(a)).join(' ');\n console.log(`${ts} [${level}] ${msg}`);\n }\n }\n } else {\n console.error(`Error: ${res.error}`);\n }\n } catch (e) {\n console.error(`Error: ${(e as Error).message}`);\n }\n });\n\n// ═══════════════════════════════════════════\n// PTY Automation Commands (replaces agent-tui)\n// ═══════════════════════════════════════════\n\nfunction ensureDaemon(session: string): void {\n if (!isDaemonRunning(session)) {\n console.error('Daemon not running. Start with: tui-devtools start');\n process.exit(1);\n }\n}\n\n// ─── run ───\nprogram\n .command('run <command...>')\n .description('Run a TUI application in a virtual terminal')\n .option('-d, --cwd <dir>', 'Working directory')\n .option('--cols <n>', 'Terminal columns', '120')\n .option('--rows <n>', 'Terminal rows', '40')\n .option('--sid <id>', 'PTY session ID', 'default')\n .action(async (commandParts: string[], cmdOpts) => {\n const opts = program.opts();\n const session = opts.session as string;\n const json = opts.json as boolean;\n const command = commandParts.join(' ');\n\n ensureDaemon(session);\n\n try {\n const res = await sendIpcRequest(session, {\n command: 'run',\n args: {\n command,\n sessionId: cmdOpts.sid,\n cwd: cmdOpts.cwd,\n cols: parseInt(cmdOpts.cols, 10),\n rows: parseInt(cmdOpts.rows, 10),\n },\n });\n if (res.ok) {\n const info = res.data as { id: string; pid: number; cols: number; rows: number };\n if (json) {\n console.log(JSON.stringify(info));\n } else {\n console.log(`Session started: ${info.id}`);\n console.log(` PID: ${info.pid}`);\n }\n } else {\n console.error(`Error: ${res.error}`);\n process.exit(1);\n }\n } catch (e) {\n console.error(`Error: ${(e as Error).message}`);\n process.exit(1);\n }\n });\n\n// ─── screenshot ───\nprogram\n .command('screenshot')\n .description('Capture current terminal screen')\n .option('--sid <id>', 'PTY session ID', 'default')\n .option('--strip-ansi', 'Strip ANSI color codes')\n .action(async (cmdOpts) => {\n const opts = program.opts();\n const session = opts.session as string;\n const json = opts.json as boolean;\n\n ensureDaemon(session);\n\n try {\n const res = await sendIpcRequest(session, {\n command: 'screenshot',\n args: { sessionId: cmdOpts.sid, stripAnsi: cmdOpts.stripAnsi },\n });\n if (res.ok) {\n const data = res.data as { screenshot: string; running: boolean };\n if (json) {\n console.log(JSON.stringify(data));\n } else {\n console.log(`Screenshot:${data.running ? '' : ' [stopped]'}`);\n console.log(data.screenshot);\n }\n } else {\n console.error(`Error: ${res.error}`);\n }\n } catch (e) {\n console.error(`Error: ${(e as Error).message}`);\n }\n });\n\n// ─── press ───\nprogram\n .command('press <keys...>')\n .description('Send key press(es) to the terminal')\n .option('--sid <id>', 'PTY session ID', 'default')\n .action(async (keys: string[], cmdOpts) => {\n const opts = program.opts();\n const session = opts.session as string;\n\n ensureDaemon(session);\n\n try {\n const res = await sendIpcRequest(session, {\n command: 'press',\n args: { sessionId: cmdOpts.sid, keys },\n });\n if (res.ok) {\n console.log('✓ Key pressed');\n } else {\n console.error(`Error: ${res.error}`);\n }\n } catch (e) {\n console.error(`Error: ${(e as Error).message}`);\n }\n });\n\n// ─── type ───\nprogram\n .command('type <text>')\n .description('Type text into the terminal')\n .option('--sid <id>', 'PTY session ID', 'default')\n .action(async (text: string, cmdOpts) => {\n const opts = program.opts();\n const session = opts.session as string;\n\n ensureDaemon(session);\n\n try {\n const res = await sendIpcRequest(session, {\n command: 'type',\n args: { sessionId: cmdOpts.sid, text },\n });\n if (res.ok) {\n console.log('✓ Text typed');\n } else {\n console.error(`Error: ${res.error}`);\n }\n } catch (e) {\n console.error(`Error: ${(e as Error).message}`);\n }\n });\n\n// ─── wait ───\nprogram\n .command('wait <text>')\n .description('Wait for text to appear on screen')\n .option('--sid <id>', 'PTY session ID', 'default')\n .option('--timeout <ms>', 'Timeout in milliseconds', '30000')\n .action(async (text: string, cmdOpts) => {\n const opts = program.opts();\n const session = opts.session as string;\n const json = opts.json as boolean;\n\n ensureDaemon(session);\n\n try {\n const res = await sendIpcRequest(session, {\n command: 'wait',\n args: { sessionId: cmdOpts.sid, text, timeout: parseInt(cmdOpts.timeout, 10) },\n });\n if (res.ok) {\n const data = res.data as { found: boolean; screenshot: string };\n if (json) {\n console.log(JSON.stringify(data));\n } else {\n console.log(data.found ? `✓ Found: \"${text}\"` : `✗ Timeout waiting for: \"${text}\"`);\n }\n } else {\n console.error(`Error: ${res.error}`);\n }\n } catch (e) {\n console.error(`Error: ${(e as Error).message}`);\n }\n });\n\n// ─── kill (PTY session) ───\nprogram\n .command('kill-session')\n .description('Kill a PTY session')\n .option('--sid <id>', 'PTY session ID', 'default')\n .action(async (cmdOpts) => {\n const opts = program.opts();\n const session = opts.session as string;\n\n ensureDaemon(session);\n\n try {\n const res = await sendIpcRequest(session, {\n command: 'kill-session',\n args: { sessionId: cmdOpts.sid },\n });\n if (res.ok) {\n console.log(`Session killed: ${cmdOpts.sid}`);\n } else {\n console.error(`Error: ${res.error}`);\n }\n } catch (e) {\n console.error(`Error: ${(e as Error).message}`);\n }\n });\n\n// ─── sessions (list PTY sessions) ───\nprogram\n .command('sessions')\n .description('List PTY sessions')\n .action(async () => {\n const opts = program.opts();\n const session = opts.session as string;\n const json = opts.json as boolean;\n\n ensureDaemon(session);\n\n try {\n const res = await sendIpcRequest(session, { command: 'sessions' });\n if (res.ok) {\n const sessions = res.data as Array<{ id: string; command: string; pid: number; running: boolean; cols: number; rows: number }>;\n if (json) {\n console.log(JSON.stringify(sessions, null, 2));\n } else if (sessions.length === 0) {\n console.log('No active PTY sessions');\n } else {\n for (const s of sessions) {\n console.log(` ${s.id} — ${s.command} [${s.running ? 'running' : 'stopped'}] ${s.cols}x${s.rows} pid:${s.pid}`);\n }\n }\n } else {\n console.error(`Error: ${res.error}`);\n }\n } catch (e) {\n console.error(`Error: ${(e as Error).message}`);\n }\n });\n\n// ─── Internal: daemon entry point ───\nif (process.argv[2] === '__daemon__') {\n const session = process.argv[3] ?? DEFAULT_SESSION;\n const port = parseInt(process.argv[4] ?? String(DEFAULT_PORT), 10);\n\n const { startDaemon } = await import('../src/daemon.js');\n await startDaemon(session, port);\n} else {\n program.parse();\n}\n"],"mappings":"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","names":[]}