junis 0.3.3 → 0.3.5

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.
@@ -1,52 +1,15 @@
1
- "use strict";
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __export = (target, all) => {
9
- for (var name in all)
10
- __defProp(target, name, { get: all[name], enumerable: true });
11
- };
12
- var __copyProps = (to, from, except, desc) => {
13
- if (from && typeof from === "object" || typeof from === "function") {
14
- for (let key of __getOwnPropNames(from))
15
- if (!__hasOwnProp.call(to, key) && key !== except)
16
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
- }
18
- return to;
19
- };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
-
30
1
  // src/server/mcp.ts
31
- var mcp_exports = {};
32
- __export(mcp_exports, {
33
- checkPermission: () => checkPermission,
34
- handleMCPRequest: () => handleMCPRequest,
35
- startMCPServer: () => startMCPServer,
36
- toolPermissions: () => toolPermissions
37
- });
38
- module.exports = __toCommonJS(mcp_exports);
39
- var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
40
- var import_streamableHttp = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
41
- var import_http = require("http");
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
4
+ import { createServer } from "http";
42
5
 
43
6
  // src/tools/filesystem.ts
44
- var import_child_process = require("child_process");
45
- var import_util = require("util");
46
- var import_promises = __toESM(require("fs/promises"));
47
- var import_path = __toESM(require("path"));
48
- var import_glob = require("glob");
49
- var import_zod = require("zod");
7
+ import { exec, execFile } from "child_process";
8
+ import { promisify } from "util";
9
+ import fs from "fs/promises";
10
+ import path from "path";
11
+ import { glob } from "glob";
12
+ import { z } from "zod";
50
13
 
51
14
  // src/server/permissions.ts
