@plumpslabs/fennec-cli 0.7.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Fennec Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/index.js ADDED
@@ -0,0 +1,352 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { existsSync as existsSync2 } from "fs";
5
+ import { resolve as resolve2 } from "path";
6
+ import { intro, outro, spinner, confirm, select, isCancel } from "@clack/prompts";
7
+ import { FennecServer, SessionStore } from "@plumpslabs/fennec-core";
8
+ import { execSync } from "child_process";
9
+ import { writeFileSync } from "fs";
10
+
11
+ // src/utils/banner.ts
12
+ function printBanner() {
13
+ console.error(`
14
+ /\\ /\\
15
+ ( o o ) fennec v0.1.0
16
+ =( Y )= ears everywhere in your stack.
17
+ ) (
18
+ `);
19
+ }
20
+
21
+ // src/utils/help.ts
22
+ function showHelp() {
23
+ console.error(`
24
+ Usage: fennec <command> [options]
25
+
26
+ Commands:
27
+ start Start the MCP server (default)
28
+ --transport Transport type (stdio | sse) [default: stdio]
29
+ --port SSE port [default: 3333]
30
+ --config Path to config file
31
+
32
+ pipe Pipe stdin to log watcher
33
+ --name Watcher name (required)
34
+
35
+ attach-pid Attach to a running process by PID
36
+ <pid> Process ID (required)
37
+ --name Name for the process
38
+
39
+ attach-port Attach to a process by port
40
+ <port> Port number (required)
41
+ --name Name for the process
42
+
43
+ watch Watch a log file
44
+ --file File path (required)
45
+ --name Watcher name
46
+
47
+ sessions List saved auth sessions
48
+ setup Configure MCP client (Claude Desktop)
49
+ install-browsers Install Playwright browser engines
50
+ init Generate fennec.config.yaml
51
+
52
+ help Show this help message
53
+ `);
54
+ }
55
+
56
+ // src/commands/pipe.ts
57
+ async function pipeCommand(args2) {
58
+ const nameIndex = args2.indexOf("--name");
59
+ const name = nameIndex !== -1 ? args2[nameIndex + 1] : "pipe";
60
+ if (!name) {
61
+ console.error("Error: --name is required for pipe command");
62
+ process.exit(1);
63
+ }
64
+ const { PipeWatcher } = await import("@plumpslabs/fennec-core");
65
+ const watcher = new PipeWatcher();
66
+ const { write } = watcher.createPipe(name);
67
+ console.error(`Pipe watcher '${name}' active. Forwarding stdin...`);
68
+ const onDrain = () => {
69
+ process.stdin.resume();
70
+ };
71
+ process.stdout.on("drain", onDrain);
72
+ process.stdin.setEncoding("utf-8");
73
+ process.stdin.on("data", (data) => {
74
+ try {
75
+ write(data);
76
+ const canContinue = process.stdout.write(data);
77
+ if (!canContinue) {
78
+ process.stdin.pause();
79
+ }
80
+ } catch (error) {
81
+ console.error("Pipe error:", error);
82
+ }
83
+ });
84
+ process.stdin.on("error", (error) => {
85
+ console.error("Pipe stdin error:", error);
86
+ });
87
+ process.stdin.on("end", () => {
88
+ console.error(`Pipe watcher '${name}' ended.`);
89
+ watcher.cleanup();
90
+ });
91
+ const shutdown = () => {
92
+ watcher.cleanup();
93
+ process.stdout.removeListener("drain", onDrain);
94
+ process.stdin.removeAllListeners();
95
+ process.exit(0);
96
+ };
97
+ process.on("SIGINT", shutdown);
98
+ process.on("SIGTERM", shutdown);
99
+ }
100
+
101
+ // src/commands/attach-pid.ts
102
+ async function attachPidCommand(args2) {
103
+ const pid = parseInt(args2[0], 10);
104
+ if (isNaN(pid)) {
105
+ console.error("Error: valid PID is required");
106
+ process.exit(1);
107
+ }
108
+ const { PortDetector } = await import("@plumpslabs/fennec-core");
109
+ const detector = new PortDetector();
110
+ const info = detector.detectByPid(pid);
111
+ if (info) {
112
+ console.log(`Attached to PID ${pid}${info.command ? ` (${info.command})` : ""}`);
113
+ if (info.port) console.log(` Port: ${info.port}`);
114
+ } else {
115
+ console.error(`Could not find process with PID ${pid}`);
116
+ process.exit(1);
117
+ }
118
+ }
119
+
120
+ // src/commands/attach-port.ts
121
+ async function attachPortCommand(args2) {
122
+ const port = parseInt(args2[0], 10);
123
+ if (isNaN(port)) {
124
+ console.error("Error: valid port number is required");
125
+ process.exit(1);
126
+ }
127
+ const { PortDetector } = await import("@plumpslabs/fennec-core");
128
+ const detector = new PortDetector();
129
+ const info = detector.detectByPort(port);
130
+ if (info) {
131
+ console.log(`Found process on port ${port}: PID ${info.pid}${info.command ? ` (${info.command})` : ""}`);
132
+ } else {
133
+ console.error(`No process found listening on port ${port}`);
134
+ process.exit(1);
135
+ }
136
+ }
137
+
138
+ // src/commands/watch.ts
139
+ import { existsSync } from "fs";
140
+ import { resolve } from "path";
141
+ async function watchCommand(args2) {
142
+ const fileIndex = args2.indexOf("--file");
143
+ const filePath = fileIndex !== -1 ? args2[fileIndex + 1] : void 0;
144
+ if (!filePath) {
145
+ console.error("Error: --file is required for watch command");
146
+ process.exit(1);
147
+ }
148
+ const nameIndex = args2.indexOf("--name");
149
+ const name = nameIndex !== -1 ? args2[nameIndex + 1] : void 0;
150
+ const resolvedPath = resolve(filePath);
151
+ if (!existsSync(resolvedPath)) {
152
+ console.error(`Error: File not found: ${resolvedPath}`);
153
+ process.exit(1);
154
+ }
155
+ const { LogWatcher } = await import("@plumpslabs/fennec-core");
156
+ const watcher = new LogWatcher();
157
+ const watcherId = watcher.watchFile(resolvedPath, name);
158
+ console.log(`Watching file: ${resolvedPath}`);
159
+ console.log(`Watcher ID: ${watcherId}`);
160
+ process.stdin.resume();
161
+ }
162
+
163
+ // src/index.ts
164
+ var [, , command, ...args] = process.argv;
165
+ async function main() {
166
+ if (!command || command === "start") {
167
+ await startServer(args);
168
+ } else if (command === "pipe") {
169
+ await pipeCommand(args);
170
+ } else if (command === "attach-pid") {
171
+ await attachPidCommand(args);
172
+ } else if (command === "attach-port") {
173
+ await attachPortCommand(args);
174
+ } else if (command === "watch") {
175
+ await watchCommand(args);
176
+ } else if (command === "sessions") {
177
+ await sessionsCommand();
178
+ } else if (command === "setup") {
179
+ await setupCommand();
180
+ } else if (command === "install-browsers") {
181
+ printBanner();
182
+ await installBrowsersCommand();
183
+ } else if (command === "init") {
184
+ printBanner();
185
+ await initCommand();
186
+ } else if (command === "help" || command === "--help" || command === "-h") {
187
+ printBanner();
188
+ showHelp();
189
+ } else {
190
+ console.error(`Unknown command: ${command}`);
191
+ console.error("Run 'fennec help' for usage information");
192
+ process.exit(1);
193
+ }
194
+ }
195
+ async function startServer(args2) {
196
+ const configIndex = args2.indexOf("--config");
197
+ const configPath = configIndex !== -1 ? args2[configIndex + 1] : void 0;
198
+ const server = new FennecServer(configPath);
199
+ await server.start();
200
+ }
201
+ async function sessionsCommand() {
202
+ const store = new SessionStore("./.fennec/sessions");
203
+ const sessions = store.list();
204
+ if (sessions.length === 0) {
205
+ console.log("No saved sessions found.");
206
+ return;
207
+ }
208
+ console.log("Saved sessions:");
209
+ for (const s of sessions) {
210
+ console.log(` ${s.name} at ${s.origin} (saved: ${new Date(s.savedAt).toLocaleString()})`);
211
+ }
212
+ console.log(`
213
+ Total: ${sessions.length} session(s)`);
214
+ }
215
+ async function setupCommand() {
216
+ intro("Fennec Setup");
217
+ const mcpClient = await select({
218
+ message: "Which MCP client are you using?",
219
+ options: [
220
+ { value: "claude", label: "Claude Desktop" },
221
+ { value: "other", label: "Other MCP client" }
222
+ ]
223
+ });
224
+ if (isCancel(mcpClient)) {
225
+ outro("Setup cancelled");
226
+ return;
227
+ }
228
+ if (mcpClient === "claude") {
229
+ console.log(`
230
+ To configure Claude Desktop for Fennec, add to your config:
231
+
232
+ {
233
+ "mcpServers": {
234
+ "fennec": {
235
+ "command": "fennec",
236
+ "args": ["start"]
237
+ }
238
+ }
239
+ }
240
+ `);
241
+ }
242
+ outro("Setup complete! Run 'fennec start' to begin.");
243
+ }
244
+ async function installBrowsersCommand() {
245
+ intro("Installing browser engines");
246
+ const s = spinner();
247
+ s.start("Installing Chromium...");
248
+ try {
249
+ execSync("npx playwright install chromium", {
250
+ stdio: "inherit",
251
+ timeout: 12e4
252
+ });
253
+ s.stop("Chromium installed successfully");
254
+ } catch {
255
+ s.stop("Failed to install Chromium");
256
+ console.error("Try running manually: npx playwright install chromium");
257
+ }
258
+ outro("Browser installation complete.");
259
+ }
260
+ async function initCommand() {
261
+ intro("Initialize Fennec Configuration");
262
+ const configFile = resolve2("./fennec.config.yaml");
263
+ if (existsSync2(configFile)) {
264
+ const overwrite = await confirm({
265
+ message: "fennec.config.yaml already exists. Overwrite?"
266
+ });
267
+ if (isCancel(overwrite) || !overwrite) {
268
+ outro("Cancelled");
269
+ return;
270
+ }
271
+ }
272
+ const config = `# Fennec Configuration
273
+ # Generated by 'fennec init'
274
+
275
+ browser:
276
+ type: chromium
277
+ headless: true
278
+ defaultTimeout: 30000
279
+ viewport:
280
+ width: 1280
281
+ height: 720
282
+
283
+ session:
284
+ maxSessions: 10
285
+ idleTimeoutSecs: 1800
286
+ persistPath: "./.fennec/sessions"
287
+
288
+ process:
289
+ maxProcesses: 10
290
+ logBufferLines: 2000
291
+ spawnAllowlist:
292
+ - "npm"
293
+ - "node"
294
+ - "pnpm"
295
+ - "yarn"
296
+ - "bun"
297
+ - "python"
298
+ - "python3"
299
+
300
+ terminal:
301
+ logBufferLines: 2000
302
+ watchDebounceMs: 50
303
+
304
+ network:
305
+ bufferSize: 1000
306
+ captureRequestBody: true
307
+ captureResponseBody: true
308
+ slowRequestThresholdMs: 1000
309
+
310
+ console:
311
+ bufferSize: 500
312
+ levels:
313
+ - log
314
+ - info
315
+ - warn
316
+ - error
317
+ - debug
318
+
319
+ correlation:
320
+ windowMs: 500
321
+ enableRootCauseInference: true
322
+ minConfidence: 0.7
323
+
324
+ security:
325
+ sandbox: true
326
+ allowProcessSpawn: true
327
+ allowProcessKill: false
328
+ allowedDomains: []
329
+ blockedDomains: []
330
+ allowFileProtocol: false
331
+ allowCDPRawAccess: false
332
+ exportPath: "./.fennec/exports"
333
+ maxExportSizeMB: 10
334
+
335
+ transport:
336
+ type: stdio
337
+ port: 3333
338
+ host: "127.0.0.1"
339
+
340
+ logging:
341
+ level: info
342
+ format: pretty
343
+ file: null
344
+ `;
345
+ writeFileSync(configFile, config, "utf-8");
346
+ outro(`Configuration written to ${configFile}`);
347
+ }
348
+ main().catch((error) => {
349
+ console.error("Fatal error:", error);
350
+ process.exit(1);
351
+ });
352
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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 } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { intro, outro, spinner, confirm, select, isCancel } from \"@clack/prompts\";\nimport { FennecServer, SessionStore } from \"@plumpslabs/fennec-core\";\nimport { execSync } from \"node:child_process\";\nimport { writeFileSync } from \"node:fs\";\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\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 intro(\"Fennec Setup\");\n\n const mcpClient = await select({\n message: \"Which MCP client are you using?\",\n options: [\n { value: \"claude\", label: \"Claude Desktop\" },\n { value: \"other\", label: \"Other MCP client\" },\n ],\n });\n\n if (isCancel(mcpClient)) {\n outro(\"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 outro(\"Setup complete! Run 'fennec start' to begin.\");\n}\n\nasync function installBrowsersCommand(): Promise<void> {\n intro(\"Installing browser engines\");\n\n const s = spinner();\n s.start(\"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 outro(\"Browser installation complete.\");\n}\n\nasync function initCommand(): Promise<void> {\n intro(\"Initialize Fennec Configuration\");\n\n const configFile = resolve(\"./fennec.config.yaml\");\n\n if (existsSync(configFile)) {\n const overwrite = await confirm({\n message: \"fennec.config.yaml already exists. Overwrite?\",\n });\n\n if (isCancel(overwrite) || !overwrite) {\n outro(\"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 outro(`Configuration written to ${configFile}`);\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 v0.1.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","args","resolve","existsSync"]}
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "@plumpslabs/fennec-cli",
3
+ "version": "0.7.0",
4
+ "description": "Fennec CLI — pipe, attach, watch, and start the Fennec MCP server",
5
+ "keywords": [
6
+ "mcp",
7
+ "model-context-protocol",
8
+ "cli",
9
+ "browser-automation",
10
+ "developer-tools"
11
+ ],
12
+ "homepage": "https://github.com/plumpslabs/fennec#readme",
13
+ "bugs": {
14
+ "url": "https://github.com/plumpslabs/fennec/issues"
15
+ },
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/plumpslabs/fennec.git"
19
+ },
20
+ "license": "MIT",
21
+ "author": "Fennec Contributors",
22
+ "type": "module",
23
+ "bin": {
24
+ "fennec": "./dist/index.js"
25
+ },
26
+ "main": "./dist/index.js",
27
+ "types": "./dist/index.d.ts",
28
+ "exports": {
29
+ ".": {
30
+ "types": "./dist/index.d.ts",
31
+ "import": "./dist/index.js",
32
+ "default": "./dist/index.js"
33
+ }
34
+ },
35
+ "files": [
36
+ "dist",
37
+ "README.md",
38
+ "LICENSE"
39
+ ],
40
+ "dependencies": {
41
+ "@clack/prompts": "^0.7.0",
42
+ "pino": "^8.19.0",
43
+ "playwright": "^1.42.0",
44
+ "@plumpslabs/fennec-core": "0.7.0"
45
+ },
46
+ "devDependencies": {
47
+ "@types/node": "^20.11.0",
48
+ "tsup": "^8.0.0",
49
+ "typescript": "^5.4.0",
50
+ "vitest": "^1.3.0"
51
+ },
52
+ "engines": {
53
+ "node": ">=20.0.0"
54
+ },
55
+ "publishConfig": {
56
+ "access": "public"
57
+ },
58
+ "scripts": {
59
+ "build": "tsup",
60
+ "dev": "tsup --watch",
61
+ "clean": "rm -rf dist",
62
+ "typecheck": "tsc --noEmit",
63
+ "lint": "echo 'lint: ok'",
64
+ "test": "vitest run",
65
+ "test:watch": "vitest"
66
+ }
67
+ }