cicy-desktop 1.0.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/.github/workflows/build.yml +85 -0
- package/.kiro/steering/dev-workflow.md +166 -0
- package/AGENTS.md +247 -0
- package/CLAUDE.md +162 -0
- package/DOCKER.md +85 -0
- package/Dockerfile +46 -0
- package/README.md +720 -0
- package/TODO-anti-detection.md +326 -0
- package/bin/cicy +176 -0
- package/bin/preinstall.sh +32 -0
- package/copy-to-desktop.sh +26 -0
- package/docs/AUTOMATION-API.md +342 -0
- package/docs/REQUEST_MONITORING.md +435 -0
- package/docs/REST-API-FEATURE.md +155 -0
- package/docs/REST-API.md +319 -0
- package/docs/feature-distributed-multi-agent.md +555 -0
- package/docs/yaml.md +255 -0
- package/electron-mcp-fixed.command +134 -0
- package/electron-mcp-simple.command +135 -0
- package/electron-mcp.command +92 -0
- package/generate-openapi.js +158 -0
- package/jest.config.js +10 -0
- package/jest.setup.global.js +13 -0
- package/jest.teardown.global.js +7 -0
- package/package.json +75 -0
- package/service.sh +164 -0
- package/src/config.js +8 -0
- package/src/extension/inject.js +135 -0
- package/src/main-old.js +837 -0
- package/src/main.js +403 -0
- package/src/preload-rpc.js +4 -0
- package/src/server/args-parser.js +37 -0
- package/src/server/electron-setup.js +33 -0
- package/src/server/express-app.js +166 -0
- package/src/server/logging.js +58 -0
- package/src/server/mcp-server.js +53 -0
- package/src/server/tool-registry.js +77 -0
- package/src/server/ui-routes.js +81 -0
- package/src/swagger-ui.html +41 -0
- package/src/tools/account-tools.js +194 -0
- package/src/tools/automation-tools.js +297 -0
- package/src/tools/cdp-tools.js +444 -0
- package/src/tools/clipboard-tools.js +180 -0
- package/src/tools/download-tools.js +57 -0
- package/src/tools/exec-js.js +297 -0
- package/src/tools/exec-tools.js +139 -0
- package/src/tools/file-tools.js +212 -0
- package/src/tools/hook-chatgpt.js +489 -0
- package/src/tools/hook-gemini.js +454 -0
- package/src/tools/index.js +19 -0
- package/src/tools/ipc-bridge.js +31 -0
- package/src/tools/ping.js +60 -0
- package/src/tools/r-reset.js +28 -0
- package/src/tools/screenshot-tools.js +28 -0
- package/src/tools/system-tools.js +531 -0
- package/src/tools/window-tools.js +882 -0
- package/src/ui.html +914 -0
- package/src/utils/auth.js +81 -0
- package/src/utils/cdp-utils.js +8 -0
- package/src/utils/download-manager.js +41 -0
- package/src/utils/process-utils.js +185 -0
- package/src/utils/snapshot-utils.js +56 -0
- package/src/utils/window-monitor.js +605 -0
- package/src/utils/window-state.js +137 -0
- package/src/utils/window-utils.js +336 -0
- package/update-desktop.sh +33 -0
|
@@ -0,0 +1,531 @@
|
|
|
1
|
+
const { z } = require("zod");
|
|
2
|
+
const { execSync } = require("child_process");
|
|
3
|
+
|
|
4
|
+
function registerTools(registerTool) {
|
|
5
|
+
registerTool(
|
|
6
|
+
"get_system_windows",
|
|
7
|
+
"获取系统所有窗口信息(进程名、PID、窗口位置)",
|
|
8
|
+
z.object({
|
|
9
|
+
detail: z.boolean().optional().default(false).describe("是否显示详细信息"),
|
|
10
|
+
}),
|
|
11
|
+
async ({ detail }) => {
|
|
12
|
+
try {
|
|
13
|
+
const fs = require("fs");
|
|
14
|
+
const path = require("path");
|
|
15
|
+
let windows = [];
|
|
16
|
+
|
|
17
|
+
if (process.platform === "linux") {
|
|
18
|
+
// 使用 wmctrl 获取窗口信息
|
|
19
|
+
const output = execSync("wmctrl -lGp", { encoding: "utf8" });
|
|
20
|
+
const lines = output.trim().split("\n");
|
|
21
|
+
|
|
22
|
+
// 获取当前活动窗口
|
|
23
|
+
let activeWinId = "";
|
|
24
|
+
try {
|
|
25
|
+
activeWinId = execSync("xdotool getactivewindow", { encoding: "utf8" }).trim();
|
|
26
|
+
activeWinId = "0x" + parseInt(activeWinId).toString(16).padStart(8, "0");
|
|
27
|
+
} catch (e) {
|
|
28
|
+
// xdotool not available
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
windows = lines.map((line) => {
|
|
32
|
+
const parts = line.split(/\s+/);
|
|
33
|
+
const winId = parts[0];
|
|
34
|
+
const desktop = parts[1];
|
|
35
|
+
const pid = parts[2];
|
|
36
|
+
const x = parseInt(parts[3]);
|
|
37
|
+
const y = parseInt(parts[4]);
|
|
38
|
+
const width = parseInt(parts[5]);
|
|
39
|
+
const height = parseInt(parts[6]);
|
|
40
|
+
const title = parts.slice(8).join(" ");
|
|
41
|
+
|
|
42
|
+
// 获取进程名
|
|
43
|
+
let processName = "";
|
|
44
|
+
try {
|
|
45
|
+
processName = execSync(`ps -p ${pid} -o comm=`, { encoding: "utf8" }).trim();
|
|
46
|
+
} catch (e) {
|
|
47
|
+
processName = "unknown";
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const isFocused = winId === activeWinId;
|
|
51
|
+
const isVisible = desktop !== "-1" && width > 0 && height > 0;
|
|
52
|
+
|
|
53
|
+
if (detail) {
|
|
54
|
+
// 生成缩略图 URL
|
|
55
|
+
const baseUrl =
|
|
56
|
+
process.env.ELECTRON_MCP_BASE_URL || `http://localhost:${process.env.PORT || 8101}`;
|
|
57
|
+
const thumbUrl = `${baseUrl}/files/screenshot/sys_win_${winId.replace(/^0x/, "")}.jpeg`;
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
windowId: winId,
|
|
61
|
+
pid: parseInt(pid),
|
|
62
|
+
processName,
|
|
63
|
+
title,
|
|
64
|
+
bounds: { x, y, width, height },
|
|
65
|
+
desktop: parseInt(desktop),
|
|
66
|
+
isVisible,
|
|
67
|
+
isFocused,
|
|
68
|
+
thumbUrl,
|
|
69
|
+
};
|
|
70
|
+
} else {
|
|
71
|
+
return {
|
|
72
|
+
windowId: winId,
|
|
73
|
+
title,
|
|
74
|
+
processName,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
} else if (process.platform === "darwin") {
|
|
79
|
+
// macOS 使用 osascript
|
|
80
|
+
const script = `
|
|
81
|
+
tell application "System Events"
|
|
82
|
+
set windowList to {}
|
|
83
|
+
repeat with proc in (every process whose background only is false)
|
|
84
|
+
set procName to name of proc
|
|
85
|
+
set procPID to unix id of proc
|
|
86
|
+
repeat with win in (every window of proc)
|
|
87
|
+
set winName to name of win
|
|
88
|
+
set winPos to position of win
|
|
89
|
+
set winSize to size of win
|
|
90
|
+
set end of windowList to {procName, procPID, winName, item 1 of winPos, item 2 of winPos, item 1 of winSize, item 2 of winSize}
|
|
91
|
+
end repeat
|
|
92
|
+
end repeat
|
|
93
|
+
return windowList
|
|
94
|
+
end tell
|
|
95
|
+
`;
|
|
96
|
+
const output = execSync(`osascript -e '${script}'`, { encoding: "utf8" });
|
|
97
|
+
// Parse output and format
|
|
98
|
+
windows = [{ raw: output }];
|
|
99
|
+
} else {
|
|
100
|
+
throw new Error("Unsupported platform");
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// 清理不在列表中的截图文件
|
|
104
|
+
const screenshotDir = path.join(
|
|
105
|
+
require("os").homedir(),
|
|
106
|
+
"electron-mcp-files",
|
|
107
|
+
"screenshot"
|
|
108
|
+
);
|
|
109
|
+
if (fs.existsSync(screenshotDir)) {
|
|
110
|
+
const validIds = new Set(windows.map((w) => w.windowId.replace(/^0x/, "")));
|
|
111
|
+
const files = fs.readdirSync(screenshotDir);
|
|
112
|
+
|
|
113
|
+
for (const file of files) {
|
|
114
|
+
if (file.startsWith("sys_win_") && file.endsWith(".jpeg")) {
|
|
115
|
+
const winId = file.replace("sys_win_", "").replace(".jpeg", "");
|
|
116
|
+
if (!validIds.has(winId)) {
|
|
117
|
+
fs.unlinkSync(path.join(screenshotDir, file));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
content: [
|
|
125
|
+
{
|
|
126
|
+
type: "text",
|
|
127
|
+
text: JSON.stringify(
|
|
128
|
+
{
|
|
129
|
+
platform: process.platform,
|
|
130
|
+
total: windows.length,
|
|
131
|
+
windows,
|
|
132
|
+
},
|
|
133
|
+
null,
|
|
134
|
+
2
|
|
135
|
+
),
|
|
136
|
+
},
|
|
137
|
+
],
|
|
138
|
+
};
|
|
139
|
+
} catch (error) {
|
|
140
|
+
return {
|
|
141
|
+
content: [{ type: "text", text: `Error: ${error.message}` }],
|
|
142
|
+
isError: true,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
{ tag: "System" }
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
registerTool(
|
|
150
|
+
"focus_system_window",
|
|
151
|
+
"聚焦指定的系统窗口",
|
|
152
|
+
z.object({
|
|
153
|
+
windowId: z.string().describe("窗口ID(从 get_system_windows 获取)"),
|
|
154
|
+
}),
|
|
155
|
+
async ({ windowId }) => {
|
|
156
|
+
try {
|
|
157
|
+
if (process.platform === "linux") {
|
|
158
|
+
execSync(`wmctrl -ia ${windowId}`);
|
|
159
|
+
return {
|
|
160
|
+
content: [
|
|
161
|
+
{
|
|
162
|
+
type: "text",
|
|
163
|
+
text: JSON.stringify(
|
|
164
|
+
{
|
|
165
|
+
success: true,
|
|
166
|
+
windowId,
|
|
167
|
+
message: "Window focused",
|
|
168
|
+
},
|
|
169
|
+
null,
|
|
170
|
+
2
|
|
171
|
+
),
|
|
172
|
+
},
|
|
173
|
+
],
|
|
174
|
+
};
|
|
175
|
+
} else {
|
|
176
|
+
throw new Error("Unsupported platform");
|
|
177
|
+
}
|
|
178
|
+
} catch (error) {
|
|
179
|
+
return {
|
|
180
|
+
content: [{ type: "text", text: `Error: ${error.message}` }],
|
|
181
|
+
isError: true,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
{ tag: "System" }
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
registerTool(
|
|
189
|
+
"get_system_info",
|
|
190
|
+
"获取系统信息(CPU、内存、磁盘、负载、IP)",
|
|
191
|
+
z.object({}),
|
|
192
|
+
async () => {
|
|
193
|
+
try {
|
|
194
|
+
const os = require("os");
|
|
195
|
+
|
|
196
|
+
// CPU
|
|
197
|
+
const cpus = os.cpus();
|
|
198
|
+
const cpuModel = cpus[0].model;
|
|
199
|
+
const cpuCount = cpus.length;
|
|
200
|
+
|
|
201
|
+
// CPU 使用率
|
|
202
|
+
let cpuUsage = 0;
|
|
203
|
+
if (process.platform === "linux") {
|
|
204
|
+
const top = execSync("top -bn1 | grep 'Cpu(s)'", { encoding: "utf8" });
|
|
205
|
+
const match = top.match(/(\d+\.\d+)\s+id/);
|
|
206
|
+
if (match) {
|
|
207
|
+
cpuUsage = (100 - parseFloat(match[1])).toFixed(1) + "%";
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// 内存
|
|
212
|
+
const totalMem = (os.totalmem() / 1024 / 1024 / 1024).toFixed(1);
|
|
213
|
+
const freeMem = (os.freemem() / 1024 / 1024 / 1024).toFixed(1);
|
|
214
|
+
const usedMem = (totalMem - freeMem).toFixed(1);
|
|
215
|
+
|
|
216
|
+
// 负载
|
|
217
|
+
const loadavg = os.loadavg().map((l) => l.toFixed(2));
|
|
218
|
+
|
|
219
|
+
// 磁盘(Linux)
|
|
220
|
+
let disk = {};
|
|
221
|
+
if (process.platform === "linux") {
|
|
222
|
+
const df = execSync("df -h /", { encoding: "utf8" });
|
|
223
|
+
const lines = df.trim().split("\n");
|
|
224
|
+
const parts = lines[1].split(/\s+/);
|
|
225
|
+
disk = {
|
|
226
|
+
total: parts[1],
|
|
227
|
+
used: parts[2],
|
|
228
|
+
available: parts[3],
|
|
229
|
+
usePercent: parts[4],
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// 本地 IP
|
|
234
|
+
const networkInterfaces = os.networkInterfaces();
|
|
235
|
+
const localIPs = [];
|
|
236
|
+
for (const name in networkInterfaces) {
|
|
237
|
+
for (const net of networkInterfaces[name]) {
|
|
238
|
+
if (net.family === "IPv4" && !net.internal) {
|
|
239
|
+
localIPs.push(net.address);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// 公网 IP
|
|
245
|
+
let publicIP = "";
|
|
246
|
+
try {
|
|
247
|
+
publicIP = execSync("curl -s https://api.myip.com", { encoding: "utf8", timeout: 5000 });
|
|
248
|
+
publicIP = JSON.parse(publicIP);
|
|
249
|
+
} catch (e) {
|
|
250
|
+
publicIP = "Failed to fetch";
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return {
|
|
254
|
+
content: [
|
|
255
|
+
{
|
|
256
|
+
type: "text",
|
|
257
|
+
text: JSON.stringify(
|
|
258
|
+
{
|
|
259
|
+
platform: os.platform(),
|
|
260
|
+
arch: os.arch(),
|
|
261
|
+
hostname: os.hostname(),
|
|
262
|
+
uptime: Math.floor(os.uptime() / 60) + " minutes",
|
|
263
|
+
cpu: {
|
|
264
|
+
model: cpuModel,
|
|
265
|
+
cores: cpuCount,
|
|
266
|
+
usage: cpuUsage,
|
|
267
|
+
},
|
|
268
|
+
memory: {
|
|
269
|
+
total: totalMem + "G",
|
|
270
|
+
used: usedMem + "G",
|
|
271
|
+
free: freeMem + "G",
|
|
272
|
+
},
|
|
273
|
+
loadavg: {
|
|
274
|
+
"1min": loadavg[0],
|
|
275
|
+
"5min": loadavg[1],
|
|
276
|
+
"15min": loadavg[2],
|
|
277
|
+
},
|
|
278
|
+
disk,
|
|
279
|
+
network: {
|
|
280
|
+
localIP: localIPs,
|
|
281
|
+
publicIP,
|
|
282
|
+
},
|
|
283
|
+
},
|
|
284
|
+
null,
|
|
285
|
+
2
|
|
286
|
+
),
|
|
287
|
+
},
|
|
288
|
+
],
|
|
289
|
+
};
|
|
290
|
+
} catch (error) {
|
|
291
|
+
return {
|
|
292
|
+
content: [{ type: "text", text: `Error: ${error.message}` }],
|
|
293
|
+
isError: true,
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
},
|
|
297
|
+
{ tag: "System" }
|
|
298
|
+
);
|
|
299
|
+
|
|
300
|
+
registerTool(
|
|
301
|
+
"system_screenshot",
|
|
302
|
+
"截取系统屏幕并保存到文件",
|
|
303
|
+
z.object({
|
|
304
|
+
quality: z.number().optional().default(80).describe("JPEG 质量 (1-100)"),
|
|
305
|
+
copyToClipboard: z.boolean().optional().default(false).describe("是否复制到剪贴板"),
|
|
306
|
+
}),
|
|
307
|
+
async ({ quality, copyToClipboard }) => {
|
|
308
|
+
try {
|
|
309
|
+
const fs = require("fs");
|
|
310
|
+
const path = require("path");
|
|
311
|
+
const crypto = require("crypto");
|
|
312
|
+
|
|
313
|
+
// 创建目录
|
|
314
|
+
const screenshotDir = path.join(
|
|
315
|
+
require("os").homedir(),
|
|
316
|
+
"electron-mcp-files",
|
|
317
|
+
"screenshot"
|
|
318
|
+
);
|
|
319
|
+
if (!fs.existsSync(screenshotDir)) {
|
|
320
|
+
fs.mkdirSync(screenshotDir, { recursive: true });
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// 临时文件
|
|
324
|
+
const tmpFile = `/tmp/screenshot-${Date.now()}.png`;
|
|
325
|
+
|
|
326
|
+
// 截图
|
|
327
|
+
if (process.platform === "linux") {
|
|
328
|
+
execSync(`import -window root ${tmpFile}`);
|
|
329
|
+
} else {
|
|
330
|
+
throw new Error("Unsupported platform");
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// 转换为 JPEG
|
|
334
|
+
const jpegFile = path.join(screenshotDir, "system.jpeg");
|
|
335
|
+
execSync(`convert ${tmpFile} -quality ${quality} ${jpegFile}`);
|
|
336
|
+
|
|
337
|
+
// 获取文件大小和尺寸
|
|
338
|
+
const stats = fs.statSync(jpegFile);
|
|
339
|
+
const sizeKB = (stats.size / 1024).toFixed(2);
|
|
340
|
+
const identify = execSync(`identify -format "%wx%h" ${jpegFile}`, { encoding: "utf8" });
|
|
341
|
+
const [width, height] = identify.split("x").map(Number);
|
|
342
|
+
|
|
343
|
+
// 复制到剪贴板
|
|
344
|
+
if (copyToClipboard) {
|
|
345
|
+
execSync(`xclip -selection clipboard -t image/jpeg -i ${jpegFile}`);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// 清理临时文件
|
|
349
|
+
fs.unlinkSync(tmpFile);
|
|
350
|
+
|
|
351
|
+
// 返回 URL
|
|
352
|
+
const baseUrl =
|
|
353
|
+
process.env.ELECTRON_MCP_BASE_URL || `http://localhost:${process.env.PORT || 8101}`;
|
|
354
|
+
const url = `${baseUrl}/files/screenshot/system.jpeg`;
|
|
355
|
+
|
|
356
|
+
return {
|
|
357
|
+
content: [
|
|
358
|
+
{
|
|
359
|
+
type: "text",
|
|
360
|
+
text: JSON.stringify(
|
|
361
|
+
{
|
|
362
|
+
success: true,
|
|
363
|
+
path: jpegFile,
|
|
364
|
+
url,
|
|
365
|
+
size: sizeKB + "KB",
|
|
366
|
+
width,
|
|
367
|
+
height,
|
|
368
|
+
quality,
|
|
369
|
+
copiedToClipboard: copyToClipboard,
|
|
370
|
+
},
|
|
371
|
+
null,
|
|
372
|
+
2
|
|
373
|
+
),
|
|
374
|
+
},
|
|
375
|
+
],
|
|
376
|
+
};
|
|
377
|
+
} catch (error) {
|
|
378
|
+
return {
|
|
379
|
+
content: [{ type: "text", text: `Error: ${error.message}` }],
|
|
380
|
+
isError: true,
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
},
|
|
384
|
+
{ tag: "System" }
|
|
385
|
+
);
|
|
386
|
+
|
|
387
|
+
registerTool(
|
|
388
|
+
"sys_win_screenshot",
|
|
389
|
+
"截取指定系统窗口并保存到文件",
|
|
390
|
+
z.object({
|
|
391
|
+
windowId: z.string().describe("窗口ID(从 get_system_windows 获取)"),
|
|
392
|
+
quality: z.number().optional().default(80).describe("JPEG 质量 (1-100)"),
|
|
393
|
+
copyToClipboard: z.boolean().optional().default(false).describe("是否复制到剪贴板"),
|
|
394
|
+
}),
|
|
395
|
+
async ({ windowId, quality, copyToClipboard }) => {
|
|
396
|
+
try {
|
|
397
|
+
const fs = require("fs");
|
|
398
|
+
const path = require("path");
|
|
399
|
+
|
|
400
|
+
// 创建目录
|
|
401
|
+
const screenshotDir = path.join(
|
|
402
|
+
require("os").homedir(),
|
|
403
|
+
"electron-mcp-files",
|
|
404
|
+
"screenshot"
|
|
405
|
+
);
|
|
406
|
+
if (!fs.existsSync(screenshotDir)) {
|
|
407
|
+
fs.mkdirSync(screenshotDir, { recursive: true });
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// 临时文件
|
|
411
|
+
const tmpFile = `/tmp/screenshot-${Date.now()}.png`;
|
|
412
|
+
|
|
413
|
+
// 截图
|
|
414
|
+
if (process.platform === "linux") {
|
|
415
|
+
execSync(`import -window ${windowId} ${tmpFile}`);
|
|
416
|
+
} else {
|
|
417
|
+
throw new Error("Unsupported platform");
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// 转换为 JPEG
|
|
421
|
+
const filename = `sys_win_${windowId.replace(/^0x/, "")}.jpeg`;
|
|
422
|
+
const jpegFile = path.join(screenshotDir, filename);
|
|
423
|
+
execSync(`convert ${tmpFile} -quality ${quality} ${jpegFile}`);
|
|
424
|
+
|
|
425
|
+
// 获取文件大小和尺寸
|
|
426
|
+
const stats = fs.statSync(jpegFile);
|
|
427
|
+
const sizeKB = (stats.size / 1024).toFixed(2);
|
|
428
|
+
const identify = execSync(`identify -format "%wx%h" ${jpegFile}`, { encoding: "utf8" });
|
|
429
|
+
const [width, height] = identify.split("x").map(Number);
|
|
430
|
+
|
|
431
|
+
// 复制到剪贴板
|
|
432
|
+
if (copyToClipboard) {
|
|
433
|
+
execSync(`xclip -selection clipboard -t image/jpeg -i ${jpegFile}`);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// 清理临时文件
|
|
437
|
+
fs.unlinkSync(tmpFile);
|
|
438
|
+
|
|
439
|
+
// 返回 URL
|
|
440
|
+
const baseUrl =
|
|
441
|
+
process.env.ELECTRON_MCP_BASE_URL || `http://localhost:${process.env.PORT || 8101}`;
|
|
442
|
+
const url = `${baseUrl}/files/screenshot/${filename}`;
|
|
443
|
+
|
|
444
|
+
return {
|
|
445
|
+
content: [
|
|
446
|
+
{
|
|
447
|
+
type: "text",
|
|
448
|
+
text: JSON.stringify(
|
|
449
|
+
{
|
|
450
|
+
success: true,
|
|
451
|
+
windowId,
|
|
452
|
+
path: jpegFile,
|
|
453
|
+
url,
|
|
454
|
+
size: sizeKB + "KB",
|
|
455
|
+
width,
|
|
456
|
+
height,
|
|
457
|
+
quality,
|
|
458
|
+
copiedToClipboard: copyToClipboard,
|
|
459
|
+
},
|
|
460
|
+
null,
|
|
461
|
+
2
|
|
462
|
+
),
|
|
463
|
+
},
|
|
464
|
+
],
|
|
465
|
+
};
|
|
466
|
+
} catch (error) {
|
|
467
|
+
return {
|
|
468
|
+
content: [{ type: "text", text: `Error: ${error.message}` }],
|
|
469
|
+
isError: true,
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
},
|
|
473
|
+
{ tag: "System" }
|
|
474
|
+
);
|
|
475
|
+
|
|
476
|
+
registerTool(
|
|
477
|
+
"system_window_setbound",
|
|
478
|
+
"设置系统窗口的位置和大小",
|
|
479
|
+
z.object({
|
|
480
|
+
windowId: z.string().describe("窗口ID(从 get_system_windows 获取)"),
|
|
481
|
+
x: z.number().optional().describe("X 坐标"),
|
|
482
|
+
y: z.number().optional().describe("Y 坐标"),
|
|
483
|
+
width: z.number().optional().describe("宽度"),
|
|
484
|
+
height: z.number().optional().describe("高度"),
|
|
485
|
+
}),
|
|
486
|
+
async ({ windowId, x, y, width, height }) => {
|
|
487
|
+
try {
|
|
488
|
+
if (process.platform === "linux") {
|
|
489
|
+
// 构建 wmctrl 命令
|
|
490
|
+
const gravity = 0; // 左上角
|
|
491
|
+
const flags = [];
|
|
492
|
+
|
|
493
|
+
if (x !== undefined || y !== undefined || width !== undefined || height !== undefined) {
|
|
494
|
+
const posX = x !== undefined ? x : -1;
|
|
495
|
+
const posY = y !== undefined ? y : -1;
|
|
496
|
+
const w = width !== undefined ? width : -1;
|
|
497
|
+
const h = height !== undefined ? height : -1;
|
|
498
|
+
execSync(`wmctrl -ir ${windowId} -e ${gravity},${posX},${posY},${w},${h}`);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
return {
|
|
502
|
+
content: [
|
|
503
|
+
{
|
|
504
|
+
type: "text",
|
|
505
|
+
text: JSON.stringify(
|
|
506
|
+
{
|
|
507
|
+
success: true,
|
|
508
|
+
windowId,
|
|
509
|
+
bounds: { x, y, width, height },
|
|
510
|
+
},
|
|
511
|
+
null,
|
|
512
|
+
2
|
|
513
|
+
),
|
|
514
|
+
},
|
|
515
|
+
],
|
|
516
|
+
};
|
|
517
|
+
} else {
|
|
518
|
+
throw new Error("Unsupported platform");
|
|
519
|
+
}
|
|
520
|
+
} catch (error) {
|
|
521
|
+
return {
|
|
522
|
+
content: [{ type: "text", text: `Error: ${error.message}` }],
|
|
523
|
+
isError: true,
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
},
|
|
527
|
+
{ tag: "System" }
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
module.exports = registerTools;
|