52
15
  var toolPermissions = {
@@ -98,8 +61,8 @@ function checkPermission(toolName) {
98
61
  }
99
62
 
100
63
  // src/tools/filesystem.ts
101
- var execAsync = (0, import_util.promisify)(import_child_process.exec);
102
- var execFileAsync = (0, import_util.promisify)(import_child_process.execFile);
64
+ var execAsync = promisify(exec);
65
+ var execFileAsync = promisify(execFile);
103
66
  var FilesystemTools = class {
104
67
  register(server) {
105
68
  server.tool(
@@ -121,14 +84,14 @@ var FilesystemTools = class {
121
84
  "- Avoid piping untrusted input into shells. Use absolute paths when possible. Quote paths containing spaces."
122
85
  ].join("\n"),
123
86
  {
124
- command: import_zod.z.string().describe("The shell command to execute. Use absolute paths when possible. Quote paths containing spaces."),
125
- timeout_ms: import_zod.z.number().optional().default(3e4).describe("Maximum execution time in milliseconds (default: 30000). Increase for long-running builds or downloads."),
126
- background: import_zod.z.boolean().optional().default(false).describe("Run in background without waiting for completion. Use for servers or long-running processes.")
87
+ command: z.string().describe("The shell command to execute. Use absolute paths when possible. Quote paths containing spaces."),
88
+ timeout_ms: z.number().optional().default(3e4).describe("Maximum execution time in milliseconds (default: 30000). Increase for long-running builds or downloads."),
89
+ background: z.boolean().optional().default(false).describe("Run in background without waiting for completion. Use for servers or long-running processes.")
127
90
  },
128
91
  async ({ command, timeout_ms, background }) => {
129
92
  checkPermission("execute_command");
130
93
  if (background) {
131
- (0, import_child_process.exec)(command);
94
+ exec(command);
132
95
  return { content: [{ type: "text", text: "Background execution started" }] };
133
96
  }
134
97
  try {
@@ -162,12 +125,12 @@ ${error.stderr ?? ""}`
162
125
  "For searching within files, prefer search_code instead. For listing directory contents, use list_directory."
163
126
  ].join("\n"),
164
127
  {
165
- path: import_zod.z.string().describe("Absolute or relative file path to read"),
166
- encoding: import_zod.z.enum(["utf-8", "base64"]).optional().default("utf-8").describe("'utf-8' for text files (default), 'base64' for binary files (images, PDFs, archives)")
128
+ path: z.string().describe("Absolute or relative file path to read"),
129
+ encoding: z.enum(["utf-8", "base64"]).optional().default("utf-8").describe("'utf-8' for text files (default), 'base64' for binary files (images, PDFs, archives)")
167
130
  },
168
131
  async ({ path: filePath, encoding }) => {
169
132
  try {
170
- const content = await import_promises.default.readFile(filePath, encoding);
133
+ const content = await fs.readFile(filePath, encoding);
171
134
  return { content: [{ type: "text", text: content }] };
172
135
  } catch (err) {
173
136
  const e = err;
@@ -187,13 +150,13 @@ ${error.stderr ?? ""}`
187
150
  "Prefer edit_block over write_file for existing files \u2014 it's safer and preserves unmodified content."
188
151
  ].join("\n"),
189
152
  {
190
- path: import_zod.z.string().describe("File path to create or overwrite. Parent directories are auto-created."),
191
- content: import_zod.z.string().describe("Complete file content. This replaces the entire file.")
153
+ path: z.string().describe("File path to create or overwrite. Parent directories are auto-created."),
154
+ content: z.string().describe("Complete file content. This replaces the entire file.")
192
155
  },
193
156
  async ({ path: filePath, content }) => {
194
157
  checkPermission("write_file");
195
- await import_promises.default.mkdir(import_path.default.dirname(filePath), { recursive: true });
196
- await import_promises.default.writeFile(filePath, content, "utf-8");
158
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
159
+ await fs.writeFile(filePath, content, "utf-8");
197
160
  return { content: [{ type: "text", text: "File saved" }] };
198
161
  }
199
162
  );
@@ -204,11 +167,11 @@ ${error.stderr ?? ""}`
204
167
  "Use this to explore project structure before reading or modifying files."
205
168
  ].join("\n"),
206
169
  {
207
- path: import_zod.z.string().describe("Directory path to list")
170
+ path: z.string().describe("Directory path to list")
208
171
  },
209
172
  async ({ path: dirPath }) => {
210
173
  try {
211
- const entries = await import_promises.default.readdir(dirPath, { withFileTypes: true });
174
+ const entries = await fs.readdir(dirPath, { withFileTypes: true });
212
175
  const lines = entries.map((e) => `${e.isDirectory() ? "\u{1F4C1}" : "\u{1F4C4}"} ${e.name}`);
213
176
  return { content: [{ type: "text", text: lines.join("\n") }] };
214
177
  } catch (err) {
@@ -229,9 +192,9 @@ ${error.stderr ?? ""}`
229
192
  "Returns matching lines with file paths and line numbers for precise navigation."
230
193
  ].join("\n"),
231
194
  {
232
- pattern: import_zod.z.string().describe("Search pattern with full regex support (e.g. 'function\\s+\\w+', 'import.*from', 'TODO')"),
233
- directory: import_zod.z.string().optional().default(".").describe("Root directory to search from (default: current working directory)"),
234
- file_pattern: import_zod.z.string().optional().default("**/*").describe("Glob pattern to filter files (e.g. '**/*.ts', '*.py', 'src/**/*.js')")
195
+ pattern: z.string().describe("Search pattern with full regex support (e.g. 'function\\s+\\w+', 'import.*from', 'TODO')"),
196
+ directory: z.string().optional().default(".").describe("Root directory to search from (default: current working directory)"),
197
+ file_pattern: z.string().optional().default("**/*").describe("Glob pattern to filter files (e.g. '**/*.ts', '*.py', 'src/**/*.js')")
235
198
  },
236
199
  async ({ pattern, directory, file_pattern }) => {
237
200
  try {
@@ -242,13 +205,13 @@ ${error.stderr ?? ""}`
242
205
  );
243
206
  return { content: [{ type: "text", text: stdout || "No results" }] };
244
207
  } catch {
245
- const safeDirectory = import_path.default.resolve(directory);
246
- const files = await (0, import_glob.glob)(file_pattern, { cwd: safeDirectory });
208
+ const safeDirectory = path.resolve(directory);
209
+ const files = await glob(file_pattern, { cwd: safeDirectory });
247
210
  const results = [];
248
211
  for (const file of files.slice(0, 100)) {
249
212
  try {
250
- const content = await import_promises.default.readFile(
251
- import_path.default.join(safeDirectory, file),
213
+ const content = await fs.readFile(
214
+ path.join(safeDirectory, file),
252
215
  "utf-8"
253
216
  );
254
217
  const lines = content.split("\n");
@@ -285,8 +248,8 @@ ${error.stderr ?? ""}`
285
248
  "SAFETY: Only kill processes the user explicitly identifies. Never kill system-critical processes (init, systemd, loginwindow, WindowServer) without explicit instruction."
286
249
  ].join("\n"),
287
250
  {
288
- pid: import_zod.z.number().describe("PID of the process to terminate (use list_processes to find PIDs)"),
289
- signal: import_zod.z.enum(["SIGTERM", "SIGKILL"]).optional().default("SIGTERM").describe("SIGTERM (default): graceful shutdown with 3s auto-SIGKILL fallback. SIGKILL: immediate force kill.")
251
+ pid: z.number().describe("PID of the process to terminate (use list_processes to find PIDs)"),
252
+ signal: z.enum(["SIGTERM", "SIGKILL"]).optional().default("SIGTERM").describe("SIGTERM (default): graceful shutdown with 3s auto-SIGKILL fallback. SIGKILL: immediate force kill.")
290
253
  },
291
254
  async ({ pid, signal }) => {
292
255
  const isWindows = process.platform === "win32";
@@ -342,13 +305,13 @@ ${error.stderr ?? ""}`
342
305
  "Prefer this over write_file for modifying existing files \u2014 it only changes what you specify and preserves the rest."
343
306
  ].join("\n"),
344
307
  {
345
- path: import_zod.z.string().describe("Path to the file to edit. The file must already exist."),
346
- old_string: import_zod.z.string().describe("The exact text to find and replace. Must match character-for-character including whitespace and newlines. Include enough context for uniqueness."),
347
- new_string: import_zod.z.string().describe("The replacement text. Use empty string to delete the matched text."),
348
- replace_all: import_zod.z.boolean().optional().default(false).describe("If true, replace ALL matches. If false (default), require exactly one match (errors on ambiguous multiple matches).")
308
+ path: z.string().describe("Path to the file to edit. The file must already exist."),
309
+ old_string: z.string().describe("The exact text to find and replace. Must match character-for-character including whitespace and newlines. Include enough context for uniqueness."),
310
+ new_string: z.string().describe("The replacement text. Use empty string to delete the matched text."),
311
+ replace_all: z.boolean().optional().default(false).describe("If true, replace ALL matches. If false (default), require exactly one match (errors on ambiguous multiple matches).")
349
312
  },
350
313
  async ({ path: filePath, old_string, new_string, replace_all }) => {
351
- const content = await import_promises.default.readFile(filePath, "utf-8");
314
+ const content = await fs.readFile(filePath, "utf-8");
352
315
  if (!content.includes(old_string)) {
353
316
  throw new Error(`old_string not found in file: ${filePath}`);
354
317
  }
@@ -372,7 +335,7 @@ ${error.stderr ?? ""}`
372
335
  result = content.replace(old_string, new_string);
373
336
  replaced = 1;
374
337
  }
375
- await import_promises.default.writeFile(filePath, result, "utf-8");
338
+ await fs.writeFile(filePath, result, "utf-8");
376
339
  return {
377
340
  content: [{ type: "text", text: `Replaced (${replaced} occurrence(s) changed)` }]
378
341
  };
@@ -387,9 +350,9 @@ ${error.stderr ?? ""}`
387
350
  "Duplicate commands are automatically detected and rejected. Use cron_list to see existing jobs."
388
351
  ].join("\n"),
389
352
  {
390
- schedule: import_zod.z.string().describe("Cron schedule expression (5 fields: minute hour day month weekday). Examples: '*/5 * * * *' (every 5 min), '0 9 * * 1-5' (weekdays 9am)"),
391
- command: import_zod.z.string().describe("Shell command to execute on schedule"),
392
- label: import_zod.z.string().optional().describe("Human-readable label for identification (e.g. 'daily-backup', 'log-cleanup')")
353
+ schedule: z.string().describe("Cron schedule expression (5 fields: minute hour day month weekday). Examples: '*/5 * * * *' (every 5 min), '0 9 * * 1-5' (weekdays 9am)"),
354
+ command: z.string().describe("Shell command to execute on schedule"),
355
+ label: z.string().optional().describe("Human-readable label for identification (e.g. 'daily-backup', 'log-cleanup')")
393
356
  },
394
357
  async ({ schedule, command, label }) => {
395
358
  try {
@@ -411,9 +374,9 @@ ${error.stderr ?? ""}`
411
374
  `;
412
375
  const updated = existing.trimEnd() + "\n" + newEntry;
413
376
  const tmpFile = `/tmp/junis_crontab_${Date.now()}.txt`;
414
- await import_promises.default.writeFile(tmpFile, updated, "utf-8");
377
+ await fs.writeFile(tmpFile, updated, "utf-8");
415
378
  await execAsync(`crontab ${tmpFile}`);
416
- await import_promises.default.unlink(tmpFile).catch(() => {
379
+ await fs.unlink(tmpFile).catch(() => {
417
380
  });
418
381
  return {
419
382
  content: [{ type: "text", text: `\u2705 Cron job created:
@@ -480,8 +443,8 @@ ${error.stderr ?? ""}`
480
443
  "cron_delete",
481
444
  "Delete a scheduled cron job by its ID (from cron_list output) or by matching command string. Associated comment labels are automatically cleaned up.",
482
445
  {
483
- id: import_zod.z.number().optional().describe("Cron job ID from cron_list output (e.g. 1, 2, 3)"),
484
- command: import_zod.z.string().optional().describe("Delete all jobs matching this command string")
446
+ id: z.number().optional().describe("Cron job ID from cron_list output (e.g. 1, 2, 3)"),
447
+ command: z.string().optional().describe("Delete all jobs matching this command string")
485
448
  },
486
449
  async ({ id, command }) => {
487
450
  if (!id && !command) {
@@ -518,9 +481,9 @@ ${error.stderr ?? ""}`
518
481
  }
519
482
  const updated2 = filtered2.join("\n");
520
483
  const tmpFile2 = `/tmp/junis_crontab_${Date.now()}.txt`;
521
- await import_promises.default.writeFile(tmpFile2, updated2, "utf-8");
484
+ await fs.writeFile(tmpFile2, updated2, "utf-8");
522
485
  await execAsync(`crontab ${tmpFile2}`);
523
- await import_promises.default.unlink(tmpFile2).catch(() => {
486
+ await fs.unlink(tmpFile2).catch(() => {
524
487
  });
525
488
  return { content: [{ type: "text", text: `\u2705 Deleted cron job matching: ${command}` }] };
526
489
  }
@@ -545,9 +508,9 @@ ${error.stderr ?? ""}`
545
508
  const filtered = lines.filter((_, i) => i < target.lineStart || i > target.lineEnd);
546
509
  const updated = filtered.join("\n");
547
510
  const tmpFile = `/tmp/junis_crontab_${Date.now()}.txt`;
548
- await import_promises.default.writeFile(tmpFile, updated, "utf-8");
511
+ await fs.writeFile(tmpFile, updated, "utf-8");
549
512
  await execAsync(`crontab ${tmpFile}`);
550
- await import_promises.default.unlink(tmpFile).catch(() => {
513
+ await fs.unlink(tmpFile).catch(() => {
551
514
  });
552
515
  return { content: [{ type: "text", text: `\u2705 Deleted cron job #${id}` }] };
553
516
  } catch (err) {
@@ -562,9 +525,9 @@ ${error.stderr ?? ""}`
562
525
  };
563
526
 
564
527
  // src/tools/browser.ts
565
- var import_browserclaw = require("browserclaw");
566
- var import_promises2 = __toESM(require("fs/promises"));
567
- var import_zod2 = require("zod");
528
+ import { BrowserClaw } from "browserclaw";
529
+ import fs2 from "fs/promises";
530
+ import { z as z2 } from "zod";
568
531
  var BrowserTools = class {
569
532
  browser = null;
570
533
  page = null;
@@ -605,11 +568,11 @@ var BrowserTools = class {
605
568
  "Always call browser_stop when done to release system resources."
606
569
  ].join("\n"),
607
570
  {
608
- mode: import_zod2.z.enum(["managed", "remote-cdp"]).optional().default("managed").describe("'managed' = launch new browser, 'remote-cdp' = connect to existing Chrome via CDP"),
609
- headless: import_zod2.z.boolean().optional().default(false).describe("Run without visible window (managed mode only). Use for background tasks."),
610
- cdpUrl: import_zod2.z.string().optional().describe("Chrome DevTools Protocol URL for remote-cdp mode (e.g. http://localhost:9222)"),
611
- profile: import_zod2.z.string().optional().describe("Browser profile name for persistent sessions \u2014 preserves cookies, logins, and history across restarts (managed mode only)"),
612
- allowInternal: import_zod2.z.boolean().optional().default(false).describe("Allow navigation to localhost and internal network URLs")
571
+ mode: z2.enum(["managed", "remote-cdp"]).optional().default("managed").describe("'managed' = launch new browser, 'remote-cdp' = connect to existing Chrome via CDP"),
572
+ headless: z2.boolean().optional().default(false).describe("Run without visible window (managed mode only). Use for background tasks."),
573
+ cdpUrl: z2.string().optional().describe("Chrome DevTools Protocol URL for remote-cdp mode (e.g. http://localhost:9222)"),
574
+ profile: z2.string().optional().describe("Browser profile name for persistent sessions \u2014 preserves cookies, logins, and history across restarts (managed mode only)"),
575
+ allowInternal: z2.boolean().optional().default(false).describe("Allow navigation to localhost and internal network URLs")
613
576
  },
614
577
  ({ mode, headless, cdpUrl, profile, allowInternal }) => this.withLock(async () => {
615
578
  if (this.browser) {
@@ -617,9 +580,9 @@ var BrowserTools = class {
617
580
  }
618
581
  if (mode === "remote-cdp") {
619
582
  if (!cdpUrl) throw new Error("cdpUrl is required for remote-cdp mode");
620
- this.browser = await import_browserclaw.BrowserClaw.connect(cdpUrl, { allowInternal });
583
+ this.browser = await BrowserClaw.connect(cdpUrl, { allowInternal });
621
584
  } else {
622
- this.browser = await import_browserclaw.BrowserClaw.launch({
585
+ this.browser = await BrowserClaw.launch({
623
586
  headless,
624
587
  profileName: profile,
625
588
  allowInternal
@@ -641,7 +604,7 @@ var BrowserTools = class {
641
604
  "browser_navigate",
642
605
  "Navigate the browser to a URL. Automatically opens a new tab if the browser is started but no page exists yet. Waits for the page to load before returning.",
643
606
  {
644
- url: import_zod2.z.string().describe("Full URL to navigate to (include https://)")
607
+ url: z2.string().describe("Full URL to navigate to (include https://)")
645
608
  },
646
609
  ({ url }) => this.withLock(async () => {
647
610
  if (!this.browser) throw new Error("Browser not started. Call browser_start first.");
@@ -665,8 +628,8 @@ var BrowserTools = class {
665
628
  "Prefer this over browser_screenshot for understanding page structure \u2014 it's faster, structured, and machine-readable."
666
629
  ].join("\n"),
667
630
  {
668
- interactive: import_zod2.z.boolean().optional().default(true).describe("true (default): only show clickable/typeable elements. false: show all elements including static text."),
669
- compact: import_zod2.z.boolean().optional().default(true).describe("true (default): hide empty containers for cleaner output")
631
+ interactive: z2.boolean().optional().default(true).describe("true (default): only show clickable/typeable elements. false: show all elements including static text."),
632
+ compact: z2.boolean().optional().default(true).describe("true (default): hide empty containers for cleaner output")
670
633
  },
671
634
  ({ interactive, compact }) => this.withLock(async () => {
672
635
  const result = await requirePage().snapshot({ interactive, compact });
@@ -688,9 +651,9 @@ ${refList}`
688
651
  "browser_click",
689
652
  "Click an element by its ref number from browser_snapshot. Always call browser_snapshot first to get current refs \u2014 they change after page updates.",
690
653
  {
691
- ref: import_zod2.z.string().describe("Element ref from browser_snapshot (e.g. 'e1', 'e15'). Call browser_snapshot first to get current refs."),
692
- doubleClick: import_zod2.z.boolean().optional().default(false).describe("Double-click instead of single click"),
693
- button: import_zod2.z.enum(["left", "right", "middle"]).optional().default("left").describe("Mouse button to use")
654
+ ref: z2.string().describe("Element ref from browser_snapshot (e.g. 'e1', 'e15'). Call browser_snapshot first to get current refs."),
655
+ doubleClick: z2.boolean().optional().default(false).describe("Double-click instead of single click"),
656
+ button: z2.enum(["left", "right", "middle"]).optional().default("left").describe("Mouse button to use")
694
657
  },
695
658
  ({ ref, doubleClick, button }) => this.withLock(async () => {
696
659
  await requirePage().click(ref, { doubleClick, button });
@@ -701,10 +664,10 @@ ${refList}`
701
664
  "browser_type",
702
665
  "Type text into an input element by ref number. Use 'submit=true' to press Enter after typing (e.g. for search forms). Use 'slowly=true' for sites requiring keystroke-by-keystroke input.",
703
666
  {
704
- ref: import_zod2.z.string().describe("Element ref from browser_snapshot (e.g. 'e3')"),
705
- text: import_zod2.z.string().describe("Text to type into the element"),
706
- submit: import_zod2.z.boolean().optional().default(false).describe("Press Enter after typing (useful for search boxes and forms)"),
707
- slowly: import_zod2.z.boolean().optional().default(false).describe("Type slowly (75ms per char) for sites that process each keystroke")
667
+ ref: z2.string().describe("Element ref from browser_snapshot (e.g. 'e3')"),
668
+ text: z2.string().describe("Text to type into the element"),
669
+ submit: z2.boolean().optional().default(false).describe("Press Enter after typing (useful for search boxes and forms)"),
670
+ slowly: z2.boolean().optional().default(false).describe("Type slowly (75ms per char) for sites that process each keystroke")
708
671
  },
709
672
  ({ ref, text, submit, slowly }) => this.withLock(async () => {
710
673
  await requirePage().type(ref, text, { submit, slowly });
@@ -715,10 +678,10 @@ ${refList}`
715
678
  "browser_fill",
716
679
  "Fill multiple form fields at once \u2014 more efficient than calling browser_type repeatedly. Each field needs a ref from browser_snapshot.",
717
680
  {
718
- fields: import_zod2.z.array(import_zod2.z.object({
719
- ref: import_zod2.z.string(),
720
- type: import_zod2.z.enum(["text", "checkbox", "radio"]),
721
- value: import_zod2.z.union([import_zod2.z.string(), import_zod2.z.boolean()])
681
+ fields: z2.array(z2.object({
682
+ ref: z2.string(),
683
+ type: z2.enum(["text", "checkbox", "radio"]),
684
+ value: z2.union([z2.string(), z2.boolean()])
722
685
  })).describe("Array of {ref, type, value}. type='text': value is string. type='checkbox'/'radio': value is boolean.")
723
686
  },
724
687
  ({ fields }) => this.withLock(async () => {
@@ -730,8 +693,8 @@ ${refList}`
730
693
  "browser_select",
731
694
  "Select one or more options from a dropdown/select element. Values should match the option value attributes, not display text.",
732
695
  {
733
- ref: import_zod2.z.string().describe("Ref of the <select> element from browser_snapshot"),
734
- values: import_zod2.z.array(import_zod2.z.string()).describe("Option value(s) to select")
696
+ ref: z2.string().describe("Ref of the <select> element from browser_snapshot"),
697
+ values: z2.array(z2.string()).describe("Option value(s) to select")
735
698
  },
736
699
  ({ ref, values }) => this.withLock(async () => {
737
700
  await requirePage().select(ref, ...values);
@@ -742,7 +705,7 @@ ${refList}`
742
705
  "browser_press",
743
706
  "Press a keyboard key or key combination. Use for shortcuts (e.g. 'Control+a', 'Escape'), form submission ('Enter'), or navigation ('Tab'). Does not require a specific element ref.",
744
707
  {
745
- key: import_zod2.z.string().describe("Key or combination: 'Enter', 'Escape', 'Tab', 'Control+a', 'Meta+c', 'ArrowDown', 'Backspace'")
708
+ key: z2.string().describe("Key or combination: 'Enter', 'Escape', 'Tab', 'Control+a', 'Meta+c', 'ArrowDown', 'Backspace'")
746
709
  },
747
710
  ({ key }) => this.withLock(async () => {
748
711
  await requirePage().press(key);
@@ -753,7 +716,7 @@ ${refList}`
753
716
  "browser_hover",
754
717
  "Move the mouse cursor over an element by ref. Use to trigger hover menus, tooltips, or dropdown previews before clicking.",
755
718
  {
756
- ref: import_zod2.z.string().describe("Element ref from browser_snapshot")
719
+ ref: z2.string().describe("Element ref from browser_snapshot")
757
720
  },
758
721
  ({ ref }) => this.withLock(async () => {
759
722
  await requirePage().hover(ref);
@@ -764,8 +727,8 @@ ${refList}`
764
727
  "browser_drag",
765
728
  "Drag an element from startRef to endRef. Both refs must come from a recent browser_snapshot. Use for drag-and-drop interfaces, sliders, or reorderable lists.",
766
729
  {
767
- startRef: import_zod2.z.string().describe("Source element ref to drag from"),
768
- endRef: import_zod2.z.string().describe("Target element ref to drag to")
730
+ startRef: z2.string().describe("Source element ref to drag from"),
731
+ endRef: z2.string().describe("Target element ref to drag to")
769
732
  },
770
733
  ({ startRef, endRef }) => this.withLock(async () => {
771
734
  await requirePage().drag(startRef, endRef);
@@ -776,8 +739,8 @@ ${refList}`
776
739
  "browser_upload",
777
740
  "Upload local files to a file input element (<input type='file'>). The ref must point to a file input from browser_snapshot.",
778
741
  {
779
- ref: import_zod2.z.string().describe("Ref of the file input element from browser_snapshot"),
780
- paths: import_zod2.z.array(import_zod2.z.string()).describe("Absolute file path(s) on the local device to upload")
742
+ ref: z2.string().describe("Ref of the file input element from browser_snapshot"),
743
+ paths: z2.array(z2.string()).describe("Absolute file path(s) on the local device to upload")
781
744
  },
782
745
  ({ ref, paths }) => this.withLock(async () => {
783
746
  await requirePage().uploadFile(ref, paths);
@@ -793,14 +756,14 @@ ${refList}`
793
756
  "Use browser_screenshot only when visual layout matters (charts, images, styling, visual verification)."
794
757
  ].join("\n"),
795
758
  {
796
- path: import_zod2.z.string().optional().describe("Save path for the screenshot. If omitted, returns base64 image data directly."),
797
- fullPage: import_zod2.z.boolean().optional().default(false).describe("Capture the full scrollable page, not just the visible viewport"),
798
- ref: import_zod2.z.string().optional().describe("Capture only a specific element by its ref from browser_snapshot")
759
+ path: z2.string().optional().describe("Save path for the screenshot. If omitted, returns base64 image data directly."),
760
+ fullPage: z2.boolean().optional().default(false).describe("Capture the full scrollable page, not just the visible viewport"),
761
+ ref: z2.string().optional().describe("Capture only a specific element by its ref from browser_snapshot")
799
762
  },
800
763
  ({ path: path2, fullPage, ref }) => this.withLock(async () => {
801
764
  const buffer = await requirePage().screenshot({ fullPage, ref });
802
765
  if (path2) {
803
- await import_promises2.default.writeFile(path2, buffer);
766
+ await fs2.writeFile(path2, buffer);
804
767
  return { content: [{ type: "text", text: `Screenshot saved: ${path2}` }] };
805
768
  }
806
769
  return {
@@ -816,11 +779,11 @@ ${refList}`
816
779
  "browser_pdf",
817
780
  "Save the current page as a PDF file. Renders the full page including below-the-fold content. Useful for archiving, sharing, or offline reading.",
818
781
  {
819
- path: import_zod2.z.string().describe("Output file path (.pdf)")
782
+ path: z2.string().describe("Output file path (.pdf)")
820
783
  },
821
784
  ({ path: path2 }) => this.withLock(async () => {
822
785
  const buffer = await requirePage().pdf();
823
- await import_promises2.default.writeFile(path2, buffer);
786
+ await fs2.writeFile(path2, buffer);
824
787
  return { content: [{ type: "text", text: `PDF saved: ${path2}` }] };
825
788
  })
826
789
  );
@@ -833,7 +796,7 @@ ${refList}`
833
796
  "Wrap complex logic in an IIFE: (function(){ ... })()"
834
797
  ].join("\n"),
835
798
  {
836
- code: import_zod2.z.string().describe("JavaScript code to execute in the page context. Return values are automatically serialized.")
799
+ code: z2.string().describe("JavaScript code to execute in the page context. Return values are automatically serialized.")
837
800
  },
838
801
  ({ code }) => this.withLock(async () => {
839
802
  try {
@@ -860,11 +823,11 @@ ${refList}`
860
823
  "OPTIONS (use one): 'text' (wait for text to appear), 'textGone' (wait for text to disappear), 'url' (URL matches glob), 'loadState' (page load state), 'timeMs' (fixed delay as last resort)."
861
824
  ].join("\n"),
862
825
  {
863
- text: import_zod2.z.string().optional().describe("Wait until this text appears on the page"),
864
- textGone: import_zod2.z.string().optional().describe("Wait until this text disappears from the page"),
865
- url: import_zod2.z.string().optional().describe("Wait until URL matches this glob pattern (e.g. '**/dashboard', '**/success')"),
866
- loadState: import_zod2.z.enum(["load", "domcontentloaded", "networkidle"]).optional().describe("Wait for page load state: 'load' (full), 'domcontentloaded' (DOM ready), 'networkidle' (no pending requests)"),
867
- timeMs: import_zod2.z.number().optional().describe("Fixed wait in milliseconds \u2014 use as last resort when other conditions don't apply")
826
+ text: z2.string().optional().describe("Wait until this text appears on the page"),
827
+ textGone: z2.string().optional().describe("Wait until this text disappears from the page"),
828
+ url: z2.string().optional().describe("Wait until URL matches this glob pattern (e.g. '**/dashboard', '**/success')"),
829
+ loadState: z2.enum(["load", "domcontentloaded", "networkidle"]).optional().describe("Wait for page load state: 'load' (full), 'domcontentloaded' (DOM ready), 'networkidle' (no pending requests)"),
830
+ timeMs: z2.number().optional().describe("Fixed wait in milliseconds \u2014 use as last resort when other conditions don't apply")
868
831
  },
869
832
  ({ text, textGone, url, loadState, timeMs }) => this.withLock(async () => {
870
833
  const condition = {};
@@ -881,14 +844,14 @@ ${refList}`
881
844
  "browser_cookies",
882
845
  "Manage browser cookies: get all cookies, set a specific cookie, or clear all cookies. Useful for authentication state, session management, or testing.",
883
846
  {
884
- action: import_zod2.z.enum(["get", "set", "clear"]).describe("'get': retrieve all cookies, 'set': add/update a cookie, 'clear': remove all cookies"),
885
- cookie: import_zod2.z.object({
886
- name: import_zod2.z.string(),
887
- value: import_zod2.z.string(),
888
- domain: import_zod2.z.string().optional(),
889
- path: import_zod2.z.string().optional(),
890
- httpOnly: import_zod2.z.boolean().optional(),
891
- secure: import_zod2.z.boolean().optional()
847
+ action: z2.enum(["get", "set", "clear"]).describe("'get': retrieve all cookies, 'set': add/update a cookie, 'clear': remove all cookies"),
848
+ cookie: z2.object({
849
+ name: z2.string(),
850
+ value: z2.string(),
851
+ domain: z2.string().optional(),
852
+ path: z2.string().optional(),
853
+ httpOnly: z2.boolean().optional(),
854
+ secure: z2.boolean().optional()
892
855
  }).optional().describe("Cookie data (required for 'set' action)")
893
856
  },
894
857
  ({ action, cookie }) => this.withLock(async () => {
@@ -910,10 +873,10 @@ ${refList}`
910
873
  "browser_storage",
911
874
  "Read, write, or clear browser localStorage/sessionStorage. Useful for managing client-side state, authentication tokens, or application preferences.",
912
875
  {
913
- action: import_zod2.z.enum(["get", "set", "clear"]).describe("'get': read value(s), 'set': write a key-value pair, 'clear': remove all entries"),
914
- kind: import_zod2.z.enum(["local", "session"]).optional().default("local").describe("'local' (persistent) or 'session' (cleared on tab close)"),
915
- key: import_zod2.z.string().optional().describe("Storage key to get or set. Omit key with 'get' to retrieve all entries."),
916
- value: import_zod2.z.string().optional().describe("Value to store (required for 'set' action)")
876
+ action: z2.enum(["get", "set", "clear"]).describe("'get': read value(s), 'set': write a key-value pair, 'clear': remove all entries"),
877
+ kind: z2.enum(["local", "session"]).optional().default("local").describe("'local' (persistent) or 'session' (cleared on tab close)"),
878
+ key: z2.string().optional().describe("Storage key to get or set. Omit key with 'get' to retrieve all entries."),
879
+ value: z2.string().optional().describe("Value to store (required for 'set' action)")
917
880
  },
918
881
  ({ action, kind, key, value }) => this.withLock(async () => {
919
882
  const page = requirePage();
@@ -941,16 +904,16 @@ ${refList}`
941
904
  "The 'accept' and 'promptText' params are only used with action='arm'."
942
905
  ].join("\n"),
943
906
  {
944
- action: import_zod2.z.enum(["arm", "wait"]).describe(
907
+ action: z2.enum(["arm", "wait"]).describe(
945
908
  "'arm' = register handler and return immediately; 'wait' = await the previously armed handler"
946
909
  ),
947
- accept: import_zod2.z.boolean().optional().default(true).describe(
910
+ accept: z2.boolean().optional().default(true).describe(
948
911
  "Accept (true) or dismiss (false) the dialog. Only used with action='arm'."
949
912
  ),
950
- promptText: import_zod2.z.string().optional().describe(
913
+ promptText: z2.string().optional().describe(
951
914
  "Text to enter if the dialog is a prompt. Only used with action='arm'."
952
915
  ),
953
- timeoutMs: import_zod2.z.number().optional().describe(
916
+ timeoutMs: z2.number().optional().describe(
954
917
  "Timeout in ms for 'wait' action (default: 30000). Increase for slow-loading dialogs."
955
918
  )
956
919
  },
@@ -982,13 +945,13 @@ ${refList}`
982
945
  };
983
946
 
984
947
  // src/tools/notebook.ts
985
- var import_zod3 = require("zod");
986
- var import_promises3 = __toESM(require("fs/promises"));
987
- var import_child_process2 = require("child_process");
988
- var import_util2 = require("util");
989
- var execAsync2 = (0, import_util2.promisify)(import_child_process2.exec);
948
+ import { z as z3 } from "zod";
949
+ import fs3 from "fs/promises";
950
+ import { exec as exec2 } from "child_process";
951
+ import { promisify as promisify2 } from "util";
952
+ var execAsync2 = promisify2(exec2);
990
953
  async function readNotebook(filePath) {
991
- const raw = await import_promises3.default.readFile(filePath, "utf-8");
954
+ const raw = await fs3.readFile(filePath, "utf-8");
992
955
  try {
993
956
  return JSON.parse(raw);
994
957
  } catch {
@@ -996,14 +959,14 @@ async function readNotebook(filePath) {
996
959
  }
997
960
  }
998
961
  async function writeNotebook(filePath, nb) {
999
- await import_promises3.default.writeFile(filePath, JSON.stringify(nb, null, 1), "utf-8");
962
+ await fs3.writeFile(filePath, JSON.stringify(nb, null, 1), "utf-8");
1000
963
  }
1001
964
  var NotebookTools = class {
1002
965
  register(server) {
1003
966
  server.tool(
1004
967
  "notebook_read",
1005
968
  "Read a Jupyter notebook (.ipynb) and return all cells with their types (code/markdown), source content, and output counts. Use this to understand notebook structure before making edits.",
1006
- { path: import_zod3.z.string().describe("Path to the .ipynb notebook file") },
969
+ { path: z3.string().describe("Path to the .ipynb notebook file") },
1007
970
  async ({ path: filePath }) => {
1008
971
  const nb = await readNotebook(filePath);
1009
972
  const cells = nb.cells.map((cell, i) => ({
@@ -1021,9 +984,9 @@ var NotebookTools = class {
1021
984
  "notebook_edit_cell",
1022
985
  "Replace the source code of a specific cell in a Jupyter notebook. Use notebook_read first to identify the correct cell index (0-based). Existing outputs for the cell are preserved \u2014 use notebook_execute to re-run.",
1023
986
  {
1024
- path: import_zod3.z.string().describe("Path to the .ipynb notebook file"),
1025
- cell_index: import_zod3.z.number().describe("Cell index to edit (0-based). Use notebook_read to find the right index."),
1026
- source: import_zod3.z.string().describe("New source code/content for the cell (replaces entire cell content)")
987
+ path: z3.string().describe("Path to the .ipynb notebook file"),
988
+ cell_index: z3.number().describe("Cell index to edit (0-based). Use notebook_read to find the right index."),
989
+ source: z3.string().describe("New source code/content for the cell (replaces entire cell content)")
1027
990
  },
1028
991
  async ({ path: filePath, cell_index, source }) => {
1029
992
  const nb = await readNotebook(filePath);
@@ -1046,8 +1009,8 @@ var NotebookTools = class {
1046
1009
  "If execution fails on a cell, the error is captured in the cell output and subsequent cells may not execute."
1047
1010
  ].join("\n"),
1048
1011
  {
1049
- path: import_zod3.z.string().describe("Path to the .ipynb notebook file to execute"),
1050
- timeout: import_zod3.z.number().optional().default(300).describe("Maximum execution time per cell in seconds (default: 300). Increase for cells with heavy computation.")
1012
+ path: z3.string().describe("Path to the .ipynb notebook file to execute"),
1013
+ timeout: z3.number().optional().default(300).describe("Maximum execution time per cell in seconds (default: 300). Increase for cells with heavy computation.")
1051
1014
  },
1052
1015
  async ({ path: filePath, timeout }) => {
1053
1016
  const nbconvertArgs = `nbconvert --to notebook --execute --inplace "${filePath}" --ExecutePreprocessor.timeout=${timeout}`;
@@ -1078,10 +1041,10 @@ var NotebookTools = class {
1078
1041
  "notebook_add_cell",
1079
1042
  "Insert a new cell into a Jupyter notebook. If position is omitted, the cell is appended at the end. Use cell_type='code' for executable Python cells, 'markdown' for documentation/text cells.",
1080
1043
  {
1081
- path: import_zod3.z.string().describe("Path to the .ipynb notebook file"),
1082
- cell_type: import_zod3.z.enum(["code", "markdown"]).describe("'code' for executable cells, 'markdown' for text/documentation cells"),
1083
- source: import_zod3.z.string().describe("Cell source content (Python code or Markdown text)"),
1084
- position: import_zod3.z.number().optional().describe("Insert position (0-based index). Omit to append at the end. If position exceeds cell count, appends at end with a warning.")
1044
+ path: z3.string().describe("Path to the .ipynb notebook file"),
1045
+ cell_type: z3.enum(["code", "markdown"]).describe("'code' for executable cells, 'markdown' for text/documentation cells"),
1046
+ source: z3.string().describe("Cell source content (Python code or Markdown text)"),
1047
+ position: z3.number().optional().describe("Insert position (0-based index). Omit to append at the end. If position exceeds cell count, appends at end with a warning.")
1085
1048
  },
1086
1049
  async ({ path: filePath, cell_type: cellType, source, position }) => {
1087
1050
  const nb = await readNotebook(filePath);
@@ -1114,8 +1077,8 @@ var NotebookTools = class {
1114
1077
  "notebook_delete_cell",
1115
1078
  "Delete a cell from a Jupyter notebook by its 0-based index. Use notebook_read first to verify the cell content before deletion. This action cannot be undone.",
1116
1079
  {
1117
- path: import_zod3.z.string().describe("Path to the .ipynb notebook file"),
1118
- cell_index: import_zod3.z.number().describe("Cell index to delete (0-based). Use notebook_read first to verify content.")
1080
+ path: z3.string().describe("Path to the .ipynb notebook file"),
1081
+ cell_index: z3.number().describe("Cell index to delete (0-based). Use notebook_read first to verify content.")
1119
1082
  },
1120
1083
  async ({ path: filePath, cell_index }) => {
1121
1084
  const nb = await readNotebook(filePath);
@@ -1131,11 +1094,11 @@ var NotebookTools = class {
1131
1094
  };
1132
1095
 
1133
1096
  // src/tools/device.ts
1134
- var import_child_process3 = require("child_process");
1135
- var import_util3 = require("util");
1136
- var import_zod4 = require("zod");
1137
- var import_node_notifier = __toESM(require("node-notifier"));
1138
- var execAsync3 = (0, import_util3.promisify)(import_child_process3.exec);
1097
+ import { exec as exec3 } from "child_process";
1098
+ import { promisify as promisify3 } from "util";
1099
+ import { z as z4 } from "zod";
1100
+ import notifier from "node-notifier";
1101
+ var execAsync3 = promisify3(exec3);
1139
1102
  var screenRecordPid = null;
1140
1103
  function platform() {
1141
1104
  if (process.platform === "darwin") return "mac";
@@ -1153,7 +1116,7 @@ var DeviceTools = class {
1153
1116
  "Requires a connected camera with OS permissions granted. If output_path is provided, the file is also saved to disk."
1154
1117
  ].join("\n"),
1155
1118
  {
1156
- output_path: import_zod4.z.string().optional().describe("File path to save the captured photo. If omitted, returns image data only (temp file auto-cleaned).")
1119
+ output_path: z4.string().optional().describe("File path to save the captured photo. If omitted, returns image data only (temp file auto-cleaned).")
1157
1120
  },
1158
1121
  async ({ output_path }) => {
1159
1122
  const p = platform();
@@ -1191,13 +1154,13 @@ Please check if a camera is connected.` }],
1191
1154
  "notification_send",
1192
1155
  "Send a native OS notification (banner/toast) to the user's desktop. Use for task completion alerts, reminders, or important status updates. The notification appears even when the terminal is not focused.",
1193
1156
  {
1194
- title: import_zod4.z.string().describe("Notification title (displayed prominently)"),
1195
- message: import_zod4.z.string().describe("Notification body text")
1157
+ title: z4.string().describe("Notification title (displayed prominently)"),
1158
+ message: z4.string().describe("Notification body text")
1196
1159
  },
1197
1160
  async ({ title, message }) => {
1198
1161
  try {
1199
1162
  await new Promise((resolve, reject) => {
1200
- import_node_notifier.default.notify(
1163
+ notifier.notify(
1201
1164
  { title, message },
1202
1165
  (err) => {
1203
1166
  if (err) reject(err);
@@ -1229,7 +1192,7 @@ Please check if a camera is connected.` }],
1229
1192
  "clipboard_write",
1230
1193
  "Write text to the system clipboard, replacing its current contents. Use to prepare content for the user to paste elsewhere.",
1231
1194
  {
1232
- text: import_zod4.z.string().describe("Text to copy to the clipboard")
1195
+ text: z4.string().describe("Text to copy to the clipboard")
1233
1196
  },
1234
1197
  async ({ text }) => {
1235
1198
  const p = platform();
@@ -1251,8 +1214,8 @@ Please check if a camera is connected.` }],
1251
1214
  "Platform-specific: macOS (screencapture -v), Windows/Linux (ffmpeg)."
1252
1215
  ].join("\n"),
1253
1216
  {
1254
- action: import_zod4.z.enum(["start", "stop"]).describe("'start': begin recording, 'stop': end recording and save the file"),
1255
- output_path: import_zod4.z.string().optional().describe("Output file path (used with 'start'). Default: /tmp/junis_record_<timestamp>.mp4")
1217
+ action: z4.enum(["start", "stop"]).describe("'start': begin recording, 'stop': end recording and save the file"),
1218
+ output_path: z4.string().optional().describe("Output file path (used with 'start'). Default: /tmp/junis_record_<timestamp>.mp4")
1256
1219
  },
1257
1220
  async ({ action, output_path }) => {
1258
1221
  const p = platform();
@@ -1317,7 +1280,7 @@ Please check if a camera is connected.` }],
1317
1280
  "audio_play",
1318
1281
  "Play an audio file through the device's speakers. Supports MP3, WAV, AAC, and other common formats. Playback is synchronous \u2014 the tool returns after playback completes. Platform-specific: macOS (afplay), Windows/Linux (ffplay).",
1319
1282
  {
1320
- file_path: import_zod4.z.string().describe("Absolute path to the audio file to play")
1283
+ file_path: z4.string().describe("Absolute path to the audio file to play")
1321
1284
  },
1322
1285
  async ({ file_path }) => {
1323
1286
  const p = platform();
@@ -1334,12 +1297,12 @@ Please check if a camera is connected.` }],
1334
1297
  };
1335
1298
 
1336
1299
  // src/setup/peekaboo-installer.ts
1337
- var import_child_process4 = require("child_process");
1338
- var import_util4 = require("util");
1339
- var import_os = require("os");
1340
- var execFileAsync2 = (0, import_util4.promisify)(import_child_process4.execFile);
1300
+ import { execFile as execFile2 } from "child_process";
1301
+ import { promisify as promisify4 } from "util";
1302
+ import { platform as platform2 } from "os";
1303
+ var execFileAsync2 = promisify4(execFile2);
1341
1304
  async function ensurePeekaboo() {
1342
- if ((0, import_os.platform)() !== "darwin") return false;
1305
+ if (platform2() !== "darwin") return false;
1343
1306
  try {
1344
1307
  await execFileAsync2("which", ["peekaboo"]);
1345
1308
  return true;
@@ -1359,9 +1322,9 @@ async function ensurePeekaboo() {
1359
1322
  }
1360
1323
 
1361
1324
  // src/tools/desktop.ts
1362
- var import_execa = require("execa");
1363
- var import_zod5 = require("zod");
1364
- var import_fs = __toESM(require("fs"));
1325
+ import { execa } from "execa";
1326
+ import { z as z5 } from "zod";
1327
+ import fs4 from "fs";
1365
1328
  var APP_BLACKLIST = /* @__PURE__ */ new Set([
1366
1329
  "Terminal",
1367
1330
  "iTerm2",
@@ -1374,7 +1337,7 @@ var MAX_CONSECUTIVE_FAILURES = 2;
1374
1337
  async function peekaboo(args) {
1375
1338
  consecutiveFailures = 0;
1376
1339
  try {
1377
- const { stdout } = await (0, import_execa.execa)("peekaboo", [...args, "--json-output"]);
1340
+ const { stdout } = await execa("peekaboo", [...args, "--json-output"]);
1378
1341
  consecutiveFailures = 0;
1379
1342
  return JSON.parse(stdout);
1380
1343
  } catch (err) {
@@ -1404,7 +1367,7 @@ var DesktopTools = class {
1404
1367
  "SAFETY: Terminal, iTerm, and Finder are blocked. Two consecutive failures trigger an automatic safety stop."
1405
1368
  ].join("\n"),
1406
1369
  {
1407
- app: import_zod5.z.string().optional().describe("App name to target (e.g. 'Safari', 'Notes', 'Google Chrome'). Omit for the frontmost app.")
1370
+ app: z5.string().optional().describe("App name to target (e.g. 'Safari', 'Notes', 'Google Chrome'). Omit for the frontmost app.")
1408
1371
  },
1409
1372
  async ({ app }) => {
1410
1373
  checkBlacklist(app);
@@ -1438,10 +1401,10 @@ var DesktopTools = class {
1438
1401
  "SAFETY: Terminal, iTerm, and Finder are blocked. Two consecutive failures trigger automatic safety stop."
1439
1402
  ].join("\n"),
1440
1403
  {
1441
- on: import_zod5.z.string().describe("Element label, accessibility ID, or 'x,y' coordinates to click"),
1442
- app: import_zod5.z.string().optional().describe("App name to target (e.g. 'Safari')"),
1443
- snapshot: import_zod5.z.string().optional().describe("snapshotId from desktop_see for cached interaction (240x faster)"),
1444
- doubleClick: import_zod5.z.boolean().optional().default(false).describe("Double-click instead of single click")
1404
+ on: z5.string().describe("Element label, accessibility ID, or 'x,y' coordinates to click"),
1405
+ app: z5.string().optional().describe("App name to target (e.g. 'Safari')"),
1406
+ snapshot: z5.string().optional().describe("snapshotId from desktop_see for cached interaction (240x faster)"),
1407
+ doubleClick: z5.boolean().optional().default(false).describe("Double-click instead of single click")
1445
1408
  },
1446
1409
  async ({ on, app, snapshot, doubleClick }) => {
1447
1410
  checkBlacklist(app);
@@ -1463,8 +1426,8 @@ var DesktopTools = class {
1463
1426
  "SAFETY: Terminal, iTerm, and Finder are blocked. Use desktop_see first to verify the correct element is focused."
1464
1427
  ].join("\n"),
1465
1428
  {
1466
- text: import_zod5.z.string().describe("Text to type into the focused element"),
1467
- app: import_zod5.z.string().optional().describe("App name to focus before typing")
1429
+ text: z5.string().describe("Text to type into the focused element"),
1430
+ app: z5.string().optional().describe("App name to focus before typing")
1468
1431
  },
1469
1432
  async ({ text, app }) => {
1470
1433
  checkBlacklist(app);
@@ -1486,8 +1449,8 @@ var DesktopTools = class {
1486
1449
  "SAFETY: Terminal, iTerm, and Finder are blocked."
1487
1450
  ].join("\n"),
1488
1451
  {
1489
- keys: import_zod5.z.string().describe("Comma-separated key combination (e.g. 'cmd,c', 'cmd,shift,t', 'escape', 'cmd,option,i')"),
1490
- app: import_zod5.z.string().optional().describe("App name to target")
1452
+ keys: z5.string().describe("Comma-separated key combination (e.g. 'cmd,c', 'cmd,shift,t', 'escape', 'cmd,option,i')"),
1453
+ app: z5.string().optional().describe("App name to target")
1491
1454
  },
1492
1455
  async ({ keys, app }) => {
1493
1456
  checkBlacklist(app);
@@ -1503,10 +1466,10 @@ var DesktopTools = class {
1503
1466
  "desktop_scroll",
1504
1467
  "Scroll within a macOS application or specific UI element. Use 'ticks' to control scroll distance (default: 3). Can target a specific element by label or ID with the 'on' parameter.",
1505
1468
  {
1506
- direction: import_zod5.z.enum(["up", "down", "left", "right"]).describe("Scroll direction"),
1507
- ticks: import_zod5.z.number().optional().default(3).describe("Number of scroll ticks (default: 3). Higher = more scrolling."),
1508
- on: import_zod5.z.string().optional().describe("Element label or ID to scroll within (from desktop_see). Omit to scroll the active area."),
1509
- app: import_zod5.z.string().optional().describe("App name to target")
1469
+ direction: z5.enum(["up", "down", "left", "right"]).describe("Scroll direction"),
1470
+ ticks: z5.number().optional().default(3).describe("Number of scroll ticks (default: 3). Higher = more scrolling."),
1471
+ on: z5.string().optional().describe("Element label or ID to scroll within (from desktop_see). Omit to scroll the active area."),
1472
+ app: z5.string().optional().describe("App name to target")
1510
1473
  },
1511
1474
  async ({ direction, ticks, on, app }) => {
1512
1475
  checkBlacklist(app);
@@ -1525,7 +1488,7 @@ var DesktopTools = class {
1525
1488
  {},
1526
1489
  async () => {
1527
1490
  try {
1528
- const { stdout } = await (0, import_execa.execa)("peekaboo", ["list", "apps", "--json"]);
1491
+ const { stdout } = await execa("peekaboo", ["list", "apps", "--json"]);
1529
1492
  return {
1530
1493
  content: [{ type: "text", text: stdout }]
1531
1494
  };
@@ -1539,21 +1502,21 @@ var DesktopTools = class {
1539
1502
  "desktop_list_windows",
1540
1503
  "List all open windows on macOS, optionally filtered by app name. If no app is specified, lists windows for the frontmost application. Useful for identifying which windows are available for automation.",
1541
1504
  {
1542
- app: import_zod5.z.string().optional().describe("Filter by app name. Omit to query the frontmost app.")
1505
+ app: z5.string().optional().describe("Filter by app name. Omit to query the frontmost app.")
1543
1506
  },
1544
1507
  async ({ app }) => {
1545
1508
  checkBlacklist(app);
1546
1509
  try {
1547
1510
  let targetApp = app;
1548
1511
  if (!targetApp) {
1549
- const { stdout: stdout2 } = await (0, import_execa.execa)("osascript", [
1512
+ const { stdout: stdout2 } = await execa("osascript", [
1550
1513
  "-e",
1551
1514
  'tell application "System Events" to get name of first application process whose frontmost is true'
1552
1515
  ]);
1553
1516
  targetApp = stdout2.trim();
1554
1517
  }
1555
1518
  const args = ["list", "windows", "--app", targetApp, "--json"];
1556
- const { stdout } = await (0, import_execa.execa)("peekaboo", args);
1519
+ const { stdout } = await execa("peekaboo", args);
1557
1520
  return {
1558
1521
  content: [{ type: "text", text: stdout }]
1559
1522
  };
@@ -1572,8 +1535,8 @@ var DesktopTools = class {
1572
1535
  "Prefer desktop_see (Accessibility Tree) for understanding UI structure \u2014 use screenshot only when visual appearance matters (layouts, images, colors)."
1573
1536
  ].join("\n"),
1574
1537
  {
1575
- app: import_zod5.z.string().optional().describe("Capture a specific app's window (by name)"),
1576
- mode: import_zod5.z.enum(["screen", "window"]).optional().default("screen").describe("'screen': full display capture, 'window': specific app window only")
1538
+ app: z5.string().optional().describe("Capture a specific app's window (by name)"),
1539
+ mode: z5.enum(["screen", "window"]).optional().default("screen").describe("'screen': full display capture, 'window': specific app window only")
1577
1540
  },
1578
1541
  async ({ app, mode }) => {
1579
1542
  checkBlacklist(app);
@@ -1584,7 +1547,7 @@ var DesktopTools = class {
1584
1547
  const files = data?.files;
1585
1548
  const filePath = files?.[0]?.path;
1586
1549
  if (filePath) {
1587
- const imageBuffer = import_fs.default.readFileSync(filePath);
1550
+ const imageBuffer = fs4.readFileSync(filePath);
1588
1551
  return {
1589
1552
  content: [{
1590
1553
  type: "image",
@@ -1607,15 +1570,15 @@ var DesktopTools = class {
1607
1570
  "The target app must be running and accessible."
1608
1571
  ].join("\n"),
1609
1572
  {
1610
- path: import_zod5.z.array(import_zod5.z.string()).describe("Menu path as array (e.g. ['File', 'Save'], ['Edit', 'Find', 'Find...'])"),
1611
- app: import_zod5.z.string().optional().describe("App name to target. Omit for the frontmost app.")
1573
+ path: z5.array(z5.string()).describe("Menu path as array (e.g. ['File', 'Save'], ['Edit', 'Find', 'Find...'])"),
1574
+ app: z5.string().optional().describe("App name to target. Omit for the frontmost app.")
1612
1575
  },
1613
1576
  async ({ path: path2, app }) => {
1614
1577
  checkBlacklist(app);
1615
1578
  const args = ["menu", "click", "--path", path2.join(" > ")];
1616
1579
  if (app) args.push("--app", app);
1617
1580
  try {
1618
- const { stdout } = await (0, import_execa.execa)("peekaboo", args);
1581
+ const { stdout } = await execa("peekaboo", args);
1619
1582
  return {
1620
1583
  content: [{ type: "text", text: stdout || "Menu click executed" }]
1621
1584
  };
@@ -1633,7 +1596,7 @@ var mcpPort = 3e3;
1633
1596
  var globalBrowserTools = null;
1634
1597
  var desktopToolsEnabled = false;
1635
1598
  function createMcpServer() {
1636
- const server = new import_mcp.McpServer({
1599
+ const server = new McpServer({
1637
1600
  name: "junis",
1638
1601
  version: "0.1.0"
1639
1602
  });
@@ -1763,7 +1726,7 @@ async function startMCPServer(port) {
1763
1726
  console.log("\u2705 Peekaboo available \u2014 desktop tools enabled");
1764
1727
  }
1765
1728
  let resolvedPort = port;
1766
- const httpServer = (0, import_http.createServer)(
1729
+ const httpServer = createServer(
1767
1730
  async (req, res) => {
1768
1731
  try {
1769
1732
  if (req.method === "OPTIONS") {
@@ -1777,7 +1740,7 @@ async function startMCPServer(port) {
1777
1740
  if (url === "/mcp") {
1778
1741
  if (req.method === "POST") {
1779
1742
  const mcpServer = createMcpServer();
1780
- const transport = new import_streamableHttp.StreamableHTTPServerTransport({
1743
+ const transport = new StreamableHTTPServerTransport({
1781
1744
  sessionIdGenerator: void 0
1782
1745
  // stateless
1783
1746
  });
@@ -1926,10 +1889,9 @@ async function handleMCPRequest(id, payload) {
1926
1889
  }
1927
1890
  return null;
1928
1891
  }
1929
- // Annotate the CommonJS export names for ESM import in node:
1930
- 0 && (module.exports = {
1892
+ export {
1931
1893
  checkPermission,
1932
1894
  handleMCPRequest,
1933
1895
  startMCPServer,
1934
1896
  toolPermissions
1935
- });
1897
+ };