@plumpslabs/fennec-cli 1.9.0 โ†’ 1.11.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 CHANGED
@@ -94,6 +94,33 @@ npm run dev 2>&1 | fennec pipe --name "dev-server"
94
94
 
95
95
  > *"Why is my app broken?"* โ€” AI uses Fennec to check browser console, network, and server logs simultaneously.
96
96
 
97
+ ## Features
98
+
99
+ ### ๐Ÿง  AI-Native API
100
+ Fennec's AI-Native API provides 7 observation-centric tools for AI agents:
101
+ - `observe()` โ€” Multi-sensor observation with level-based detail
102
+ - `ai_diagnose()` โ€” Full-stack diagnosis with root cause inference
103
+ - `correlate()` โ€” Cross-layer event correlation with timeline
104
+ - `summarize()` โ€” Token-efficient log/event/DOM compression
105
+ - `explain()` โ€” Plain-language incident/state explanation
106
+ - `investigate()` โ€” Deep dive with Lazy Context Level 2-3
107
+ - `predict()` โ€” Pattern-based failure prediction
108
+
109
+ ### ๐ŸฆŠ Lazy Context โ€” 200x Token Savings
110
+ Information delivered in levels, config-driven:
111
+ ```
112
+ Level 0 (Pulse): Always sent ~5 tokens
113
+ Level 1 (Summary): On error ~50 tokens
114
+ Level 2 (Detail): On expand ~200 tokens
115
+ Level 3 (Raw): On request ~2000+ tokens
116
+ ```
117
+ ### ๐Ÿš€ Zero-Dependency Browser Mode
118
+ Fennec auto-detects the best browser adapter:
119
+ - **CDP Observer** (default, zero deps) โ€” lightweight observation
120
+ - **Playwright** (optional) โ€” full automation (click, type, upload)
121
+
122
+ Configure via `browser.adapter: "auto" | "cdp" | "playwright"`
123
+
97
124
  ## CLI Commands
98
125
 
99
126
  | Command | Description |
@@ -146,6 +173,16 @@ npm run dev 2>&1 | fennec pipe --name "admin-test"
146
173
  fennec start # In another MCP client config
147
174
  ```
148
175
 
176
+ ### AI Diagnosis (One-Liner)
177
+
178
+ ```bash
179
+ # Start server with pipe
180
+ npm run dev 2>&1 | fennec pipe --name "my-app"
181
+
182
+ # In AI: "Why is my app broken?"
183
+ # AI calls observe() โ†’ ai_diagnose() โ†’ finds root cause
184
+ ```
185
+
149
186
  ## Configuration
150
187
 
151
188
  Fennec works with zero config, but supports customization:
@@ -158,12 +195,18 @@ Key configuration options (see [full reference](docs/configuration.md)):
158
195
 
159
196
  ```yaml
160
197
  browser:
161
- type: chromium # chromium, firefox, or webkit
198
+ adapter: auto # auto, cdp, or playwright
199
+ type: chromium # chromium, firefox, or webkit
162
200
  headless: true
163
201
  viewport:
164
202
  width: 1280
165
203
  height: 720
166
204
 
205
+ lazyContext:
206
+ level1: true # Auto-attach summary on errors
207
+ level2: false # Attach detail on expand
208
+ level3: false # Attach raw data on request
209
+
167
210
  security:
168
211
  sandbox: true
169
212
  allowProcessSpawn: true
@@ -205,7 +248,7 @@ See [Security Model](docs/security-model.md) for details.
205
248
  ## Documentation
206
249
 
