junis 0.3.7 → 0.3.8
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/dist/cli/index.js +64 -18
- package/dist/server/mcp.js +52 -16
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -1626,7 +1626,6 @@ var APP_BLACKLIST = /* @__PURE__ */ new Set([
|
|
|
1626
1626
|
var consecutiveFailures = 0;
|
|
1627
1627
|
var MAX_CONSECUTIVE_FAILURES = 2;
|
|
1628
1628
|
async function peekaboo(args) {
|
|
1629
|
-
consecutiveFailures = 0;
|
|
1630
1629
|
try {
|
|
1631
1630
|
const { stdout } = await execa("peekaboo", [...args, "--json-output"]);
|
|
1632
1631
|
consecutiveFailures = 0;
|
|
@@ -1650,10 +1649,10 @@ var DesktopTools = class {
|
|
|
1650
1649
|
server.tool(
|
|
1651
1650
|
"desktop_see",
|
|
1652
1651
|
[
|
|
1653
|
-
"Capture the macOS Accessibility Tree snapshot for a running application. Returns structured element list with IDs, roles, labels, and positions.",
|
|
1652
|
+
"Capture the macOS Accessibility Tree snapshot for a running application. Returns a structured element list with IDs, roles, labels, and positions.",
|
|
1654
1653
|
"",
|
|
1655
|
-
"WORKFLOW:
|
|
1656
|
-
"Pass the returned snapshotId to subsequent calls for 240x speed improvement (cached lookup vs. full re-scan).",
|
|
1654
|
+
"WORKFLOW: List running apps \u2192 capture accessibility tree \u2192 find target element by role/label \u2192 interact using element ID or label (click, type, scroll).",
|
|
1655
|
+
"Pass the returned snapshotId to subsequent interaction calls for 240x speed improvement (cached lookup vs. full re-scan).",
|
|
1657
1656
|
"",
|
|
1658
1657
|
"SAFETY: Terminal, iTerm, and Finder are blocked. Two consecutive failures trigger an automatic safety stop."
|
|
1659
1658
|
].join("\n"),
|
|
@@ -1686,15 +1685,15 @@ var DesktopTools = class {
|
|
|
1686
1685
|
[
|
|
1687
1686
|
"Click a macOS UI element by its accessibility label, ID, or x,y coordinates.",
|
|
1688
1687
|
"",
|
|
1689
|
-
"The 'on' parameter accepts: element label text (e.g. 'Save'), accessibility ID from
|
|
1690
|
-
"For faster interaction, pass the snapshotId from a recent
|
|
1688
|
+
"The 'on' parameter accepts: element label text (e.g. 'Save'), accessibility ID from a previous accessibility tree capture, or coordinates as 'x,y' string.",
|
|
1689
|
+
"For faster interaction, pass the snapshotId from a recent accessibility tree capture.",
|
|
1691
1690
|
"",
|
|
1692
1691
|
"SAFETY: Terminal, iTerm, and Finder are blocked. Two consecutive failures trigger automatic safety stop."
|
|
1693
1692
|
].join("\n"),
|
|
1694
1693
|
{
|
|
1695
1694
|
on: z5.string().describe("Element label, accessibility ID, or 'x,y' coordinates to click"),
|
|
1696
1695
|
app: z5.string().optional().describe("App name to target (e.g. 'Safari')"),
|
|
1697
|
-
snapshot: z5.string().optional().describe("snapshotId from
|
|
1696
|
+
snapshot: z5.string().optional().describe("snapshotId from a previous accessibility tree capture for cached interaction (240x faster)"),
|
|
1698
1697
|
doubleClick: z5.boolean().optional().default(false).describe("Double-click instead of single click")
|
|
1699
1698
|
},
|
|
1700
1699
|
async ({ on, app, snapshot, doubleClick }) => {
|
|
@@ -1714,7 +1713,9 @@ var DesktopTools = class {
|
|
|
1714
1713
|
[
|
|
1715
1714
|
"Type text into the currently focused UI element on macOS. The text is sent as keyboard input character-by-character.",
|
|
1716
1715
|
"",
|
|
1717
|
-
"
|
|
1716
|
+
"IMPORTANT: Always capture the accessibility tree first to verify the correct element is focused before typing.",
|
|
1717
|
+
"",
|
|
1718
|
+
"SAFETY: Terminal, iTerm, and Finder are blocked."
|
|
1718
1719
|
].join("\n"),
|
|
1719
1720
|
{
|
|
1720
1721
|
text: z5.string().describe("Text to type into the focused element"),
|
|
@@ -1755,11 +1756,17 @@ var DesktopTools = class {
|
|
|
1755
1756
|
);
|
|
1756
1757
|
server.tool(
|
|
1757
1758
|
"desktop_scroll",
|
|
1758
|
-
|
|
1759
|
+
[
|
|
1760
|
+
"Scroll within a macOS application or specific UI element.",
|
|
1761
|
+
"",
|
|
1762
|
+
"Use 'ticks' to control scroll distance (default: 3, higher = more scrolling). Can target a specific element by label or ID from a previous accessibility tree capture.",
|
|
1763
|
+
"",
|
|
1764
|
+
"SAFETY: Terminal, iTerm, and Finder are blocked."
|
|
1765
|
+
].join("\n"),
|
|
1759
1766
|
{
|
|
1760
1767
|
direction: z5.enum(["up", "down", "left", "right"]).describe("Scroll direction"),
|
|
1761
1768
|
ticks: z5.number().optional().default(3).describe("Number of scroll ticks (default: 3). Higher = more scrolling."),
|
|
1762
|
-
on: z5.string().optional().describe("Element label or ID to scroll within (from
|
|
1769
|
+
on: z5.string().optional().describe("Element label or ID to scroll within (from a previous accessibility tree capture). Omit to scroll the active area."),
|
|
1763
1770
|
app: z5.string().optional().describe("App name to target")
|
|
1764
1771
|
},
|
|
1765
1772
|
async ({ direction, ticks, on, app }) => {
|
|
@@ -1775,23 +1782,38 @@ var DesktopTools = class {
|
|
|
1775
1782
|
);
|
|
1776
1783
|
server.tool(
|
|
1777
1784
|
"desktop_list_apps",
|
|
1778
|
-
|
|
1785
|
+
[
|
|
1786
|
+
"List all currently running applications on macOS. Returns app names usable as the 'app' parameter in all other desktop tools.",
|
|
1787
|
+
"",
|
|
1788
|
+
"WORKFLOW: This is typically the first step \u2014 identify which apps are running before interacting with their UI.",
|
|
1789
|
+
"The returned names are exact strings to pass as 'app' (e.g. 'Safari', 'Google Chrome', 'Notes')."
|
|
1790
|
+
].join("\n"),
|
|
1779
1791
|
{},
|
|
1780
1792
|
async () => {
|
|
1781
1793
|
try {
|
|
1782
1794
|
const { stdout } = await execa("peekaboo", ["list", "apps", "--json"]);
|
|
1795
|
+
consecutiveFailures = 0;
|
|
1783
1796
|
return {
|
|
1784
1797
|
content: [{ type: "text", text: stdout }]
|
|
1785
1798
|
};
|
|
1786
1799
|
} catch (err) {
|
|
1787
1800
|
consecutiveFailures++;
|
|
1801
|
+
if (consecutiveFailures >= MAX_CONSECUTIVE_FAILURES) {
|
|
1802
|
+
consecutiveFailures = 0;
|
|
1803
|
+
throw new Error(`peekaboo failed ${MAX_CONSECUTIVE_FAILURES} times in a row. Auto-stopped for safety. Last error: ${err.message}`);
|
|
1804
|
+
}
|
|
1788
1805
|
throw err;
|
|
1789
1806
|
}
|
|
1790
1807
|
}
|
|
1791
1808
|
);
|
|
1792
1809
|
server.tool(
|
|
1793
1810
|
"desktop_list_windows",
|
|
1794
|
-
|
|
1811
|
+
[
|
|
1812
|
+
"List all open windows on macOS, optionally filtered by app name. Returns window titles and metadata.",
|
|
1813
|
+
"",
|
|
1814
|
+
"If no app is specified, lists windows for the frontmost application.",
|
|
1815
|
+
"Use this after identifying running apps to find specific windows before capturing the accessibility tree or taking a screenshot."
|
|
1816
|
+
].join("\n"),
|
|
1795
1817
|
{
|
|
1796
1818
|
app: z5.string().optional().describe("Filter by app name. Omit to query the frontmost app.")
|
|
1797
1819
|
},
|
|
@@ -1808,11 +1830,16 @@ var DesktopTools = class {
|
|
|
1808
1830
|
}
|
|
1809
1831
|
const args = ["list", "windows", "--app", targetApp, "--json"];
|
|
1810
1832
|
const { stdout } = await execa("peekaboo", args);
|
|
1833
|
+
consecutiveFailures = 0;
|
|
1811
1834
|
return {
|
|
1812
1835
|
content: [{ type: "text", text: stdout }]
|
|
1813
1836
|
};
|
|
1814
1837
|
} catch (err) {
|
|
1815
1838
|
consecutiveFailures++;
|
|
1839
|
+
if (consecutiveFailures >= MAX_CONSECUTIVE_FAILURES) {
|
|
1840
|
+
consecutiveFailures = 0;
|
|
1841
|
+
throw new Error(`peekaboo failed ${MAX_CONSECUTIVE_FAILURES} times in a row. Auto-stopped for safety. Last error: ${err.message}`);
|
|
1842
|
+
}
|
|
1816
1843
|
throw err;
|
|
1817
1844
|
}
|
|
1818
1845
|
}
|
|
@@ -1820,10 +1847,12 @@ var DesktopTools = class {
|
|
|
1820
1847
|
server.tool(
|
|
1821
1848
|
"desktop_screenshot",
|
|
1822
1849
|
[
|
|
1823
|
-
"Take a high-quality macOS screenshot
|
|
1850
|
+
"Take a high-quality macOS screenshot (Retina display support). Returns base64 image data.",
|
|
1824
1851
|
"",
|
|
1825
1852
|
"MODES: 'screen' captures the full display, 'window' captures a specific app window.",
|
|
1826
|
-
"Prefer
|
|
1853
|
+
"TIP: Prefer the accessibility tree for understanding UI structure \u2014 use screenshots only when visual appearance matters (layouts, images, colors).",
|
|
1854
|
+
"",
|
|
1855
|
+
"SAFETY: Terminal, iTerm, and Finder are blocked."
|
|
1827
1856
|
].join("\n"),
|
|
1828
1857
|
{
|
|
1829
1858
|
app: z5.string().optional().describe("Capture a specific app's window (by name)"),
|
|
@@ -1855,10 +1884,12 @@ var DesktopTools = class {
|
|
|
1855
1884
|
server.tool(
|
|
1856
1885
|
"desktop_menu",
|
|
1857
1886
|
[
|
|
1858
|
-
"Click a menu bar item in a macOS application. Navigate nested menus by
|
|
1887
|
+
"Click a menu bar item in a macOS application. Navigate nested menus by providing path segments.",
|
|
1859
1888
|
"",
|
|
1860
1889
|
"Examples: ['File', 'New Tab'], ['Edit', 'Find', 'Find...'], ['View', 'Enter Full Screen'].",
|
|
1861
|
-
"The target app must be running
|
|
1890
|
+
"Omit the 'app' parameter to target the frontmost app. The target app must be running.",
|
|
1891
|
+
"",
|
|
1892
|
+
"SAFETY: Terminal, iTerm, and Finder are blocked."
|
|
1862
1893
|
].join("\n"),
|
|
1863
1894
|
{
|
|
1864
1895
|
path: z5.array(z5.string()).describe("Menu path as array (e.g. ['File', 'Save'], ['Edit', 'Find', 'Find...'])"),
|
|
@@ -1870,11 +1901,16 @@ var DesktopTools = class {
|
|
|
1870
1901
|
if (app) args.push("--app", app);
|
|
1871
1902
|
try {
|
|
1872
1903
|
const { stdout } = await execa("peekaboo", args);
|
|
1904
|
+
consecutiveFailures = 0;
|
|
1873
1905
|
return {
|
|
1874
1906
|
content: [{ type: "text", text: stdout || "Menu click executed" }]
|
|
1875
1907
|
};
|
|
1876
1908
|
} catch (err) {
|
|
1877
1909
|
consecutiveFailures++;
|
|
1910
|
+
if (consecutiveFailures >= MAX_CONSECUTIVE_FAILURES) {
|
|
1911
|
+
consecutiveFailures = 0;
|
|
1912
|
+
throw new Error(`peekaboo failed ${MAX_CONSECUTIVE_FAILURES} times in a row. Auto-stopped for safety. Last error: ${err.message}`);
|
|
1913
|
+
}
|
|
1878
1914
|
throw err;
|
|
1879
1915
|
}
|
|
1880
1916
|
}
|
|
@@ -2613,6 +2649,8 @@ program.command("start", { isDefault: true }).description("Start Junis agent con
|
|
|
2613
2649
|
console.log("");
|
|
2614
2650
|
}
|
|
2615
2651
|
} else {
|
|
2652
|
+
const oldDeviceKey = config2.device_key;
|
|
2653
|
+
const oldToken = config2.token;
|
|
2616
2654
|
const checked = await checkEnsureTeam(config2, "junis");
|
|
2617
2655
|
if (!checked) {
|
|
2618
2656
|
let waitingPrinted = false;
|
|
@@ -2633,7 +2671,10 @@ program.command("start", { isDefault: true }).description("Start Junis agent con
|
|
|
2633
2671
|
} else {
|
|
2634
2672
|
process.stdout.write("\xB7");
|
|
2635
2673
|
}
|
|
2636
|
-
}
|
|
2674
|
+
},
|
|
2675
|
+
oldDeviceKey,
|
|
2676
|
+
oldToken,
|
|
2677
|
+
oldDeviceKey
|
|
2637
2678
|
);
|
|
2638
2679
|
console.log("");
|
|
2639
2680
|
console.log(` \u2705 Authenticated as ${authResult.email ?? "your account"}`);
|
|
@@ -2799,6 +2840,8 @@ program.command("start", { isDefault: true }).description("Start Junis agent con
|
|
|
2799
2840
|
console.log("");
|
|
2800
2841
|
}
|
|
2801
2842
|
} else {
|
|
2843
|
+
const oldDeviceKey = config.device_key;
|
|
2844
|
+
const oldToken = config.token;
|
|
2802
2845
|
const checked = await checkEnsureTeam(config, "junis");
|
|
2803
2846
|
if (!checked) {
|
|
2804
2847
|
let waitingPrinted = false;
|
|
@@ -2819,7 +2862,10 @@ program.command("start", { isDefault: true }).description("Start Junis agent con
|
|
|
2819
2862
|
} else {
|
|
2820
2863
|
process.stdout.write("\xB7");
|
|
2821
2864
|
}
|
|
2822
|
-
}
|
|
2865
|
+
},
|
|
2866
|
+
oldDeviceKey,
|
|
2867
|
+
oldToken,
|
|
2868
|
+
oldDeviceKey
|
|
2823
2869
|
);
|
|
2824
2870
|
console.log("");
|
|
2825
2871
|
console.log(` \u2705 Authenticated as ${authResult.email ?? "your account"}`);
|
package/dist/server/mcp.js
CHANGED
|
@@ -1335,7 +1335,6 @@ var APP_BLACKLIST = /* @__PURE__ */ new Set([
|
|
|
1335
1335
|
var consecutiveFailures = 0;
|
|
1336
1336
|
var MAX_CONSECUTIVE_FAILURES = 2;
|
|
1337
1337
|
async function peekaboo(args) {
|
|
1338
|
-
consecutiveFailures = 0;
|
|
1339
1338
|
try {
|
|
1340
1339
|
const { stdout } = await execa("peekaboo", [...args, "--json-output"]);
|
|
1341
1340
|
consecutiveFailures = 0;
|
|
@@ -1359,10 +1358,10 @@ var DesktopTools = class {
|
|
|
1359
1358
|
server.tool(
|
|
1360
1359
|
"desktop_see",
|
|
1361
1360
|
[
|
|
1362
|
-
"Capture the macOS Accessibility Tree snapshot for a running application. Returns structured element list with IDs, roles, labels, and positions.",
|
|
1361
|
+
"Capture the macOS Accessibility Tree snapshot for a running application. Returns a structured element list with IDs, roles, labels, and positions.",
|
|
1363
1362
|
"",
|
|
1364
|
-
"WORKFLOW:
|
|
1365
|
-
"Pass the returned snapshotId to subsequent calls for 240x speed improvement (cached lookup vs. full re-scan).",
|
|
1363
|
+
"WORKFLOW: List running apps \u2192 capture accessibility tree \u2192 find target element by role/label \u2192 interact using element ID or label (click, type, scroll).",
|
|
1364
|
+
"Pass the returned snapshotId to subsequent interaction calls for 240x speed improvement (cached lookup vs. full re-scan).",
|
|
1366
1365
|
"",
|
|
1367
1366
|
"SAFETY: Terminal, iTerm, and Finder are blocked. Two consecutive failures trigger an automatic safety stop."
|
|
1368
1367
|
].join("\n"),
|
|
@@ -1395,15 +1394,15 @@ var DesktopTools = class {
|
|
|
1395
1394
|
[
|
|
1396
1395
|
"Click a macOS UI element by its accessibility label, ID, or x,y coordinates.",
|
|
1397
1396
|
"",
|
|
1398
|
-
"The 'on' parameter accepts: element label text (e.g. 'Save'), accessibility ID from
|
|
1399
|
-
"For faster interaction, pass the snapshotId from a recent
|
|
1397
|
+
"The 'on' parameter accepts: element label text (e.g. 'Save'), accessibility ID from a previous accessibility tree capture, or coordinates as 'x,y' string.",
|
|
1398
|
+
"For faster interaction, pass the snapshotId from a recent accessibility tree capture.",
|
|
1400
1399
|
"",
|
|
1401
1400
|
"SAFETY: Terminal, iTerm, and Finder are blocked. Two consecutive failures trigger automatic safety stop."
|
|
1402
1401
|
].join("\n"),
|
|
1403
1402
|
{
|
|
1404
1403
|
on: z5.string().describe("Element label, accessibility ID, or 'x,y' coordinates to click"),
|
|
1405
1404
|
app: z5.string().optional().describe("App name to target (e.g. 'Safari')"),
|
|
1406
|
-
snapshot: z5.string().optional().describe("snapshotId from
|
|
1405
|
+
snapshot: z5.string().optional().describe("snapshotId from a previous accessibility tree capture for cached interaction (240x faster)"),
|
|
1407
1406
|
doubleClick: z5.boolean().optional().default(false).describe("Double-click instead of single click")
|
|
1408
1407
|
},
|
|
1409
1408
|
async ({ on, app, snapshot, doubleClick }) => {
|
|
@@ -1423,7 +1422,9 @@ var DesktopTools = class {
|
|
|
1423
1422
|
[
|
|
1424
1423
|
"Type text into the currently focused UI element on macOS. The text is sent as keyboard input character-by-character.",
|
|
1425
1424
|
"",
|
|
1426
|
-
"
|
|
1425
|
+
"IMPORTANT: Always capture the accessibility tree first to verify the correct element is focused before typing.",
|
|
1426
|
+
"",
|
|
1427
|
+
"SAFETY: Terminal, iTerm, and Finder are blocked."
|
|
1427
1428
|
].join("\n"),
|
|
1428
1429
|
{
|
|
1429
1430
|
text: z5.string().describe("Text to type into the focused element"),
|
|
@@ -1464,11 +1465,17 @@ var DesktopTools = class {
|
|
|
1464
1465
|
);
|
|
1465
1466
|
server.tool(
|
|
1466
1467
|
"desktop_scroll",
|
|
1467
|
-
|
|
1468
|
+
[
|
|
1469
|
+
"Scroll within a macOS application or specific UI element.",
|
|
1470
|
+
"",
|
|
1471
|
+
"Use 'ticks' to control scroll distance (default: 3, higher = more scrolling). Can target a specific element by label or ID from a previous accessibility tree capture.",
|
|
1472
|
+
"",
|
|
1473
|
+
"SAFETY: Terminal, iTerm, and Finder are blocked."
|
|
1474
|
+
].join("\n"),
|
|
1468
1475
|
{
|
|
1469
1476
|
direction: z5.enum(["up", "down", "left", "right"]).describe("Scroll direction"),
|
|
1470
1477
|
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
|
|
1478
|
+
on: z5.string().optional().describe("Element label or ID to scroll within (from a previous accessibility tree capture). Omit to scroll the active area."),
|
|
1472
1479
|
app: z5.string().optional().describe("App name to target")
|
|
1473
1480
|
},
|
|
1474
1481
|
async ({ direction, ticks, on, app }) => {
|
|
@@ -1484,23 +1491,38 @@ var DesktopTools = class {
|
|
|
1484
1491
|
);
|
|
1485
1492
|
server.tool(
|
|
1486
1493
|
"desktop_list_apps",
|
|
1487
|
-
|
|
1494
|
+
[
|
|
1495
|
+
"List all currently running applications on macOS. Returns app names usable as the 'app' parameter in all other desktop tools.",
|
|
1496
|
+
"",
|
|
1497
|
+
"WORKFLOW: This is typically the first step \u2014 identify which apps are running before interacting with their UI.",
|
|
1498
|
+
"The returned names are exact strings to pass as 'app' (e.g. 'Safari', 'Google Chrome', 'Notes')."
|
|
1499
|
+
].join("\n"),
|
|
1488
1500
|
{},
|
|
1489
1501
|
async () => {
|
|
1490
1502
|
try {
|
|
1491
1503
|
const { stdout } = await execa("peekaboo", ["list", "apps", "--json"]);
|
|
1504
|
+
consecutiveFailures = 0;
|
|
1492
1505
|
return {
|
|
1493
1506
|
content: [{ type: "text", text: stdout }]
|
|
1494
1507
|
};
|
|
1495
1508
|
} catch (err) {
|
|
1496
1509
|
consecutiveFailures++;
|
|
1510
|
+
if (consecutiveFailures >= MAX_CONSECUTIVE_FAILURES) {
|
|
1511
|
+
consecutiveFailures = 0;
|
|
1512
|
+
throw new Error(`peekaboo failed ${MAX_CONSECUTIVE_FAILURES} times in a row. Auto-stopped for safety. Last error: ${err.message}`);
|
|
1513
|
+
}
|
|
1497
1514
|
throw err;
|
|
1498
1515
|
}
|
|
1499
1516
|
}
|
|
1500
1517
|
);
|
|
1501
1518
|
server.tool(
|
|
1502
1519
|
"desktop_list_windows",
|
|
1503
|
-
|
|
1520
|
+
[
|
|
1521
|
+
"List all open windows on macOS, optionally filtered by app name. Returns window titles and metadata.",
|
|
1522
|
+
"",
|
|
1523
|
+
"If no app is specified, lists windows for the frontmost application.",
|
|
1524
|
+
"Use this after identifying running apps to find specific windows before capturing the accessibility tree or taking a screenshot."
|
|
1525
|
+
].join("\n"),
|
|
1504
1526
|
{
|
|
1505
1527
|
app: z5.string().optional().describe("Filter by app name. Omit to query the frontmost app.")
|
|
1506
1528
|
},
|
|
@@ -1517,11 +1539,16 @@ var DesktopTools = class {
|
|
|
1517
1539
|
}
|
|
1518
1540
|
const args = ["list", "windows", "--app", targetApp, "--json"];
|
|
1519
1541
|
const { stdout } = await execa("peekaboo", args);
|
|
1542
|
+
consecutiveFailures = 0;
|
|
1520
1543
|
return {
|
|
1521
1544
|
content: [{ type: "text", text: stdout }]
|
|
1522
1545
|
};
|
|
1523
1546
|
} catch (err) {
|
|
1524
1547
|
consecutiveFailures++;
|
|
1548
|
+
if (consecutiveFailures >= MAX_CONSECUTIVE_FAILURES) {
|
|
1549
|
+
consecutiveFailures = 0;
|
|
1550
|
+
throw new Error(`peekaboo failed ${MAX_CONSECUTIVE_FAILURES} times in a row. Auto-stopped for safety. Last error: ${err.message}`);
|
|
1551
|
+
}
|
|
1525
1552
|
throw err;
|
|
1526
1553
|
}
|
|
1527
1554
|
}
|
|
@@ -1529,10 +1556,12 @@ var DesktopTools = class {
|
|
|
1529
1556
|
server.tool(
|
|
1530
1557
|
"desktop_screenshot",
|
|
1531
1558
|
[
|
|
1532
|
-
"Take a high-quality macOS screenshot
|
|
1559
|
+
"Take a high-quality macOS screenshot (Retina display support). Returns base64 image data.",
|
|
1533
1560
|
"",
|
|
1534
1561
|
"MODES: 'screen' captures the full display, 'window' captures a specific app window.",
|
|
1535
|
-
"Prefer
|
|
1562
|
+
"TIP: Prefer the accessibility tree for understanding UI structure \u2014 use screenshots only when visual appearance matters (layouts, images, colors).",
|
|
1563
|
+
"",
|
|
1564
|
+
"SAFETY: Terminal, iTerm, and Finder are blocked."
|
|
1536
1565
|
].join("\n"),
|
|
1537
1566
|
{
|
|
1538
1567
|
app: z5.string().optional().describe("Capture a specific app's window (by name)"),
|
|
@@ -1564,10 +1593,12 @@ var DesktopTools = class {
|
|
|
1564
1593
|
server.tool(
|
|
1565
1594
|
"desktop_menu",
|
|
1566
1595
|
[
|
|
1567
|
-
"Click a menu bar item in a macOS application. Navigate nested menus by
|
|
1596
|
+
"Click a menu bar item in a macOS application. Navigate nested menus by providing path segments.",
|
|
1568
1597
|
"",
|
|
1569
1598
|
"Examples: ['File', 'New Tab'], ['Edit', 'Find', 'Find...'], ['View', 'Enter Full Screen'].",
|
|
1570
|
-
"The target app must be running
|
|
1599
|
+
"Omit the 'app' parameter to target the frontmost app. The target app must be running.",
|
|
1600
|
+
"",
|
|
1601
|
+
"SAFETY: Terminal, iTerm, and Finder are blocked."
|
|
1571
1602
|
].join("\n"),
|
|
1572
1603
|
{
|
|
1573
1604
|
path: z5.array(z5.string()).describe("Menu path as array (e.g. ['File', 'Save'], ['Edit', 'Find', 'Find...'])"),
|
|
@@ -1579,11 +1610,16 @@ var DesktopTools = class {
|
|
|
1579
1610
|
if (app) args.push("--app", app);
|
|
1580
1611
|
try {
|
|
1581
1612
|
const { stdout } = await execa("peekaboo", args);
|
|
1613
|
+
consecutiveFailures = 0;
|
|
1582
1614
|
return {
|
|
1583
1615
|
content: [{ type: "text", text: stdout || "Menu click executed" }]
|
|
1584
1616
|
};
|
|
1585
1617
|
} catch (err) {
|
|
1586
1618
|
consecutiveFailures++;
|
|
1619
|
+
if (consecutiveFailures >= MAX_CONSECUTIVE_FAILURES) {
|
|
1620
|
+
consecutiveFailures = 0;
|
|
1621
|
+
throw new Error(`peekaboo failed ${MAX_CONSECUTIVE_FAILURES} times in a row. Auto-stopped for safety. Last error: ${err.message}`);
|
|
1622
|
+
}
|
|
1587
1623
|
throw err;
|
|
1588
1624
|
}
|
|
1589
1625
|
}
|