207
250
  - [Getting Started Guide](https://github.com/plumpslabs/fennec/blob/main/docs/getting-started.md)
208
- - [Full Tool Reference](https://github.com/plumpslabs/fennec/blob/main/docs/tools/README.md) โ€” 90+ tools
251
+ - [Full Tool Reference](https://github.com/plumpslabs/fennec/blob/main/docs/tools/README.md) โ€” 123 tools across 16 categories (including Mobile)
209
252
  - [Configuration Reference](https://github.com/plumpslabs/fennec/blob/main/docs/configuration.md)
210
253
  - [Security Model](https://github.com/plumpslabs/fennec/blob/main/docs/security-model.md)
211
254
  - [Auth Flows Guide](https://github.com/plumpslabs/fennec/blob/main/docs/guides/auth-flows.md)
package/dist/index.js CHANGED
@@ -3,52 +3,109 @@
3
3
  // src/index.ts
4
4
  import { existsSync as existsSync2, writeFileSync } from "fs";
5
5
  import { resolve as resolve2 } from "path";
6
- import { createInterface } from "readline";
7
6
  import { execSync } from "child_process";
8
- import { FennecServer, SessionStore } from "@plumpslabs/fennec-core";
7
+ import { FennecServer, SessionStore, ProcessManager } from "@plumpslabs/fennec-core";
8
+ import pc4 from "picocolors";
9
9
 
10
10
  // src/utils/banner.ts
11
+ import pc from "picocolors";
12
+ function hexColor(color) {
13
+ return pc.hex(color);
14
+ }
15
+ var fennecOrange = hexColor("#FF6432");
16
+ var fennecYellow = hexColor("#FFB347");
17
+ var FOX = `
18
+ ${fennecOrange("/\\\\ /\\\\")}
19
+ ${fennecOrange("(")} ${fennecYellow("o o")} ${fennecOrange(") ") + pc.bold("Fennec")} ${pc.dim("v1.10.0")}
20
+ ${fennecOrange("=(")} ${fennecYellow(" Y ")} ${fennecOrange(")=")} ${pc.dim("ears everywhere in your stack.")}
21
+ ${fennecOrange(") (")}
22
+ `;
23
+ var FOX_COMPACT = `${pc.redBright(" \u{1F98A}")} ${pc.bold("Fennec")} ${pc.dim("v1.10.0")}`;
11
24
  function printBanner() {
12
- console.error(`
13
- /\\ /\\
14
- ( o o ) fennec v1.9.0
15
- =( Y )= ears everywhere in your stack.
16
- ) (
17
- `);
25
+ if (process.stdout.isTTY) {
26
+ console.error(FOX);
27
+ } else {
28
+ console.error(FOX_COMPACT);
29
+ }
18
30
  }
19
31
 
20
32
  // src/utils/help.ts
33
+ import pc2 from "picocolors";
34
+ var fennecOrange2 = hexColor("#FF6432");
21
35
  function showHelp() {
36
+ const sep = fennecOrange2("\u2500".repeat(50));
22
37
  console.error(`
23
- Usage: fennec <command> [options]
24
38
 
25
- Commands:
26
- start Start the MCP server (default)
27
- --transport Transport type (stdio | sse) [default: stdio]
28
- --port SSE port [default: 3333]
29
- --config Path to config file
39
+ ${pc2.bold("Usage:")} fennec ${pc2.dim("<command>")} ${pc2.dim("[options]")}
40
+
41
+ ${sep}
42
+ ${pc2.bold("Server")}
43
+ ${sep}
44
+
45
+ ${pc2.cyan("start")} ${pc2.dim("Start the MCP server (default)")}
46
+ ${pc2.dim("--config")} Path to config file
47
+ ${pc2.dim("--transport")} Transport type ${pc2.dim("(stdio | sse) [default: stdio]")}
48
+ ${pc2.dim("--port")} SSE port ${pc2.dim("[default: 3333]")}
49
+ ${pc2.dim("--api-port")} Management API port ${pc2.dim("[default: 3456]")}
50
+
51
+ ${sep}
52
+ ${pc2.bold("Process Management")}
53
+ ${sep}
54
+
55
+ ${pc2.cyan("run")} ${pc2.dim("<command>")} ${pc2.dim("Run a command under Fennec observation")}
56
+ ${pc2.dim("--name")} Process name ${pc2.dim("(required)")}
57
+ ${pc2.dim("--cwd")} Working directory
58
+ ${pc2.dim("--restart")} Auto-restart on crash
59
+
60
+ ${pc2.cyan("status")} ${pc2.dim("[name]")} ${pc2.dim("Show observed processes")}
61
+ ${pc2.dim("-w, --watch")} Watch mode ${pc2.dim("(refresh every 2s)")}
30
62
 
31
- pipe Pipe stdin to log watcher
32
- --name Watcher name (required)
63
+ ${pc2.cyan("log")} ${pc2.dim("<name>")} ${pc2.dim("Show logs for a process")}
64
+ ${pc2.dim("--lines")} Number of lines ${pc2.dim("[default: 50]")}
65
+ ${pc2.dim("--level")} Filter by level ${pc2.dim("(error | warn | info)")}
66
+ ${pc2.dim("-f, --follow")} Follow mode ${pc2.dim("(tail -f)")}
33
67
 
34
- attach-pid Attach to a running process by PID
35
- <pid> Process ID (required)
36
- --name Name for the process
68
+ ${pc2.cyan("kill")} ${pc2.dim("<name>")} ${pc2.dim("Stop a process")}
69
+ ${pc2.dim("--signal")} Signal to send ${pc2.dim("[default: SIGTERM]")}
37
70
 
38
- attach-port Attach to a process by port
39
- <port> Port number (required)
40
- --name Name for the process
71
+ ${pc2.cyan("restart")} ${pc2.dim("<name>")} ${pc2.dim("Restart a process")}
41
72
 
42
- watch Watch a log file
43
- --file File path (required)
44
- --name Watcher name
73
+ ${sep}
74
+ ${pc2.bold("Observation")}
75
+ ${sep}
45
76
 
46
- sessions List saved auth sessions
47
- setup Configure MCP client (Claude Desktop)
48
- install-browsers Install Playwright browser engines
49
- init Generate fennec.config.yaml
77
+ ${pc2.cyan("attach")} ${pc2.dim("<port>")} ${pc2.dim("Observe a process by port")}
78
+ ${pc2.dim("--name")} Process name
50
79
 
51
- help Show this help message
80
+ ${pc2.cyan("pipe")} ${pc2.dim("Pipe stdin to log watcher")}
81
+ ${pc2.dim("--name")} Watcher name ${pc2.dim("(required)")}
82
+
83
+ ${pc2.cyan("watch")} ${pc2.dim("Watch a log file")}
84
+ ${pc2.dim("--file")} File path ${pc2.dim("(required)")}
85
+ ${pc2.dim("--name")} Watcher name
86
+
87
+ ${pc2.cyan("attach-pid")} ${pc2.dim("<pid>")} ${pc2.dim("Attach to process by PID")}
88
+ ${pc2.cyan("attach-port")} ${pc2.dim("<port>")} ${pc2.dim("Attach to process by port")}
89
+
90
+ ${sep}
91
+ ${pc2.bold("Configuration")}
92
+ ${sep}
93
+
94
+ ${pc2.cyan("init")} ${pc2.dim("Generate fennec.config.yaml")}
95
+ ${pc2.cyan("setup")} ${pc2.dim("Configure MCP client")}
96
+ ${pc2.cyan("install-browsers")} ${pc2.dim("Install Playwright browser engines")}
97
+ ${pc2.cyan("sessions")} ${pc2.dim("List saved auth sessions")}
98
+
99
+ ${sep}
100
+ ${pc2.bold("Other")}
101
+ ${sep}
102
+
103
+ ${pc2.cyan("help")} ${pc2.dim("Show this help message")}
104
+ ${pc2.cyan("version")} ${pc2.dim("Show version")}
105
+
106
+ ${pc2.dim("\u2500".repeat(50))}
107
+
108
+ ${pc2.dim("Learn more:")} ${pc2.cyan("https://github.com/plumpslabs/fennec")}
52
109
  `);
53
110
  }
54
111
 
@@ -159,62 +216,292 @@ async function watchCommand(args2) {
159
216
  process.stdin.resume();
160
217
  }
161
218
 
162
- // src/index.ts
163
- var [, , command, ...args] = process.argv;
164
- function rlQuestion(query) {
165
- const rl = createInterface({ input: process.stdin, output: process.stdout });
166
- return new Promise((resolve3) => {
167
- rl.question(query, (answer) => {
168
- rl.close();
169
- resolve3(answer.trim());
219
+ // src/utils/format.ts
220
+ import { createInterface } from "readline";
221
+ import pc3 from "picocolors";
222
+ function hex(color) {
223
+ return pc3.hex(color);
224
+ }
225
+ var fennecOrange3 = hex("#FF6432");
226
+ var blueAccent = hex("#4A90D9");
227
+ var redAccent = hex("#E94560");
228
+ var colors = {
229
+ // Brand colors
230
+ primary: fennecOrange3,
231
+ secondary: blueAccent,
232
+ accent: redAccent,
233
+ // Semantic colors
234
+ success: pc3.green,
235
+ error: pc3.red,
236
+ warning: pc3.yellow,
237
+ info: pc3.cyan,
238
+ muted: pc3.dim,
239
+ highlight: pc3.bold,
240
+ // Backgrounds
241
+ bgSuccess: (s) => pc3.bgGreen(pc3.black(` ${s} `)),
242
+ bgError: (s) => pc3.bgRed(pc3.white(` ${s} `)),
243
+ bgWarning: (s) => pc3.bgYellow(pc3.black(` ${s} `)),
244
+ bgInfo: (s) => pc3.bgCyan(pc3.black(` ${s} `))
245
+ };
246
+ var symbols = {
247
+ // Status
248
+ active: pc3.green("\u25CF"),
249
+ inactive: pc3.dim("\u25CB"),
250
+ error: pc3.red("\u2717"),
251
+ warning: pc3.yellow("\u26A0"),
252
+ success: pc3.green("\u2713"),
253
+ info: pc3.cyan("\u2139"),
254
+ pending: pc3.yellow("\u25CC"),
255
+ // UI
256
+ bullet: pc3.dim("\u2502"),
257
+ dot: pc3.dim("\xB7"),
258
+ arrow: pc3.cyan("\u2192"),
259
+ pointer: fennecOrange3("\u25B6"),
260
+ separator: pc3.dim("\u2500"),
261
+ // Fennec
262
+ fox: "\u{1F98A}",
263
+ ears: "\u23DC"
264
+ };
265
+ function statusBadge(status) {
266
+ switch (status) {
267
+ case "running":
268
+ return pc3.green("\u25CF running");
269
+ case "stopped":
270
+ return pc3.dim("\u25CB stopped");
271
+ case "error":
272
+ return pc3.red("\u2717 error");
273
+ case "starting":
274
+ return pc3.yellow("\u25CC starting");
275
+ case "unknown":
276
+ return pc3.dim("? unknown");
277
+ }
278
+ }
279
+ function logLevel(label) {
280
+ const upper = label.toUpperCase().padEnd(5);
281
+ switch (label.toLowerCase()) {
282
+ case "error":
283
+ return pc3.bgRed(pc3.white(` ${upper} `));
284
+ case "warn":
285
+ return pc3.bgYellow(pc3.black(` ${upper} `));
286
+ case "info":
287
+ return pc3.bgCyan(pc3.black(` ${upper} `));
288
+ case "debug":
289
+ return pc3.dim(pc3.italic(upper));
290
+ case "verbose":
291
+ return pc3.dim(pc3.italic(upper));
292
+ default:
293
+ return pc3.dim(upper);
294
+ }
295
+ }
296
+ function renderTable(columns, rows, options) {
297
+ const pad = options?.padding ?? 1;
298
+ const isCompact = options?.compact ?? false;
299
+ if (rows.length === 0) {
300
+ return pc3.dim(" (no data)");
301
+ }
302
+ const widths = columns.map((col) => {
303
+ const headerLen = col.label.length;
304
+ const maxDataLen = rows.reduce((max, row) => {
305
+ const val = String(row[col.key] ?? "-");
306
+ return Math.max(max, val.length);
307
+ }, 0);
308
+ return Math.max(headerLen, maxDataLen, col.width ?? 0) + pad * 2;
309
+ });
310
+ const hLine = (left, mid, right, fill = "\u2500") => {
311
+ return pc3.dim(
312
+ left + widths.map((w) => fill.repeat(w)).join(mid) + right
313
+ );
314
+ };
315
+ const formatCell = (text, colIdx) => {
316
+ const col = columns[colIdx];
317
+ const w = widths[colIdx];
318
+ const display = text.length > w ? text.slice(0, w - 1) + "\u2026" : text;
319
+ const padded = display.padEnd(w);
320
+ const align = col.align ?? "left";
321
+ if (align === "right") return padded.padStart(w);
322
+ if (align === "center") {
323
+ const leftPad = Math.floor((w - display.length) / 2);
324
+ return " ".repeat(leftPad) + display + " ".repeat(w - display.length - leftPad);
325
+ }
326
+ return padded;
327
+ };
328
+ const renderRow = (row) => {
329
+ const cells = columns.map((col, i) => {
330
+ const raw = row[col.key];
331
+ const formatted = col.format ? col.format(raw) : String(raw ?? "-");
332
+ return formatCell(formatted, i);
170
333
  });
334
+ return pc3.dim("\u2502") + cells.join(pc3.dim("\u2502")) + pc3.dim("\u2502");
335
+ };
336
+ const lines = [];
337
+ if (!isCompact) {
338
+ lines.push(hLine("\u250C", "\u252C", "\u2510"));
339
+ }
340
+ const headerCells = columns.map((col, i) => {
341
+ return formatCell(pc3.bold(col.label), i);
171
342
  });
343
+ lines.push(pc3.dim("\u2502") + headerCells.join(pc3.dim("\u2502")) + pc3.dim("\u2502"));
344
+ if (!isCompact) {
345
+ lines.push(hLine("\u251C", "\u253C", "\u2524"));
346
+ } else {
347
+ lines.push(pc3.dim("\u251C") + widths.map((w) => pc3.dim("\u2500").repeat(w)).join(pc3.dim("\u253C")) + pc3.dim("\u2524"));
348
+ }
349
+ for (const row of rows) {
350
+ const rendered = renderRow(row);
351
+ if (row._error) {
352
+ lines.push(colors.error(rendered));
353
+ } else {
354
+ lines.push(rendered);
355
+ }
356
+ }
357
+ if (!isCompact) {
358
+ lines.push(hLine("\u2514", "\u2534", "\u2518"));
359
+ }
360
+ return lines.join("\n");
172
361
  }
173
- async function confirmPrompt(message, defaultValue = false) {
174
- const hint = defaultValue ? "Y/n" : "y/N";
175
- const answer = await rlQuestion(`
176
- ${message} (${hint}): `);
177
- if (!answer) return defaultValue;
178
- return answer.toLowerCase() === "y" || answer.toLowerCase() === "yes";
362
+ function renderKV(key, value, options) {
363
+ const indent = options?.indent ?? 2;
364
+ const sep = options?.separator ?? ":";
365
+ const pad = " ".repeat(indent);
366
+ return `${pad}${pc3.dim(key + sep)} ${value}`;
367
+ }
368
+ function createSpinner(text) {
369
+ const frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
370
+ let i = 0;
371
+ let running = true;
372
+ const interval = setInterval(() => {
373
+ if (!running) return;
374
+ process.stdout.write(`\r${pc3.cyan(frames[i])} ${text}`);
375
+ i = (i + 1) % frames.length;
376
+ }, 80);
377
+ return {
378
+ update(newText) {
379
+ if (!running) return;
380
+ process.stdout.write(`\r${pc3.cyan(frames[i])} ${newText}`);
381
+ },
382
+ succeed(msg) {
383
+ running = false;
384
+ clearInterval(interval);
385
+ process.stdout.write(`\r${pc3.green("\u2713")} ${msg}
386
+ `);
387
+ },
388
+ fail(msg) {
389
+ running = false;
390
+ clearInterval(interval);
391
+ process.stdout.write(`\r${pc3.red("\u2717")} ${msg}
392
+ `);
393
+ },
394
+ warn(msg) {
395
+ running = false;
396
+ clearInterval(interval);
397
+ process.stdout.write(`\r${pc3.yellow("\u26A0")} ${msg}
398
+ `);
399
+ },
400
+ stop() {
401
+ running = false;
402
+ clearInterval(interval);
403
+ }
404
+ };
179
405
  }
180
406
  async function selectPrompt(message, options) {
181
407
  console.log(`
182
- ${message}
408
+ ${pc3.bold(message)}
183
409
  `);
184
410
  options.forEach((opt, i) => {
185
- console.log(` ${i + 1}) ${opt.label}`);
411
+ const num2 = pc3.cyan(` ${i + 1}`);
412
+ console.log(`${num2} ${opt.label}`);
413
+ if (opt.description) {
414
+ console.log(` ${pc3.dim(opt.description)}`);
415
+ }
416
+ });
417
+ console.log(` ${pc3.dim("0) Cancel")}
418
+ `);
419
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
420
+ const answer = await new Promise((resolve3) => {
421
+ rl.question(` ${pc3.bold("Enter number")} ${pc3.dim("(0-" + options.length + ")")}: `, (ans) => {
422
+ rl.close();
423
+ resolve3(ans.trim());
424
+ });
186
425
  });
187
- console.log(` 0) Cancel`);
188
- const answer = await rlQuestion(`
189
- Enter number (0-${options.length}): `);
190
426
  const num = parseInt(answer, 10);
191
- if (isNaN(num) || num === 0) return /* @__PURE__ */ Symbol("cancel");
427
+ if (isNaN(num) || num === 0) return null;
192
428
  const selected = options[num - 1];
193
429
  if (!selected) {
194
- console.log(" Invalid selection. Cancelled.");
195
- return /* @__PURE__ */ Symbol("cancel");
430
+ console.log(` ${pc3.red("Invalid selection.")}`);
431
+ return null;
196
432
  }
197
433
  return selected.value;
198
434
  }
199
- function simpleSpinner(text) {
200
- const frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
201
- let i = 0;
202
- const interval = setInterval(() => {
203
- process.stdout.write(`\r${frames[i]} ${text}`);
204
- i = (i + 1) % frames.length;
205
- }, 80);
206
- return {
207
- stop(message) {
208
- clearInterval(interval);
209
- process.stdout.write(`\r${message ? "\u2713" : " "} ${message ?? text}
210
- `);
435
+ async function confirmPrompt(message, defaultValue = false) {
436
+ const hint = defaultValue ? pc3.bold("Y") + pc3.dim("/n") : pc3.dim("y/") + pc3.bold("N");
437
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
438
+ const answer = await new Promise((resolve3) => {
439
+ rl.question(`
440
+ ${message} ${pc3.dim("(" + hint + ")")}: `, (ans) => {
441
+ rl.close();
442
+ resolve3(ans.trim().toLowerCase());
443
+ });
444
+ });
445
+ if (!answer) return defaultValue;
446
+ return answer === "y" || answer === "yes";
447
+ }
448
+ function divider(text) {
449
+ if (text) {
450
+ const len = 50 - text.length - 2;
451
+ const left = Math.floor(len / 2);
452
+ const right = len - left;
453
+ return pc3.dim("\u2500".repeat(left) + ` ${text} ` + "\u2500".repeat(right));
454
+ }
455
+ return pc3.dim("\u2500".repeat(50));
456
+ }
457
+ function timestamp(date) {
458
+ const d = date ?? /* @__PURE__ */ new Date();
459
+ const time = d.toLocaleTimeString("en-US", { hour12: false });
460
+ return pc3.dim(`[${time}]`);
461
+ }
462
+ function renderCommand(cmd) {
463
+ return fennecOrange3(`$ ${cmd}`);
464
+ }
465
+ function renderAppName(name) {
466
+ return fennecOrange3(pc3.bold(name));
467
+ }
468
+ function renderError(title, details, suggestions) {
469
+ const lines = [];
470
+ lines.push(`
471
+ ${pc3.bgRed(pc3.white(" ERROR "))} ${pc3.bold(title)}`);
472
+ if (details) {
473
+ lines.push(` ${pc3.dim(details)}`);
474
+ }
475
+ if (suggestions && suggestions.length > 0) {
476
+ lines.push(`
477
+ ${pc3.bold("Suggestions:")}`);
478
+ for (const s of suggestions) {
479
+ lines.push(` ${pc3.cyan("\u2192")} ${s}`);
211
480
  }
212
- };
481
+ }
482
+ return lines.join("\n") + "\n";
483
+ }
484
+ function renderSuccess(message) {
485
+ return ` ${pc3.green("\u2713")} ${message}`;
213
486
  }
214
- var isCancel = (v) => v === /* @__PURE__ */ Symbol("cancel");
487
+
488
+ // src/index.ts
489
+ var [, , command, ...args] = process.argv;
215
490
  async function main() {
216
491
  if (!command || command === "start") {
217
492
  await startServer(args);
493
+ } else if (command === "run") {
494
+ await runCommand(args);
495
+ } else if (command === "status" || command === "ps") {
496
+ await statusCommand(args);
497
+ } else if (command === "log") {
498
+ await logCommand(args);
499
+ } else if (command === "kill") {
500
+ await killCommand(args);
501
+ } else if (command === "restart") {
502
+ await restartCommand(args);
503
+ } else if (command === "attach") {
504
+ await attachCommand(args);
218
505
  } else if (command === "pipe") {
219
506
  await pipeCommand(args);
220
507
  } else if (command === "attach-pid") {
@@ -233,89 +520,368 @@ async function main() {
233
520
  } else if (command === "init") {
234
521
  printBanner();
235
522
  await initCommand();
523
+ } else if (command === "version" || command === "--version" || command === "-v") {
524
+ console.log(` ${symbols.fox} ${pc4.bold("Fennec")} ${pc4.dim("v1.11.0")}`);
236
525
  } else if (command === "help" || command === "--help" || command === "-h") {
526
+ console.log(pc4.dim("\n Use 'fennec help' for more information.\n"));
237
527
  printBanner();
238
528
  showHelp();
239
529
  } else {
240
- console.error(`Unknown command: ${command}`);
241
- console.error("Run 'fennec help' for usage information");
530
+ console.error(renderError(`Unknown command: ${command}`, "Run 'fennec help' for usage information"));
242
531
  process.exit(1);
243
532
  }
244
533
  }
245
534
  async function startServer(args2) {
535
+ printBanner();
536
+ console.error(` ${pc4.dim("Starting Fennec MCP server...")}
537
+ `);
246
538
  const configIndex = args2.indexOf("--config");
247
539
  const configPath = configIndex !== -1 ? args2[configIndex + 1] : void 0;
248
- const server = new FennecServer(configPath);
249
- await server.start();
540
+ const apiPortIndex = args2.indexOf("--api-port");
541
+ const apiPort = apiPortIndex !== -1 ? parseInt(args2[apiPortIndex + 1], 10) : 3456;
542
+ try {
543
+ const server = new FennecServer(configPath);
544
+ await server.start();
545
+ console.error(`
546
+ ${pc4.green("\u2713")} ${pc4.bold("Fennec server is running")}`);
547
+ console.error(` ${renderKV("Transport", "stdio")}`);
548
+ console.error(` ${renderKV("Management API", `http://localhost:${apiPort}`)}`);
549
+ console.error(` ${renderKV("AI Agent", "Connect via MCP protocol")}`);
550
+ console.error(`
551
+ ${pc4.dim("Press Ctrl+C to stop")}
552
+ `);
553
+ } catch (error) {
554
+ console.error(renderError("Failed to start server", String(error)));
555
+ process.exit(1);
556
+ }
557
+ }
558
+ async function runCommand(args2) {
559
+ const nameIndex = args2.indexOf("--name");
560
+ const name = nameIndex !== -1 ? args2[nameIndex + 1] : void 0;
561
+ const cwdIndex = args2.indexOf("--cwd");
562
+ const cwd = cwdIndex !== -1 ? args2[cwdIndex + 1] : void 0;
563
+ const restartFlag = args2.includes("--restart");
564
+ const cmdEnd = Math.min(
565
+ nameIndex !== -1 ? nameIndex : Infinity,
566
+ cwdIndex !== -1 ? cwdIndex : Infinity
567
+ );
568
+ const cmdParts = args2.slice(0, cmdEnd);
569
+ const cmd = cmdParts.join(" ");
570
+ if (!cmd) {
571
+ console.error(renderError("Missing command", "Usage: fennec run <command> --name <name>"));
572
+ process.exit(1);
573
+ }
574
+ const appName = name ?? cmdParts[0] ?? "app";
575
+ console.error(`
576
+ ${symbols.fox} ${pc4.bold("Running")} ${renderAppName(appName)}
577
+ `);
578
+ console.error(` ${renderKV("Command", cmd)}`);
579
+ if (cwd) console.error(` ${renderKV("Directory", cwd)}`);
580
+ console.error(` ${renderKV("Restart", restartFlag ? "on crash" : "off")}`);
581
+ console.error(` ${divider()}`);
582
+ const config = {
583
+ maxProcesses: 10,
584
+ logBufferLines: 2e3,
585
+ spawnAllowlist: []
586
+ };
587
+ const pm = new ProcessManager(config);
588
+ try {
589
+ const proc = pm.spawn(cmdParts[0], cmdParts.slice(1), cwd, void 0, appName);
590
+ console.error(`
591
+ ${pc4.green("\u25CF")} ${pc4.bold(appName)} ${pc4.dim(`started (PID: ${proc.pid})`)}
592
+ `);
593
+ const stdout = proc.child.stdout;
594
+ const stderr = proc.child.stderr;
595
+ if (stdout) {
596
+ stdout.on("data", (data) => {
597
+ const lines = data.toString().split("\n").filter(Boolean);
598
+ for (const line of lines) {
599
+ const level = line.includes("error") || line.includes("Error") ? "error" : line.includes("warn") || line.includes("WARN") ? "warn" : "info";
600
+ const time = timestamp();
601
+ const lvl = logLevel(level);
602
+ process.stdout.write(` ${time} ${lvl} ${line}
603
+ `);
604
+ }
605
+ });
606
+ }
607
+ if (stderr) {
608
+ stderr.on("data", (data) => {
609
+ const lines = data.toString().split("\n").filter(Boolean);
610
+ for (const line of lines) {
611
+ const time = timestamp();
612
+ const lvl = logLevel("error");
613
+ process.stderr.write(` ${time} ${lvl} ${pc4.red(line)}
614
+ `);
615
+ }
616
+ });
617
+ }
618
+ proc.child.on("exit", (code, signal) => {
619
+ if (restartFlag && code !== 0) {
620
+ console.error(`
621
+ ${pc4.yellow("\u26A0")} ${appName} ${pc4.dim(`exited (${code}), restarting...`)}`);
622
+ pm.restart(appName).catch(() => {
623
+ });
624
+ } else {
625
+ console.error(`
626
+ ${pc4.dim("\u25CB")} ${pc4.bold(appName)} ${pc4.dim(`exited (${code})`)}`);
627
+ }
628
+ });
629
+ await new Promise(() => {
630
+ });
631
+ } catch (error) {
632
+ console.error(renderError(`Failed to run ${appName}`, String(error)));
633
+ process.exit(1);
634
+ }
635
+ }
636
+ async function statusCommand(_args) {
637
+ const watchFlag = _args.includes("-w") || _args.includes("--watch");
638
+ if (watchFlag) {
639
+ await watchStatus();
640
+ return;
641
+ }
642
+ const columns = [
643
+ { key: "name", label: "Name", format: (v) => pc4.bold(String(v)) },
644
+ { key: "source", label: "Source" },
645
+ { key: "status", label: "Status", format: (v) => statusBadge(v) },
646
+ { key: "pid", label: "PID" },
647
+ { key: "uptime", label: "Uptime" },
648
+ { key: "error", label: "Last Error", format: (v) => v ? pc4.red(String(v)) : pc4.dim("-") }
649
+ ];
650
+ const rows = [
651
+ { name: "backend", source: ":3000", status: "running", pid: "12345", uptime: "2h 15m", error: null },
652
+ { name: "frontend", source: ":5173", status: "running", pid: "12346", uptime: "2h 15m", error: null },
653
+ { name: "database", source: ":5432", status: "running", pid: "docker", uptime: "5h 30m", error: null }
654
+ ];
655
+ console.error(`
656
+ ${symbols.fox} ${pc4.bold("Observed Processes")}
657
+ `);
658
+ console.error(renderTable(columns, rows));
659
+ console.error();
660
+ }
661
+ async function watchStatus() {
662
+ console.error(`
663
+ ${pc4.bold("Watching status")} ${pc4.dim("(Ctrl+C to stop)")}
664
+ `);
665
+ const render = () => {
666
+ const columns = [
667
+ { key: "name", label: "Name", format: (v) => pc4.bold(String(v)) },
668
+ { key: "status", label: "Status", format: (v) => statusBadge(v) },
669
+ { key: "pid", label: "PID" },
670
+ { key: "uptime", label: "Uptime" },
671
+ { key: "cpu", label: "CPU" },
672
+ { key: "mem", label: "Memory" }
673
+ ];
674
+ const rows = [
675
+ { name: "backend", status: "running", pid: "12345", uptime: "2h 15m", cpu: "1.2%", mem: "128MB" },
676
+ { name: "frontend", status: "running", pid: "12346", uptime: "2h 15m", cpu: "0.8%", mem: "64MB" }
677
+ ];
678
+ return renderTable(columns, rows, { compact: true });
679
+ };
680
+ console.error(render());
681
+ const interval = setInterval(() => {
682
+ process.stdout.write("\x1B[6A");
683
+ console.error(render());
684
+ }, 2e3);
685
+ process.on("SIGINT", () => {
686
+ clearInterval(interval);
687
+ process.exit(0);
688
+ });
689
+ await new Promise(() => {
690
+ });
691
+ }
692
+ async function logCommand(args2) {
693
+ const name = args2[0];
694
+ if (!name) {
695
+ console.error(renderError("Missing process name", "Usage: fennec log <name> [options]"));
696
+ process.exit(1);
697
+ }
698
+ const linesIndex = args2.indexOf("--lines");
699
+ const lines = linesIndex !== -1 ? parseInt(args2[linesIndex + 1], 10) : 50;
700
+ const levelIndex = args2.indexOf("--level");
701
+ const level = levelIndex !== -1 ? args2[levelIndex + 1] : void 0;
702
+ const followFlag = args2.includes("-f") || args2.includes("--follow");
703
+ console.error(`
704
+ ${symbols.fox} ${pc4.bold("Logs")} ${renderAppName(name)} ${pc4.dim(`(last ${lines} lines)`)}
705
+ `);
706
+ const mockLogs = [
707
+ { time: "12:00:00", level: "INFO", msg: "Server started on port 3000" },
708
+ { time: "12:00:01", level: "INFO", msg: "Database connected" },
709
+ { time: "12:00:02", level: "WARN", msg: "Deprecation: use express@5" },
710
+ { time: "12:00:05", level: "ERROR", msg: "POST /api/login \u2192 500" },
711
+ { time: "12:00:06", level: "ERROR", msg: "JWT_SECRET not set in environment" }
712
+ ];
713
+ for (const log of mockLogs) {
714
+ const time = pc4.dim(`[${log.time}]`);
715
+ const lvl = logLevel(log.level);
716
+ const msg = log.level === "ERROR" ? pc4.red(log.msg) : log.level === "WARN" ? pc4.yellow(log.msg) : log.msg;
717
+ console.error(` ${time} ${lvl} ${msg}`);
718
+ }
719
+ if (followFlag) {
720
+ console.error(`
721
+ ${pc4.dim("Following... (Ctrl+C to stop)")}
722
+ `);
723
+ await new Promise(() => {
724
+ });
725
+ }
726
+ console.error();
727
+ }
728
+ async function killCommand(args2) {
729
+ const name = args2[0];
730
+ if (!name) {
731
+ console.error(renderError("Missing process name", "Usage: fennec kill <name>"));
732
+ process.exit(1);
733
+ }
734
+ const signalIndex = args2.indexOf("--signal");
735
+ const signalRaw = signalIndex !== -1 ? args2[signalIndex + 1] : "SIGTERM";
736
+ const signal = signalRaw ?? "SIGTERM";
737
+ const confirmed = await confirmPrompt(
738
+ `Stop ${pc4.bold(name)} with ${pc4.yellow(signal)}?`,
739
+ false
740
+ );
741
+ if (!confirmed) {
742
+ console.error(` ${pc4.dim("Cancelled")}`);
743
+ return;
744
+ }
745
+ console.error(`
746
+ ${pc4.green("\u2713")} ${renderAppName(name)} ${pc4.dim(`stopped (${signal})`)}
747
+ `);
748
+ }
749
+ async function restartCommand(args2) {
750
+ const name = args2[0];
751
+ if (!name) {
752
+ console.error(renderError("Missing process name", "Usage: fennec restart <name>"));
753
+ process.exit(1);
754
+ }
755
+ const spinner = createSpinner(`Restarting ${name}...`);
756
+ await new Promise((resolve3) => setTimeout(resolve3, 1e3));
757
+ spinner.succeed(`${name} restarted`);
758
+ }
759
+ async function attachCommand(args2) {
760
+ const port = parseInt(args2[0], 10);
761
+ if (isNaN(port)) {
762
+ console.error(renderError("Invalid port", "Usage: fennec attach <port> --name <name>"));
763
+ process.exit(1);
764
+ }
765
+ const nameIndex = args2.indexOf("--name");
766
+ const rawName = nameIndex !== -1 ? args2[nameIndex + 1] : void 0;
767
+ const name = rawName ?? `port-${port}`;
768
+ const spinner = createSpinner(`Attaching to :${port}...`);
769
+ try {
770
+ const { PortDetector } = await import("@plumpslabs/fennec-core");
771
+ const detector = new PortDetector();
772
+ const info = detector.detectByPort(port);
773
+ if (info) {
774
+ spinner.succeed(`Attached to :${port}`);
775
+ console.error(` ${renderKV("Name", renderAppName(name))}`);
776
+ console.error(` ${renderKV("PID", String(info.pid))}`);
777
+ console.error(` ${renderKV("Command", info.command || pc4.dim("unknown"))}`);
778
+ } else {
779
+ spinner.fail(`No process found on port ${port}`);
780
+ process.exit(1);
781
+ }
782
+ } catch (error) {
783
+ spinner.fail(`Failed to attach to :${port}`);
784
+ console.error(renderError("Error", String(error)));
785
+ process.exit(1);
786
+ }
250
787
  }
251
788
  async function sessionsCommand() {
252
789
  const store = new SessionStore("./.fennec/sessions");
253
790
  const sessions = store.list();
254
791
  if (sessions.length === 0) {
255
- console.log("No saved sessions found.");
792
+ console.error(`
793
+ ${pc4.dim("No saved sessions found.")}
794
+ `);
256
795
  return;
257
796
  }
258
- console.log("Saved sessions:");
259
- for (const s of sessions) {
260
- console.log(` ${s.name} at ${s.origin} (saved: ${new Date(s.savedAt).toLocaleString()})`);
261
- }
262
- console.log(`
263
- Total: ${sessions.length} session(s)`);
797
+ const columns = [
798
+ { key: "name", label: "Name", format: (v) => pc4.bold(String(v)) },
799
+ { key: "origin", label: "Origin" },
800
+ { key: "savedAt", label: "Saved", format: (v) => pc4.dim(String(v)) }
801
+ ];
802
+ const rows = sessions.map((s) => ({
803
+ name: s.name,
804
+ origin: s.origin,
805
+ savedAt: new Date(s.savedAt).toLocaleString()
806
+ }));
807
+ console.error(`
808
+ ${symbols.fox} ${pc4.bold("Saved Sessions")}
809
+ `);
810
+ console.error(renderTable(columns, rows));
811
+ console.error(` ${pc4.dim(`${sessions.length} session(s)`)}
812
+ `);
264
813
  }
265
814
  async function setupCommand() {
266
- console.log("\n Fennec Setup\n");
815
+ console.error(`
816
+ ${symbols.fox} ${pc4.bold("Fennec Setup")}
817
+ `);
267
818
  const mcpClient = await selectPrompt("Which MCP client are you using?", [
268
- { value: "claude", label: "Claude Desktop" },
269
- { value: "other", label: "Other MCP client" }
819
+ { value: "claude", label: "Claude Desktop", description: "Anthropic's AI desktop app" },
820
+ { value: "cursor", label: "Cursor", description: "AI-powered code editor" },
821
+ { value: "cline", label: "Cline", description: "VS Code MCP client" },
822
+ { value: "other", label: "Other MCP client", description: "Any MCP-compatible client" }
270
823
  ]);
271
- if (isCancel(mcpClient)) {
272
- console.log("Setup cancelled");
824
+ if (!mcpClient) {
825
+ console.error(` ${pc4.dim("Setup cancelled.")}
826
+ `);
273
827
  return;
274
828
  }
275
- if (mcpClient === "claude") {
276
- console.log(`
277
- To configure Claude Desktop for Fennec, add to your config:
278
-
279
- {
829
+ console.error(`
830
+ ${pc4.green("\u2713")} Selected: ${pc4.bold(mcpClient)}
831
+ `);
832
+ const configSnippet = `{
280
833
  "mcpServers": {
281
834
  "fennec": {
282
835
  "command": "fennec",
283
836
  "args": ["start"]
284
837
  }
285
838
  }
286
- }
287
- `);
288
- }
289
- console.log("Setup complete! Run 'fennec start' to begin.\n");
839
+ }`;
840
+ console.error(` ${pc4.bold("Add this to your MCP client config:")}
841
+ `);
842
+ console.error(` ${pc4.dim("```")}`);
843
+ console.error(configSnippet.split("\n").map((l) => ` ${l}`).join("\n"));
844
+ console.error(` ${pc4.dim("```")}
845
+ `);
846
+ console.error(` ${renderSuccess("Setup complete!")} ${pc4.dim("Run")} ${renderCommand("fennec start")} ${pc4.dim("to begin.")}
847
+ `);
290
848
  }
291
849
  async function installBrowsersCommand() {
292
- console.log("\n Installing browser engines\n");
293
- const s = simpleSpinner("Installing Chromium...");
850
+ console.error(`
851
+ ${pc4.bold("Installing Browser Engines")}
852
+ `);
853
+ const spinner = createSpinner("Installing Chromium...");
294
854
  try {
295
855
  execSync("npx playwright install chromium", {
296
- stdio: "inherit",
856
+ stdio: "pipe",
297
857
  timeout: 12e4
298
858
  });
299
- s.stop("Chromium installed successfully");
859
+ spinner.succeed("Chromium installed successfully");
300
860
  } catch {
301
- s.stop("Failed to install Chromium");
302
- console.error("Try running manually: npx playwright install chromium");
861
+ spinner.fail("Failed to install Chromium");
862
+ console.error(` ${pc4.yellow("\u2192")} Try running: ${renderCommand("npx playwright install chromium")}`);
303
863
  }
304
- console.log("\nBrowser installation complete.\n");
864
+ console.error(`
865
+ ${pc4.green("\u2713")} Browser installation complete.
866
+ `);
305
867
  }
306
868
  async function initCommand() {
307
- console.log("\n Initialize Fennec Configuration\n");
869
+ console.error(`
870
+ ${pc4.bold("Initialize Fennec Configuration")}
871
+ `);
308
872
  const configFile = resolve2("./fennec.config.yaml");
309
873
  if (existsSync2(configFile)) {
310
874
  const overwrite = await confirmPrompt(
311
- "fennec.config.yaml already exists. Overwrite?",
875
+ `${pc4.yellow("fennec.config.yaml")} already exists. Overwrite?`,
312
876
  false
313
877
  );
314
878
  if (!overwrite) {
315
- console.log("Cancelled");
879
+ console.error(` ${pc4.dim("Cancelled.")}
880
+ `);
316
881
  return;
317
882
  }
318
883
  }
884
+ const spinner = createSpinner("Generating configuration...");
319
885
  const config = `# Fennec Configuration
320
886
  # Generated by 'fennec init'
321
887
 
@@ -390,11 +956,13 @@ logging:
390
956
  file: null
391
957
  `;
392
958
  writeFileSync(configFile, config, "utf-8");
393
- console.log(`Configuration written to ${configFile}
959
+ spinner.succeed(`Configuration written to ${pc4.bold(configFile)}`);
960
+ console.error(`
961
+ ${pc4.dim("Edit the file to customize Fennec behavior.")}
394
962
  `);
395
963
  }
396
964
  main().catch((error) => {
397
- console.error("Fatal error:", error);
965
+ console.error(renderError("Fatal error", String(error)));
398
966
  process.exit(1);
399
967
  });
400
968
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/utils/banner.ts","../src/utils/help.ts","../src/commands/pipe.ts","../src/commands/attach-pid.ts","../src/commands/attach-port.ts","../src/commands/watch.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { existsSync, writeFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { createInterface } from \"node:readline\";\nimport { execSync } from \"node:child_process\";\nimport { FennecServer, SessionStore } from \"@plumpslabs/fennec-core\";\nimport { printBanner } from \"./utils/banner.js\";\nimport { showHelp } from \"./utils/help.js\";\nimport { pipeCommand } from \"./commands/pipe.js\";\nimport { attachPidCommand } from \"./commands/attach-pid.js\";\nimport { attachPortCommand } from \"./commands/attach-port.js\";\nimport { watchCommand } from \"./commands/watch.js\";\n\nconst [, , command, ...args] = process.argv;\n\n// โ”€โ”€โ”€ Minimal readline-based prompt utilities โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n\nfunction rlQuestion(query: string): Promise<string> {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n return new Promise((resolve) => {\n rl.question(query, (answer) => {\n rl.close();\n resolve(answer.trim());\n });\n });\n}\n\nasync function confirmPrompt(message: string, defaultValue = false): Promise<boolean> {\n const hint = defaultValue ? \"Y/n\" : \"y/N\";\n const answer = await rlQuestion(`\\n${message} (${hint}): `);\n if (!answer) return defaultValue;\n return answer.toLowerCase() === \"y\" || answer.toLowerCase() === \"yes\";\n}\n\nasync function selectPrompt<T extends string>(\n message: string,\n options: { value: T; label: string }[],\n): Promise<T | symbol> {\n console.log(`\\n${message}\\n`);\n options.forEach((opt, i) => {\n console.log(` ${i + 1}) ${opt.label}`);\n });\n console.log(` 0) Cancel`);\n const answer = await rlQuestion(`\\nEnter number (0-${options.length}): `);\n const num = parseInt(answer, 10);\n if (isNaN(num) || num === 0) return Symbol(\"cancel\");\n const selected = options[num - 1];\n if (!selected) {\n console.log(\" Invalid selection. Cancelled.\");\n return Symbol(\"cancel\");\n }\n return selected.value;\n}\n\nfunction simpleSpinner(text: string) {\n const frames = [\"โ ‹\", \"โ ™\", \"โ น\", \"โ ธ\", \"โ ผ\", \"โ ด\", \"โ ฆ\", \"โ ง\", \"โ ‡\", \"โ \"];\n let i = 0;\n const interval = setInterval(() => {\n process.stdout.write(`\\r${frames[i]} ${text}`);\n i = (i + 1) % frames.length;\n }, 80);\n return {\n stop(message?: string) {\n clearInterval(interval);\n process.stdout.write(`\\r${message ? \"โœ“\" : \" \"} ${message ?? text}\\n`);\n },\n };\n}\n\nconst isCancel = (v: unknown): boolean => v === Symbol(\"cancel\");\n\n// โ”€โ”€โ”€ Main โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n\nasync function main(): Promise<void> {\n if (!command || command === \"start\") {\n await startServer(args);\n } else if (command === \"pipe\") {\n await pipeCommand(args);\n } else if (command === \"attach-pid\") {\n await attachPidCommand(args);\n } else if (command === \"attach-port\") {\n await attachPortCommand(args);\n } else if (command === \"watch\") {\n await watchCommand(args);\n } else if (command === \"sessions\") {\n await sessionsCommand();\n } else if (command === \"setup\") {\n await setupCommand();\n } else if (command === \"install-browsers\") {\n printBanner();\n await installBrowsersCommand();\n } else if (command === \"init\") {\n printBanner();\n await initCommand();\n } else if (command === \"help\" || command === \"--help\" || command === \"-h\") {\n printBanner();\n showHelp();\n } else {\n console.error(`Unknown command: ${command}`);\n console.error(\"Run 'fennec help' for usage information\");\n process.exit(1);\n }\n}\n\nasync function startServer(args: string[]): Promise<void> {\n const configIndex = args.indexOf(\"--config\");\n const configPath = configIndex !== -1 ? args[configIndex + 1] : undefined;\n const server = new FennecServer(configPath);\n await server.start();\n}\n\nasync function sessionsCommand(): Promise<void> {\n const store = new SessionStore(\"./.fennec/sessions\");\n const sessions = store.list();\n\n if (sessions.length === 0) {\n console.log(\"No saved sessions found.\");\n return;\n }\n\n console.log(\"Saved sessions:\");\n for (const s of sessions) {\n console.log(` ${s.name} at ${s.origin} (saved: ${new Date(s.savedAt).toLocaleString()})`);\n }\n console.log(`\\nTotal: ${sessions.length} session(s)`);\n}\n\nasync function setupCommand(): Promise<void> {\n console.log(\"\\n Fennec Setup\\n\");\n\n const mcpClient = await selectPrompt(\"Which MCP client are you using?\", [\n { value: \"claude\", label: \"Claude Desktop\" },\n { value: \"other\", label: \"Other MCP client\" },\n ]);\n\n if (isCancel(mcpClient)) {\n console.log(\"Setup cancelled\");\n return;\n }\n\n if (mcpClient === \"claude\") {\n console.log(`\nTo configure Claude Desktop for Fennec, add to your config:\n\n{\n \"mcpServers\": {\n \"fennec\": {\n \"command\": \"fennec\",\n \"args\": [\"start\"]\n }\n }\n}\n `);\n }\n\n console.log(\"Setup complete! Run 'fennec start' to begin.\\n\");\n}\n\nasync function installBrowsersCommand(): Promise<void> {\n console.log(\"\\n Installing browser engines\\n\");\n\n const s = simpleSpinner(\"Installing Chromium...\");\n\n try {\n execSync(\"npx playwright install chromium\", {\n stdio: \"inherit\",\n timeout: 120000,\n });\n s.stop(\"Chromium installed successfully\");\n } catch {\n s.stop(\"Failed to install Chromium\");\n console.error(\"Try running manually: npx playwright install chromium\");\n }\n\n console.log(\"\\nBrowser installation complete.\\n\");\n}\n\nasync function initCommand(): Promise<void> {\n console.log(\"\\n Initialize Fennec Configuration\\n\");\n\n const configFile = resolve(\"./fennec.config.yaml\");\n\n if (existsSync(configFile)) {\n const overwrite = await confirmPrompt(\n \"fennec.config.yaml already exists. Overwrite?\",\n false,\n );\n\n if (!overwrite) {\n console.log(\"Cancelled\");\n return;\n }\n }\n\n const config = `# Fennec Configuration\n# Generated by 'fennec init'\n\nbrowser:\n type: chromium\n headless: true\n defaultTimeout: 30000\n viewport:\n width: 1280\n height: 720\n\nsession:\n maxSessions: 10\n idleTimeoutSecs: 1800\n persistPath: \".fennec/sessions\"\n\nprocess:\n maxProcesses: 10\n logBufferLines: 2000\n spawnAllowlist:\n - \"npm\"\n - \"node\"\n - \"pnpm\"\n - \"yarn\"\n - \"bun\"\n - \"python\"\n - \"python3\"\n\nterminal:\n logBufferLines: 2000\n watchDebounceMs: 50\n\nnetwork:\n bufferSize: 1000\n captureRequestBody: true\n captureResponseBody: true\n slowRequestThresholdMs: 1000\n\nconsole:\n bufferSize: 500\n levels:\n - log\n - info\n - warn\n - error\n - debug\n\ncorrelation:\n windowMs: 500\n enableRootCauseInference: true\n minConfidence: 0.7\n\nsecurity:\n sandbox: true\n allowProcessSpawn: true\n allowProcessKill: false\n allowedDomains: []\n blockedDomains: []\n allowFileProtocol: false\n allowCDPRawAccess: false\n exportPath: \".fennec/exports\"\n maxExportSizeMB: 10\n\ntransport:\n type: stdio\n port: 3333\n host: \"127.0.0.1\"\n\nlogging:\n level: info\n format: pretty\n file: null\n`;\n\n writeFileSync(configFile, config, \"utf-8\");\n console.log(`Configuration written to ${configFile}\\n`);\n}\n\nmain().catch((error) => {\n console.error(\"Fatal error:\", error);\n process.exit(1);\n});\n","export function printBanner(): void {\n console.error(`\n /\\\\ /\\\\\n ( o o ) fennec v1.9.0\n =( Y )= ears everywhere in your stack.\n ) (\n `);\n}\n","export function showHelp(): void {\n console.error(`\nUsage: fennec <command> [options]\n\nCommands:\n start Start the MCP server (default)\n --transport Transport type (stdio | sse) [default: stdio]\n --port SSE port [default: 3333]\n --config Path to config file\n\n pipe Pipe stdin to log watcher\n --name Watcher name (required)\n\n attach-pid Attach to a running process by PID\n <pid> Process ID (required)\n --name Name for the process\n\n attach-port Attach to a process by port\n <port> Port number (required)\n --name Name for the process\n\n watch Watch a log file\n --file File path (required)\n --name Watcher name\n\n sessions List saved auth sessions\n setup Configure MCP client (Claude Desktop)\n install-browsers Install Playwright browser engines\n init Generate fennec.config.yaml\n\n help Show this help message\n `);\n}\n","export async function pipeCommand(args: string[]): Promise<void> {\n const nameIndex = args.indexOf(\"--name\");\n const name = nameIndex !== -1 ? args[nameIndex + 1] : \"pipe\";\n\n if (!name) {\n console.error(\"Error: --name is required for pipe command\");\n process.exit(1);\n }\n\n const { PipeWatcher } = await import(\"@plumpslabs/fennec-core\");\n const watcher = new PipeWatcher();\n const { write } = watcher.createPipe(name);\n\n console.error(`Pipe watcher '${name}' active. Forwarding stdin...`);\n\n const onDrain = () => {\n process.stdin.resume();\n };\n process.stdout.on(\"drain\", onDrain);\n\n process.stdin.setEncoding(\"utf-8\");\n process.stdin.on(\"data\", (data: string) => {\n try {\n write(data);\n const canContinue = process.stdout.write(data);\n if (!canContinue) {\n process.stdin.pause();\n }\n } catch (error) {\n console.error(\"Pipe error:\", error);\n }\n });\n\n process.stdin.on(\"error\", (error) => {\n console.error(\"Pipe stdin error:\", error);\n });\n\n process.stdin.on(\"end\", () => {\n console.error(`Pipe watcher '${name}' ended.`);\n watcher.cleanup();\n });\n\n const shutdown = () => {\n watcher.cleanup();\n process.stdout.removeListener(\"drain\", onDrain);\n process.stdin.removeAllListeners();\n process.exit(0);\n };\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n}\n","export async function attachPidCommand(args: string[]): Promise<void> {\n const pid = parseInt(args[0]!, 10);\n if (isNaN(pid)) {\n console.error(\"Error: valid PID is required\");\n process.exit(1);\n }\n const { PortDetector } = await import(\"@plumpslabs/fennec-core\");\n const detector = new PortDetector();\n const info = detector.detectByPid(pid);\n if (info) {\n console.log(`Attached to PID ${pid}${info.command ? ` (${info.command})` : \"\"}`);\n if (info.port) console.log(` Port: ${info.port}`);\n } else {\n console.error(`Could not find process with PID ${pid}`);\n process.exit(1);\n }\n}\n","export async function attachPortCommand(args: string[]): Promise<void> {\n const port = parseInt(args[0]!, 10);\n if (isNaN(port)) {\n console.error(\"Error: valid port number is required\");\n process.exit(1);\n }\n const { PortDetector } = await import(\"@plumpslabs/fennec-core\");\n const detector = new PortDetector();\n const info = detector.detectByPort(port);\n if (info) {\n console.log(`Found process on port ${port}: PID ${info.pid}${info.command ? ` (${info.command})` : \"\"}`);\n } else {\n console.error(`No process found listening on port ${port}`);\n process.exit(1);\n }\n}\n","import { existsSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\n\nexport async function watchCommand(args: string[]): Promise<void> {\n const fileIndex = args.indexOf(\"--file\");\n const filePath = fileIndex !== -1 ? args[fileIndex + 1] : undefined;\n if (!filePath) {\n console.error(\"Error: --file is required for watch command\");\n process.exit(1);\n }\n const nameIndex = args.indexOf(\"--name\");\n const name = nameIndex !== -1 ? args[nameIndex + 1] : undefined;\n const resolvedPath = resolve(filePath);\n if (!existsSync(resolvedPath)) {\n console.error(`Error: File not found: ${resolvedPath}`);\n process.exit(1);\n }\n const { LogWatcher } = await import(\"@plumpslabs/fennec-core\");\n const watcher = new LogWatcher();\n const watcherId = watcher.watchFile(resolvedPath, name);\n console.log(`Watching file: ${resolvedPath}`);\n console.log(`Watcher ID: ${watcherId}`);\n process.stdin.resume();\n}\n"],"mappings":"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","names":["existsSync","resolve","args","args","args","args","resolve","args","existsSync"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/utils/banner.ts","../src/utils/help.ts","../src/commands/pipe.ts","../src/commands/attach-pid.ts","../src/commands/attach-port.ts","../src/commands/watch.ts","../src/utils/format.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { existsSync, writeFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { execSync } from \"node:child_process\";\nimport { FennecServer, SessionStore, ProcessManager } from \"@plumpslabs/fennec-core\";\nimport pc from \"picocolors\";\nimport { printBanner } from \"./utils/banner.js\";\nimport { showHelp } from \"./utils/help.js\";\nimport { pipeCommand } from \"./commands/pipe.js\";\nimport { attachPidCommand } from \"./commands/attach-pid.js\";\nimport { attachPortCommand } from \"./commands/attach-port.js\";\nimport { watchCommand } from \"./commands/watch.js\";\nimport {\n symbols,\n renderTable,\n renderKV,\n renderError,\n renderSuccess,\n renderCommand,\n renderAppName,\n statusBadge,\n logLevel,\n timestamp,\n divider,\n createSpinner,\n selectPrompt,\n confirmPrompt,\n type Column,\n type Row,\n type ProcessStatus,\n} from \"./utils/format.js\";\n\nconst [, , command, ...args] = process.argv;\n\n// โ”€โ”€โ”€ Main โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n\nasync function main(): Promise<void> {\n if (!command || command === \"start\") {\n await startServer(args);\n } else if (command === \"run\") {\n await runCommand(args);\n } else if (command === \"status\" || command === \"ps\") {\n await statusCommand(args);\n } else if (command === \"log\") {\n await logCommand(args);\n } else if (command === \"kill\") {\n await killCommand(args);\n } else if (command === \"restart\") {\n await restartCommand(args);\n } else if (command === \"attach\") {\n await attachCommand(args);\n } else if (command === \"pipe\") {\n await pipeCommand(args);\n } else if (command === \"attach-pid\") {\n await attachPidCommand(args);\n } else if (command === \"attach-port\") {\n await attachPortCommand(args);\n } else if (command === \"watch\") {\n await watchCommand(args);\n } else if (command === \"sessions\") {\n await sessionsCommand();\n } else if (command === \"setup\") {\n await setupCommand();\n } else if (command === \"install-browsers\") {\n printBanner();\n await installBrowsersCommand();\n } else if (command === \"init\") {\n printBanner();\n await initCommand();\n } else if (command === \"version\" || command === \"--version\" || command === \"-v\") {\n console.log(` ${symbols.fox} ${pc.bold(\"Fennec\")} ${pc.dim(\"v1.11.0\")}`);\n } else if (command === \"help\" || command === \"--help\" || command === \"-h\") {\n console.log(pc.dim(\"\\n Use 'fennec help' for more information.\\n\"));\n printBanner();\n showHelp();\n } else {\n console.error(renderError(`Unknown command: ${command}`, \"Run 'fennec help' for usage information\"));\n process.exit(1);\n }\n}\n\n// โ”€โ”€โ”€ Command: start โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n\nasync function startServer(args: string[]): Promise<void> {\n printBanner();\n console.error(` ${pc.dim(\"Starting Fennec MCP server...\")}\\n`);\n\n const configIndex = args.indexOf(\"--config\");\n const configPath = configIndex !== -1 ? args[configIndex + 1] : undefined;\n\n const apiPortIndex = args.indexOf(\"--api-port\");\n const apiPort = apiPortIndex !== -1 ? parseInt(args[apiPortIndex + 1]!, 10) : 3456;\n\n try {\n const server = new FennecServer(configPath);\n await server.start();\n\n console.error(`\\n ${pc.green(\"โœ“\")} ${pc.bold(\"Fennec server is running\")}`);\n console.error(` ${renderKV(\"Transport\", \"stdio\")}`);\n console.error(` ${renderKV(\"Management API\", `http://localhost:${apiPort}`)}`);\n console.error(` ${renderKV(\"AI Agent\", \"Connect via MCP protocol\")}`);\n console.error(`\\n ${pc.dim(\"Press Ctrl+C to stop\")}\\n`);\n } catch (error) {\n console.error(renderError(\"Failed to start server\", String(error)));\n process.exit(1);\n }\n}\n\n// โ”€โ”€โ”€ Command: run โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n\nasync function runCommand(args: string[]): Promise<void> {\n const nameIndex = args.indexOf(\"--name\");\n const name = nameIndex !== -1 ? args[nameIndex + 1] : undefined;\n const cwdIndex = args.indexOf(\"--cwd\");\n const cwd = cwdIndex !== -1 ? args[cwdIndex + 1] : undefined;\n const restartFlag = args.includes(\"--restart\");\n\n // Extract command (everything before --name or --cwd)\n const cmdEnd = Math.min(\n nameIndex !== -1 ? nameIndex : Infinity,\n cwdIndex !== -1 ? cwdIndex : Infinity,\n );\n const cmdParts = args.slice(0, cmdEnd);\n const cmd = cmdParts.join(\" \");\n\n if (!cmd) {\n console.error(renderError(\"Missing command\", \"Usage: fennec run <command> --name <name>\"));\n process.exit(1);\n }\n\n const appName = name ?? cmdParts[0] ?? \"app\";\n\n console.error(`\\n ${symbols.fox} ${pc.bold(\"Running\")} ${renderAppName(appName)}\\n`);\n console.error(` ${renderKV(\"Command\", cmd)}`);\n if (cwd) console.error(` ${renderKV(\"Directory\", cwd)}`);\n console.error(` ${renderKV(\"Restart\", restartFlag ? \"on crash\" : \"off\")}`);\n console.error(` ${divider()}`);\n\n // Spawn process using ProcessManager\n const config = {\n maxProcesses: 10,\n logBufferLines: 2000,\n spawnAllowlist: [],\n };\n const pm = new ProcessManager(config);\n\n try {\n const proc = pm.spawn(cmdParts[0]!, cmdParts.slice(1), cwd, undefined, appName);\n\n console.error(`\\n ${pc.green(\"โ—\")} ${pc.bold(appName)} ${pc.dim(`started (PID: ${proc.pid})`)}\\n`);\n\n // Forward logs to console\n const stdout = proc.child.stdout;\n const stderr = proc.child.stderr;\n\n if (stdout) {\n stdout.on(\"data\", (data: Buffer) => {\n const lines = data.toString().split(\"\\n\").filter(Boolean);\n for (const line of lines) {\n const level = line.includes(\"error\") || line.includes(\"Error\") ? \"error\" as const\n : line.includes(\"warn\") || line.includes(\"WARN\") ? \"warn\" as const\n : \"info\" as const;\n const time = timestamp();\n const lvl = logLevel(level);\n process.stdout.write(` ${time} ${lvl} ${line}\\n`);\n }\n });\n }\n\n if (stderr) {\n stderr.on(\"data\", (data: Buffer) => {\n const lines = data.toString().split(\"\\n\").filter(Boolean);\n for (const line of lines) {\n const time = timestamp();\n const lvl = logLevel(\"error\");\n process.stderr.write(` ${time} ${lvl} ${pc.red(line)}\\n`);\n }\n });\n }\n\n // Handle process exit\n proc.child.on(\"exit\", (code, signal) => {\n if (restartFlag && code !== 0) {\n console.error(`\\n ${pc.yellow(\"โš \")} ${appName} ${pc.dim(`exited (${code}), restarting...`)}`);\n pm.restart(appName).catch(() => {});\n } else {\n console.error(`\\n ${pc.dim(\"โ—‹\")} ${pc.bold(appName)} ${pc.dim(`exited (${code})`)}`);\n }\n });\n\n // Keep running\n await new Promise(() => {});\n } catch (error) {\n console.error(renderError(`Failed to run ${appName}`, String(error)));\n process.exit(1);\n }\n}\n\n// โ”€โ”€โ”€ Command: status โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n\nasync function statusCommand(_args: string[]): Promise<void> {\n const watchFlag = _args.includes(\"-w\") || _args.includes(\"--watch\");\n\n if (watchFlag) {\n await watchStatus();\n return;\n }\n\n const columns: Column[] = [\n { key: \"name\", label: \"Name\", format: (v) => pc.bold(String(v)) },\n { key: \"source\", label: \"Source\" },\n { key: \"status\", label: \"Status\", format: (v) => statusBadge(v as ProcessStatus) },\n { key: \"pid\", label: \"PID\" },\n { key: \"uptime\", label: \"Uptime\" },\n { key: \"error\", label: \"Last Error\", format: (v) => v ? pc.red(String(v)) : pc.dim(\"-\") },\n ];\n\n // In a real scenario, this would connect to the Fennec server API\n // For now, show a static example or basic process list\n const rows: Row[] = [\n { name: \"backend\", source: \":3000\", status: \"running\", pid: \"12345\", uptime: \"2h 15m\", error: null },\n { name: \"frontend\", source: \":5173\", status: \"running\", pid: \"12346\", uptime: \"2h 15m\", error: null },\n { name: \"database\", source: \":5432\", status: \"running\", pid: \"docker\", uptime: \"5h 30m\", error: null },\n ];\n\n console.error(`\\n ${symbols.fox} ${pc.bold(\"Observed Processes\")}\\n`);\n console.error(renderTable(columns, rows));\n console.error();\n}\n\nasync function watchStatus(): Promise<void> {\n console.error(`\\n ${pc.bold(\"Watching status\")} ${pc.dim(\"(Ctrl+C to stop)\")}\\n`);\n\n const render = () => {\n const columns: Column[] = [\n { key: \"name\", label: \"Name\", format: (v) => pc.bold(String(v)) },\n { key: \"status\", label: \"Status\", format: (v) => statusBadge(v as ProcessStatus) },\n { key: \"pid\", label: \"PID\" },\n { key: \"uptime\", label: \"Uptime\" },\n { key: \"cpu\", label: \"CPU\" },\n { key: \"mem\", label: \"Memory\" },\n ];\n\n const rows: Row[] = [\n { name: \"backend\", status: \"running\", pid: \"12345\", uptime: \"2h 15m\", cpu: \"1.2%\", mem: \"128MB\" },\n { name: \"frontend\", status: \"running\", pid: \"12346\", uptime: \"2h 15m\", cpu: \"0.8%\", mem: \"64MB\" },\n ];\n\n return renderTable(columns, rows, { compact: true });\n };\n\n console.error(render());\n const interval = setInterval(() => {\n process.stdout.write(\"\\x1B[6A\"); // Move cursor up\n console.error(render());\n }, 2000);\n\n process.on(\"SIGINT\", () => {\n clearInterval(interval);\n process.exit(0);\n });\n\n await new Promise(() => {});\n}\n\n// โ”€โ”€โ”€ Command: log โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n\nasync function logCommand(args: string[]): Promise<void> {\n const name = args[0];\n if (!name) {\n console.error(renderError(\"Missing process name\", \"Usage: fennec log <name> [options]\"));\n process.exit(1);\n }\n\n const linesIndex = args.indexOf(\"--lines\");\n const lines = linesIndex !== -1 ? parseInt(args[linesIndex + 1]!, 10) : 50;\n\n const levelIndex = args.indexOf(\"--level\");\n const level = levelIndex !== -1 ? args[levelIndex + 1] : undefined;\n\n const followFlag = args.includes(\"-f\") || args.includes(\"--follow\");\n\n console.error(`\\n ${symbols.fox} ${pc.bold(\"Logs\")} ${renderAppName(name)} ${pc.dim(`(last ${lines} lines)`)}\\n`);\n\n // Placeholder: in production, this would fetch from the server API\n const mockLogs = [\n { time: \"12:00:00\", level: \"INFO\" as const, msg: \"Server started on port 3000\" },\n { time: \"12:00:01\", level: \"INFO\" as const, msg: \"Database connected\" },\n { time: \"12:00:02\", level: \"WARN\" as const, msg: \"Deprecation: use express@5\" },\n { time: \"12:00:05\", level: \"ERROR\" as const, msg: \"POST /api/login โ†’ 500\" },\n { time: \"12:00:06\", level: \"ERROR\" as const, msg: \"JWT_SECRET not set in environment\" },\n ];\n\n for (const log of mockLogs) {\n const time = pc.dim(`[${log.time}]`);\n const lvl = logLevel(log.level);\n const msg = log.level === \"ERROR\" ? pc.red(log.msg) : log.level === \"WARN\" ? pc.yellow(log.msg) : log.msg;\n console.error(` ${time} ${lvl} ${msg}`);\n }\n\n if (followFlag) {\n console.error(`\\n ${pc.dim(\"Following... (Ctrl+C to stop)\")}\\n`);\n await new Promise(() => {});\n }\n\n console.error();\n}\n\n// โ”€โ”€โ”€ Command: kill โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n\nasync function killCommand(args: string[]): Promise<void> {\n const name = args[0];\n if (!name) {\n console.error(renderError(\"Missing process name\", \"Usage: fennec kill <name>\"));\n process.exit(1);\n }\n\n const signalIndex = args.indexOf(\"--signal\");\n const signalRaw = signalIndex !== -1 ? args[signalIndex + 1] : \"SIGTERM\";\n const signal = (signalRaw ?? \"SIGTERM\") as NodeJS.Signals;\n\n const confirmed = await confirmPrompt(\n `Stop ${pc.bold(name)} with ${pc.yellow(signal)}?`,\n false,\n );\n\n if (!confirmed) {\n console.error(` ${pc.dim(\"Cancelled\")}`);\n return;\n }\n\n // Placeholder: would connect to Fennec server API\n console.error(`\\n ${pc.green(\"โœ“\")} ${renderAppName(name)} ${pc.dim(`stopped (${signal})`)}\\n`);\n}\n\n// โ”€โ”€โ”€ Command: restart โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n\nasync function restartCommand(args: string[]): Promise<void> {\n const name = args[0];\n if (!name) {\n console.error(renderError(\"Missing process name\", \"Usage: fennec restart <name>\"));\n process.exit(1);\n }\n\n const spinner = createSpinner(`Restarting ${name}...`);\n\n // Placeholder: would connect to Fennec server API\n await new Promise((resolve) => setTimeout(resolve, 1000));\n\n spinner.succeed(`${name} restarted`);\n}\n\n// โ”€โ”€โ”€ Command: attach โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n\nasync function attachCommand(args: string[]): Promise<void> {\n const port = parseInt(args[0]!, 10);\n if (isNaN(port)) {\n console.error(renderError(\"Invalid port\", \"Usage: fennec attach <port> --name <name>\"));\n process.exit(1);\n }\n\n const nameIndex = args.indexOf(\"--name\");\n const rawName = nameIndex !== -1 ? args[nameIndex + 1] : undefined;\n const name = rawName ?? `port-${port}`;\n\n const spinner = createSpinner(`Attaching to :${port}...`);\n\n try {\n const { PortDetector } = await import(\"@plumpslabs/fennec-core\");\n const detector = new PortDetector();\n const info = detector.detectByPort(port);\n\n if (info) {\n spinner.succeed(`Attached to :${port}`);\n console.error(` ${renderKV(\"Name\", renderAppName(name))}`);\n console.error(` ${renderKV(\"PID\", String(info.pid))}`);\n console.error(` ${renderKV(\"Command\", info.command || pc.dim(\"unknown\"))}`);\n } else {\n spinner.fail(`No process found on port ${port}`);\n process.exit(1);\n }\n } catch (error) {\n spinner.fail(`Failed to attach to :${port}`);\n console.error(renderError(\"Error\", String(error)));\n process.exit(1);\n }\n}\n\n// โ”€โ”€โ”€ Command: sessions โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n\nasync function sessionsCommand(): Promise<void> {\n const store = new SessionStore(\"./.fennec/sessions\");\n const sessions = store.list();\n\n if (sessions.length === 0) {\n console.error(`\\n ${pc.dim(\"No saved sessions found.\")}\\n`);\n return;\n }\n\n const columns: Column[] = [\n { key: \"name\", label: \"Name\", format: (v) => pc.bold(String(v)) },\n { key: \"origin\", label: \"Origin\" },\n { key: \"savedAt\", label: \"Saved\", format: (v) => pc.dim(String(v)) },\n ];\n\n const rows: Row[] = sessions.map((s) => ({\n name: s.name,\n origin: s.origin,\n savedAt: new Date(s.savedAt).toLocaleString(),\n }));\n\n console.error(`\\n ${symbols.fox} ${pc.bold(\"Saved Sessions\")}\\n`);\n console.error(renderTable(columns, rows));\n console.error(` ${pc.dim(`${sessions.length} session(s)`)}\\n`);\n}\n\n// โ”€โ”€โ”€ Command: setup โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n\nasync function setupCommand(): Promise<void> {\n console.error(`\\n ${symbols.fox} ${pc.bold(\"Fennec Setup\")}\\n`);\n\n const mcpClient = await selectPrompt(\"Which MCP client are you using?\", [\n { value: \"claude\", label: \"Claude Desktop\", description: \"Anthropic's AI desktop app\" },\n { value: \"cursor\", label: \"Cursor\", description: \"AI-powered code editor\" },\n { value: \"cline\", label: \"Cline\", description: \"VS Code MCP client\" },\n { value: \"other\", label: \"Other MCP client\", description: \"Any MCP-compatible client\" },\n ]);\n\n if (!mcpClient) {\n console.error(` ${pc.dim(\"Setup cancelled.\")}\\n`);\n return;\n }\n\n console.error(`\\n ${pc.green(\"โœ“\")} Selected: ${pc.bold(mcpClient)}\\n`);\n\n const configSnippet = `{\n \"mcpServers\": {\n \"fennec\": {\n \"command\": \"fennec\",\n \"args\": [\"start\"]\n }\n }\n}`;\n\n console.error(` ${pc.bold(\"Add this to your MCP client config:\")}\\n`);\n console.error(` ${pc.dim(\"```\")}`);\n console.error(configSnippet.split(\"\\n\").map((l) => ` ${l}`).join(\"\\n\"));\n console.error(` ${pc.dim(\"```\")}\\n`);\n console.error(` ${renderSuccess(\"Setup complete!\")} ${pc.dim(\"Run\")} ${renderCommand(\"fennec start\")} ${pc.dim(\"to begin.\")}\\n`);\n}\n\n// โ”€โ”€โ”€ Command: install-browsers โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n\nasync function installBrowsersCommand(): Promise<void> {\n console.error(`\\n ${pc.bold(\"Installing Browser Engines\")}\\n`);\n\n const spinner = createSpinner(\"Installing Chromium...\");\n\n try {\n execSync(\"npx playwright install chromium\", {\n stdio: \"pipe\",\n timeout: 120000,\n });\n spinner.succeed(\"Chromium installed successfully\");\n } catch {\n spinner.fail(\"Failed to install Chromium\");\n console.error(` ${pc.yellow(\"โ†’\")} Try running: ${renderCommand(\"npx playwright install chromium\")}`);\n }\n\n console.error(`\\n ${pc.green(\"โœ“\")} Browser installation complete.\\n`);\n}\n\n// โ”€โ”€โ”€ Command: init โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n\nasync function initCommand(): Promise<void> {\n console.error(`\\n ${pc.bold(\"Initialize Fennec Configuration\")}\\n`);\n\n const configFile = resolve(\"./fennec.config.yaml\");\n\n if (existsSync(configFile)) {\n const overwrite = await confirmPrompt(\n `${pc.yellow(\"fennec.config.yaml\")} already exists. Overwrite?`,\n false,\n );\n\n if (!overwrite) {\n console.error(` ${pc.dim(\"Cancelled.\")}\\n`);\n return;\n }\n }\n\n const spinner = createSpinner(\"Generating configuration...\");\n\n const config = `# Fennec Configuration\n# Generated by 'fennec init'\n\nbrowser:\n type: chromium\n headless: true\n defaultTimeout: 30000\n viewport:\n width: 1280\n height: 720\n\nsession:\n maxSessions: 10\n idleTimeoutSecs: 1800\n persistPath: \".fennec/sessions\"\n\nprocess:\n maxProcesses: 10\n logBufferLines: 2000\n spawnAllowlist:\n - \"npm\"\n - \"node\"\n - \"pnpm\"\n - \"yarn\"\n - \"bun\"\n - \"python\"\n - \"python3\"\n\nterminal:\n logBufferLines: 2000\n watchDebounceMs: 50\n\nnetwork:\n bufferSize: 1000\n captureRequestBody: true\n captureResponseBody: true\n slowRequestThresholdMs: 1000\n\nconsole:\n bufferSize: 500\n levels:\n - log\n - info\n - warn\n - error\n - debug\n\ncorrelation:\n windowMs: 500\n enableRootCauseInference: true\n minConfidence: 0.7\n\nsecurity:\n sandbox: true\n allowProcessSpawn: true\n allowProcessKill: false\n allowedDomains: []\n blockedDomains: []\n allowFileProtocol: false\n allowCDPRawAccess: false\n exportPath: \".fennec/exports\"\n maxExportSizeMB: 10\n\ntransport:\n type: stdio\n port: 3333\n host: \"127.0.0.1\"\n\nlogging:\n level: info\n format: pretty\n file: null\n`;\n\n writeFileSync(configFile, config, \"utf-8\");\n\n spinner.succeed(`Configuration written to ${pc.bold(configFile)}`);\n console.error(`\\n ${pc.dim(\"Edit the file to customize Fennec behavior.\")}\\n`);\n}\n\n// โ”€โ”€โ”€ Start โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n\nmain().catch((error) => {\n console.error(renderError(\"Fatal error\", String(error)));\n process.exit(1);\n});\n","import pc from \"picocolors\";\n\n// picocolors 1.x types don't include hex(), but it works at runtime.\n// We expose hexColor for use by the format module.\nexport function hexColor(color: string): (s: string) => string {\n return (pc as any).hex(color);\n}\n\nconst fennecOrange = hexColor(\"#FF6432\");\nconst fennecYellow = hexColor(\"#FFB347\");\n\nconst FOX = `\n ${fennecOrange(\"/\\\\\\\\ /\\\\\\\\\")}\n ${fennecOrange(\"(\")} ${fennecYellow(\"o o\")} ${fennecOrange(\") \") + pc.bold(\"Fennec\")} ${pc.dim(\"v1.10.0\")}\n ${fennecOrange(\"=(\")} ${fennecYellow(\" Y \")} ${fennecOrange(\")=\")} ${pc.dim(\"ears everywhere in your stack.\")}\n ${fennecOrange(\") (\")}\n`;\n\nconst FOX_COMPACT = `${pc.redBright(\" ๐ŸฆŠ\")} ${pc.bold(\"Fennec\")} ${pc.dim(\"v1.10.0\")}`;\n\nexport function printBanner(): void {\n if (process.stdout.isTTY) {\n console.error(FOX);\n } else {\n console.error(FOX_COMPACT);\n }\n}\n\nexport function printMiniBanner(): void {\n console.error(FOX_COMPACT + pc.dim(\" โ€” ears everywhere in your stack.\"));\n}\n\nexport { FOX, FOX_COMPACT };\n","import pc from \"picocolors\";\nimport { hexColor } from \"./banner.js\";\n\nconst fennecOrange = hexColor(\"#FF6432\");\n\nexport function showHelp(): void {\n const sep = fennecOrange(\"โ”€\".repeat(50));\n\n console.error(`\n\n ${pc.bold(\"Usage:\")} fennec ${pc.dim(\"<command>\")} ${pc.dim(\"[options]\")}\n\n ${sep}\n ${pc.bold(\"Server\")}\n ${sep}\n\n ${pc.cyan(\"start\")} ${pc.dim(\"Start the MCP server (default)\")}\n ${pc.dim(\"--config\")} Path to config file\n ${pc.dim(\"--transport\")} Transport type ${pc.dim(\"(stdio | sse) [default: stdio]\")}\n ${pc.dim(\"--port\")} SSE port ${pc.dim(\"[default: 3333]\")}\n ${pc.dim(\"--api-port\")} Management API port ${pc.dim(\"[default: 3456]\")}\n\n ${sep}\n ${pc.bold(\"Process Management\")}\n ${sep}\n\n ${pc.cyan(\"run\")} ${pc.dim(\"<command>\")} ${pc.dim(\"Run a command under Fennec observation\")}\n ${pc.dim(\"--name\")} Process name ${pc.dim(\"(required)\")}\n ${pc.dim(\"--cwd\")} Working directory\n ${pc.dim(\"--restart\")} Auto-restart on crash\n\n ${pc.cyan(\"status\")} ${pc.dim(\"[name]\")} ${pc.dim(\"Show observed processes\")}\n ${pc.dim(\"-w, --watch\")} Watch mode ${pc.dim(\"(refresh every 2s)\")}\n\n ${pc.cyan(\"log\")} ${pc.dim(\"<name>\")} ${pc.dim(\"Show logs for a process\")}\n ${pc.dim(\"--lines\")} Number of lines ${pc.dim(\"[default: 50]\")}\n ${pc.dim(\"--level\")} Filter by level ${pc.dim(\"(error | warn | info)\")}\n ${pc.dim(\"-f, --follow\")} Follow mode ${pc.dim(\"(tail -f)\")}\n\n ${pc.cyan(\"kill\")} ${pc.dim(\"<name>\")} ${pc.dim(\"Stop a process\")}\n ${pc.dim(\"--signal\")} Signal to send ${pc.dim(\"[default: SIGTERM]\")}\n\n ${pc.cyan(\"restart\")} ${pc.dim(\"<name>\")} ${pc.dim(\"Restart a process\")}\n\n ${sep}\n ${pc.bold(\"Observation\")}\n ${sep}\n\n ${pc.cyan(\"attach\")} ${pc.dim(\"<port>\")} ${pc.dim(\"Observe a process by port\")}\n ${pc.dim(\"--name\")} Process name\n\n ${pc.cyan(\"pipe\")} ${pc.dim(\"Pipe stdin to log watcher\")}\n ${pc.dim(\"--name\")} Watcher name ${pc.dim(\"(required)\")}\n\n ${pc.cyan(\"watch\")} ${pc.dim(\"Watch a log file\")}\n ${pc.dim(\"--file\")} File path ${pc.dim(\"(required)\")}\n ${pc.dim(\"--name\")} Watcher name\n\n ${pc.cyan(\"attach-pid\")} ${pc.dim(\"<pid>\")} ${pc.dim(\"Attach to process by PID\")}\n ${pc.cyan(\"attach-port\")} ${pc.dim(\"<port>\")} ${pc.dim(\"Attach to process by port\")}\n\n ${sep}\n ${pc.bold(\"Configuration\")}\n ${sep}\n\n ${pc.cyan(\"init\")} ${pc.dim(\"Generate fennec.config.yaml\")}\n ${pc.cyan(\"setup\")} ${pc.dim(\"Configure MCP client\")}\n ${pc.cyan(\"install-browsers\")} ${pc.dim(\"Install Playwright browser engines\")}\n ${pc.cyan(\"sessions\")} ${pc.dim(\"List saved auth sessions\")}\n\n ${sep}\n ${pc.bold(\"Other\")}\n ${sep}\n\n ${pc.cyan(\"help\")} ${pc.dim(\"Show this help message\")}\n ${pc.cyan(\"version\")} ${pc.dim(\"Show version\")}\n\n ${pc.dim(\"โ”€\".repeat(50))}\n\n ${pc.dim(\"Learn more:\")} ${pc.cyan(\"https://github.com/plumpslabs/fennec\")}\n `);\n}\n","export async function pipeCommand(args: string[]): Promise<void> {\n const nameIndex = args.indexOf(\"--name\");\n const name = nameIndex !== -1 ? args[nameIndex + 1] : \"pipe\";\n\n if (!name) {\n console.error(\"Error: --name is required for pipe command\");\n process.exit(1);\n }\n\n const { PipeWatcher } = await import(\"@plumpslabs/fennec-core\");\n const watcher = new PipeWatcher();\n const { write } = watcher.createPipe(name);\n\n console.error(`Pipe watcher '${name}' active. Forwarding stdin...`);\n\n const onDrain = () => {\n process.stdin.resume();\n };\n process.stdout.on(\"drain\", onDrain);\n\n process.stdin.setEncoding(\"utf-8\");\n process.stdin.on(\"data\", (data: string) => {\n try {\n write(data);\n const canContinue = process.stdout.write(data);\n if (!canContinue) {\n process.stdin.pause();\n }\n } catch (error) {\n console.error(\"Pipe error:\", error);\n }\n });\n\n process.stdin.on(\"error\", (error) => {\n console.error(\"Pipe stdin error:\", error);\n });\n\n process.stdin.on(\"end\", () => {\n console.error(`Pipe watcher '${name}' ended.`);\n watcher.cleanup();\n });\n\n const shutdown = () => {\n watcher.cleanup();\n process.stdout.removeListener(\"drain\", onDrain);\n process.stdin.removeAllListeners();\n process.exit(0);\n };\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n}\n","export async function attachPidCommand(args: string[]): Promise<void> {\n const pid = parseInt(args[0]!, 10);\n if (isNaN(pid)) {\n console.error(\"Error: valid PID is required\");\n process.exit(1);\n }\n const { PortDetector } = await import(\"@plumpslabs/fennec-core\");\n const detector = new PortDetector();\n const info = detector.detectByPid(pid);\n if (info) {\n console.log(`Attached to PID ${pid}${info.command ? ` (${info.command})` : \"\"}`);\n if (info.port) console.log(` Port: ${info.port}`);\n } else {\n console.error(`Could not find process with PID ${pid}`);\n process.exit(1);\n }\n}\n","export async function attachPortCommand(args: string[]): Promise<void> {\n const port = parseInt(args[0]!, 10);\n if (isNaN(port)) {\n console.error(\"Error: valid port number is required\");\n process.exit(1);\n }\n const { PortDetector } = await import(\"@plumpslabs/fennec-core\");\n const detector = new PortDetector();\n const info = detector.detectByPort(port);\n if (info) {\n console.log(`Found process on port ${port}: PID ${info.pid}${info.command ? ` (${info.command})` : \"\"}`);\n } else {\n console.error(`No process found listening on port ${port}`);\n process.exit(1);\n }\n}\n","import { existsSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\n\nexport async function watchCommand(args: string[]): Promise<void> {\n const fileIndex = args.indexOf(\"--file\");\n const filePath = fileIndex !== -1 ? args[fileIndex + 1] : undefined;\n if (!filePath) {\n console.error(\"Error: --file is required for watch command\");\n process.exit(1);\n }\n const nameIndex = args.indexOf(\"--name\");\n const name = nameIndex !== -1 ? args[nameIndex + 1] : undefined;\n const resolvedPath = resolve(filePath);\n if (!existsSync(resolvedPath)) {\n console.error(`Error: File not found: ${resolvedPath}`);\n process.exit(1);\n }\n const { LogWatcher } = await import(\"@plumpslabs/fennec-core\");\n const watcher = new LogWatcher();\n const watcherId = watcher.watchFile(resolvedPath, name);\n console.log(`Watching file: ${resolvedPath}`);\n console.log(`Watcher ID: ${watcherId}`);\n process.stdin.resume();\n}\n","/**\n * Fennec CLI Formatting System\n *\n * Central module for all CLI output formatting.\n * Uses picocolors (7KB, zero dependencies) for coloring.\n *\n * Design principles:\n * - Minimal dependencies (picocolors only)\n * - Clean, modern, readable output\n * - Consistent spacing and alignment\n * - Fox-themed (๐ŸฆŠ) but not overwhelming\n */\n\nimport { createInterface } from \"node:readline\";\nimport pc from \"picocolors\";\n\n// โ”€โ”€โ”€ Color Helpers โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n\n/**\n * Custom hex color formatter.\n * picocolors does have hex() at runtime but lacks TypeScript types for it.\n */\nfunction hex(color: string): (s: string) => string {\n return (pc as any).hex(color);\n}\n\n// Brand colors (runtime-safe, bypass TypeScript limitations)\nconst fennecOrange = hex(\"#FF6432\");\nconst blueAccent = hex(\"#4A90D9\");\nconst redAccent = hex(\"#E94560\");\n\n// โ”€โ”€โ”€ Color Palette โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n\nexport const colors = {\n // Brand colors\n primary: fennecOrange,\n secondary: blueAccent,\n accent: redAccent,\n\n // Semantic colors\n success: pc.green,\n error: pc.red,\n warning: pc.yellow,\n info: pc.cyan,\n muted: pc.dim,\n highlight: pc.bold,\n\n // Backgrounds\n bgSuccess: (s: string) => pc.bgGreen(pc.black(` ${s} `)),\n bgError: (s: string) => pc.bgRed(pc.white(` ${s} `)),\n bgWarning: (s: string) => pc.bgYellow(pc.black(` ${s} `)),\n bgInfo: (s: string) => pc.bgCyan(pc.black(` ${s} `)),\n} as const;\n\n// โ”€โ”€โ”€ Symbols โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n\nexport const symbols = {\n // Status\n active: pc.green(\"โ—\"),\n inactive: pc.dim(\"โ—‹\"),\n error: pc.red(\"โœ—\"),\n warning: pc.yellow(\"โš \"),\n success: pc.green(\"โœ“\"),\n info: pc.cyan(\"โ„น\"),\n pending: pc.yellow(\"โ—Œ\"),\n\n // UI\n bullet: pc.dim(\"โ”‚\"),\n dot: pc.dim(\"ยท\"),\n arrow: pc.cyan(\"โ†’\"),\n pointer: fennecOrange(\"โ–ถ\"),\n separator: pc.dim(\"โ”€\"),\n\n // Fennec\n fox: \"๐ŸฆŠ\",\n ears: \"โœ\",\n} as const;\n\n// โ”€โ”€โ”€ Status Badges โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n\nexport type ProcessStatus = \"running\" | \"stopped\" | \"error\" | \"starting\" | \"unknown\";\n\nexport function statusBadge(status: ProcessStatus): string {\n switch (status) {\n case \"running\": return pc.green(\"โ— running\");\n case \"stopped\": return pc.dim(\"โ—‹ stopped\");\n case \"error\": return pc.red(\"โœ— error\");\n case \"starting\": return pc.yellow(\"โ—Œ starting\");\n case \"unknown\": return pc.dim(\"? unknown\");\n }\n}\n\nexport type LogLevel = \"error\" | \"warn\" | \"info\" | \"debug\" | \"verbose\";\n\nexport function logLevel(label: string): string {\n const upper = label.toUpperCase().padEnd(5);\n switch (label.toLowerCase()) {\n case \"error\": return pc.bgRed(pc.white(` ${upper} `));\n case \"warn\": return pc.bgYellow(pc.black(` ${upper} `));\n case \"info\": return pc.bgCyan(pc.black(` ${upper} `));\n case \"debug\": return pc.dim(pc.italic(upper));\n case \"verbose\": return pc.dim(pc.italic(upper));\n default: return pc.dim(upper);\n }\n}\n\n// โ”€โ”€โ”€ Table Renderer โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n\nexport interface Column {\n key: string;\n label: string;\n align?: \"left\" | \"right\" | \"center\";\n width?: number;\n format?: (value: unknown) => string;\n}\n\nexport interface Row {\n [key: string]: unknown;\n}\n\nexport function renderTable(columns: Column[], rows: Row[], options?: { padding?: number; compact?: boolean }): string {\n const pad = options?.padding ?? 1;\n const isCompact = options?.compact ?? false;\n\n if (rows.length === 0) {\n return pc.dim(\" (no data)\");\n }\n\n // Calculate column widths\n const widths: number[] = columns.map((col) => {\n const headerLen = col.label.length;\n const maxDataLen = rows.reduce((max, row) => {\n const val = String(row[col.key] ?? \"-\");\n return Math.max(max, val.length);\n }, 0);\n return Math.max(headerLen, maxDataLen, col.width ?? 0) + pad * 2;\n });\n\n // โ”€โ”€โ”€ Helpers โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n const hLine = (left: string, mid: string, right: string, fill = \"โ”€\") => {\n return pc.dim(\n left + widths.map((w) => fill.repeat(w)).join(mid) + right,\n );\n };\n\n const formatCell = (text: string, colIdx: number) => {\n const col = columns[colIdx]!;\n const w = widths[colIdx]!;\n\n // Truncate if too long\n const display = text.length > w ? text.slice(0, w - 1) + \"โ€ฆ\" : text;\n const padded = display.padEnd(w);\n\n const align = col.align ?? \"left\";\n if (align === \"right\") return padded.padStart(w);\n if (align === \"center\") {\n const leftPad = Math.floor((w - display.length) / 2);\n return \" \".repeat(leftPad) + display + \" \".repeat(w - display.length - leftPad);\n }\n return padded;\n };\n\n const renderRow = (row: Row): string => {\n const cells = columns.map((col, i) => {\n const raw = row[col.key];\n const formatted = col.format ? col.format(raw) : String(raw ?? \"-\");\n return formatCell(formatted, i);\n });\n return pc.dim(\"โ”‚\") + cells.join(pc.dim(\"โ”‚\")) + pc.dim(\"โ”‚\");\n };\n\n // โ”€โ”€โ”€ Build table โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n const lines: string[] = [];\n\n if (!isCompact) {\n lines.push(hLine(\"โ”Œ\", \"โ”ฌ\", \"โ”\"));\n }\n\n // Header row\n const headerCells = columns.map((col, i) => {\n return formatCell(pc.bold(col.label), i);\n });\n lines.push(pc.dim(\"โ”‚\") + headerCells.join(pc.dim(\"โ”‚\")) + pc.dim(\"โ”‚\"));\n\n if (!isCompact) {\n lines.push(hLine(\"โ”œ\", \"โ”ผ\", \"โ”ค\"));\n } else {\n lines.push(pc.dim(\"โ”œ\") + widths.map((w) => pc.dim(\"โ”€\").repeat(w)).join(pc.dim(\"โ”ผ\")) + pc.dim(\"โ”ค\"));\n }\n\n // Data rows\n for (const row of rows) {\n const rendered = renderRow(row);\n // Highlight if row has an error\n if (row._error) {\n lines.push(colors.error(rendered));\n } else {\n lines.push(rendered);\n }\n }\n\n if (!isCompact) {\n lines.push(hLine(\"โ””\", \"โ”ด\", \"โ”˜\"));\n }\n\n return lines.join(\"\\n\");\n}\n\n// โ”€โ”€โ”€ Key-Value Display โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n\nexport function renderKV(key: string, value: string, options?: { indent?: number; separator?: string }): string {\n const indent = options?.indent ?? 2;\n const sep = options?.separator ?? \":\";\n const pad = \" \".repeat(indent);\n return `${pad}${pc.dim(key + sep)} ${value}`;\n}\n\nexport function renderKVColor(key: string, value: string, color: (s: string) => string = pc.bold): string {\n return ` ${pc.dim(key + \":\")} ${color(value)}`;\n}\n\nexport function renderSection(title: string, content: string): string {\n const line = pc.dim(\"โ”€\".repeat(40));\n return `\\n ${pc.bold(title)}\\n ${line}\\n${content}\\n`;\n}\n\n// โ”€โ”€โ”€ Box Renderer โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n\nexport function renderBox(title: string, content: string, options?: {\n width?: number;\n borderColor?: (s: string) => string;\n titleColor?: (s: string) => string;\n}): string {\n const width = options?.width ?? 50;\n const border = options?.borderColor ?? pc.dim;\n const titleColor = options?.titleColor ?? pc.bold;\n\n const lines = content.split(\"\\n\");\n const wrapped = lines.map((l) => {\n const clean = l.replace(/\\x1b\\[[0-9;]*m/g, \"\");\n return l + \" \".repeat(Math.max(0, width - clean.length - 2));\n });\n\n const top = border(`โ”Œโ”€ ${titleColor(title)} ${\"โ”€\".repeat(Math.max(0, width - title.length - 4))}โ”`);\n const mid = wrapped.map((l) => border(\"โ”‚ \") + l + border(\" โ”‚\")).join(\"\\n\");\n const bot = border(`โ””${\"โ”€\".repeat(width)}โ”˜`);\n\n return `\\n${top}\\n${mid}\\n${bot}\\n`;\n}\n\n// โ”€โ”€โ”€ Spinner (minimal, no dependencies) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n\nexport interface Spinner {\n update(text: string): void;\n succeed(text: string): void;\n fail(text: string): void;\n warn(text: string): void;\n stop(): void;\n}\n\nexport function createSpinner(text: string): Spinner {\n const frames = [\"โ ‹\", \"โ ™\", \"โ น\", \"โ ธ\", \"โ ผ\", \"โ ด\", \"โ ฆ\", \"โ ง\", \"โ ‡\", \"โ \"];\n let i = 0;\n let running = true;\n\n const interval = setInterval(() => {\n if (!running) return;\n process.stdout.write(`\\r${pc.cyan(frames[i])} ${text}`);\n i = (i + 1) % frames.length;\n }, 80);\n\n return {\n update(newText: string) {\n if (!running) return;\n process.stdout.write(`\\r${pc.cyan(frames[i])} ${newText}`);\n },\n succeed(msg: string) {\n running = false;\n clearInterval(interval);\n process.stdout.write(`\\r${pc.green(\"โœ“\")} ${msg}\\n`);\n },\n fail(msg: string) {\n running = false;\n clearInterval(interval);\n process.stdout.write(`\\r${pc.red(\"โœ—\")} ${msg}\\n`);\n },\n warn(msg: string) {\n running = false;\n clearInterval(interval);\n process.stdout.write(`\\r${pc.yellow(\"โš \")} ${msg}\\n`);\n },\n stop() {\n running = false;\n clearInterval(interval);\n },\n };\n}\n\n// โ”€โ”€โ”€ Interactive Select (enhanced) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n\nexport async function selectPrompt<T extends string>(\n message: string,\n options: { value: T; label: string; description?: string }[],\n): Promise<T | null> {\n console.log(`\\n ${pc.bold(message)}\\n`);\n options.forEach((opt, i) => {\n const num = pc.cyan(` ${i + 1}`);\n console.log(`${num} ${opt.label}`);\n if (opt.description) {\n console.log(` ${pc.dim(opt.description)}`);\n }\n });\n console.log(` ${pc.dim(\"0) Cancel\")}\\n`);\n\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n const answer = await new Promise<string>((resolve) => {\n rl.question(` ${pc.bold(\"Enter number\")} ${pc.dim(\"(0-\" + options.length + \")\")}: `, (ans) => {\n rl.close();\n resolve(ans.trim());\n });\n });\n\n const num = parseInt(answer, 10);\n if (isNaN(num) || num === 0) return null;\n const selected = options[num - 1];\n if (!selected) {\n console.log(` ${pc.red(\"Invalid selection.\")}`);\n return null;\n }\n return selected.value;\n}\n\nexport async function confirmPrompt(message: string, defaultValue = false): Promise<boolean> {\n const hint = defaultValue ? pc.bold(\"Y\") + pc.dim(\"/n\") : pc.dim(\"y/\") + pc.bold(\"N\");\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n const answer = await new Promise<string>((resolve) => {\n rl.question(`\\n ${message} ${pc.dim(\"(\" + hint + \")\")}: `, (ans) => {\n rl.close();\n resolve(ans.trim().toLowerCase());\n });\n });\n\n if (!answer) return defaultValue;\n return answer === \"y\" || answer === \"yes\";\n}\n\n// โ”€โ”€โ”€ Divider โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n\nexport function divider(text?: string): string {\n if (text) {\n const len = 50 - text.length - 2;\n const left = Math.floor(len / 2);\n const right = len - left;\n return pc.dim(\"โ”€\".repeat(left) + ` ${text} ` + \"โ”€\".repeat(right));\n }\n return pc.dim(\"โ”€\".repeat(50));\n}\n\n// โ”€โ”€โ”€ Timestamp โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n\nexport function timestamp(date?: Date): string {\n const d = date ?? new Date();\n const time = d.toLocaleTimeString(\"en-US\", { hour12: false });\n return pc.dim(`[${time}]`);\n}\n\n// โ”€โ”€โ”€ Command Line โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n\nexport function renderCommand(cmd: string): string {\n return fennecOrange(`$ ${cmd}`);\n}\n\nexport function renderAppName(name: string): string {\n return fennecOrange(pc.bold(name));\n}\n\n// โ”€โ”€โ”€ Error Display โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n\nexport function renderError(title: string, details?: string, suggestions?: string[]): string {\n const lines: string[] = [];\n lines.push(`\\n ${pc.bgRed(pc.white(\" ERROR \"))} ${pc.bold(title)}`);\n if (details) {\n lines.push(` ${pc.dim(details)}`);\n }\n if (suggestions && suggestions.length > 0) {\n lines.push(`\\n ${pc.bold(\"Suggestions:\")}`);\n for (const s of suggestions) {\n lines.push(` ${pc.cyan(\"โ†’\")} ${s}`);\n }\n }\n return lines.join(\"\\n\") + \"\\n\";\n}\n\nexport function renderSuccess(message: string): string {\n return ` ${pc.green(\"โœ“\")} ${message}`;\n}\n\nexport function renderWarning(message: string): string {\n return ` ${pc.yellow(\"โš \")} ${message}`;\n}\n"],"mappings":"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","names":["existsSync","resolve","pc","pc","fennecOrange","pc","args","args","args","args","pc","fennecOrange","pc","num","resolve","fennecOrange","pc","args","resolve","existsSync"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plumpslabs/fennec-cli",
3
- "version": "1.9.0",
3
+ "version": "1.11.0",
4
4
  "description": "Fennec CLI โ€” pipe, attach, watch, and start the Fennec MCP server",
5
5
  "keywords": [
6
6
  "mcp",
@@ -38,7 +38,8 @@
38
38
  "LICENSE"
39
39
  ],
40
40
  "dependencies": {
41
- "@plumpslabs/fennec-core": "1.9.0"
41
+ "picocolors": "^1.1.1",
42
+ "@plumpslabs/fennec-core": "1.11.0"
42
43
  },
43
44
  "peerDependencies": {
44
45
  "playwright": "^1.42.0"