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.
Files changed (66) hide show
  1. package/.github/workflows/build.yml +85 -0
  2. package/.kiro/steering/dev-workflow.md +166 -0
  3. package/AGENTS.md +247 -0
  4. package/CLAUDE.md +162 -0
  5. package/DOCKER.md +85 -0
  6. package/Dockerfile +46 -0
  7. package/README.md +720 -0
  8. package/TODO-anti-detection.md +326 -0
  9. package/bin/cicy +176 -0
  10. package/bin/preinstall.sh +32 -0
  11. package/copy-to-desktop.sh +26 -0
  12. package/docs/AUTOMATION-API.md +342 -0
  13. package/docs/REQUEST_MONITORING.md +435 -0
  14. package/docs/REST-API-FEATURE.md +155 -0
  15. package/docs/REST-API.md +319 -0
  16. package/docs/feature-distributed-multi-agent.md +555 -0
  17. package/docs/yaml.md +255 -0
  18. package/electron-mcp-fixed.command +134 -0
  19. package/electron-mcp-simple.command +135 -0
  20. package/electron-mcp.command +92 -0
  21. package/generate-openapi.js +158 -0
  22. package/jest.config.js +10 -0
  23. package/jest.setup.global.js +13 -0
  24. package/jest.teardown.global.js +7 -0
  25. package/package.json +75 -0
  26. package/service.sh +164 -0
  27. package/src/config.js +8 -0
  28. package/src/extension/inject.js +135 -0
  29. package/src/main-old.js +837 -0
  30. package/src/main.js +403 -0
  31. package/src/preload-rpc.js +4 -0
  32. package/src/server/args-parser.js +37 -0
  33. package/src/server/electron-setup.js +33 -0
  34. package/src/server/express-app.js +166 -0
  35. package/src/server/logging.js +58 -0
  36. package/src/server/mcp-server.js +53 -0
  37. package/src/server/tool-registry.js +77 -0
  38. package/src/server/ui-routes.js +81 -0
  39. package/src/swagger-ui.html +41 -0
  40. package/src/tools/account-tools.js +194 -0
  41. package/src/tools/automation-tools.js +297 -0
  42. package/src/tools/cdp-tools.js +444 -0
  43. package/src/tools/clipboard-tools.js +180 -0
  44. package/src/tools/download-tools.js +57 -0
  45. package/src/tools/exec-js.js +297 -0
  46. package/src/tools/exec-tools.js +139 -0
  47. package/src/tools/file-tools.js +212 -0
  48. package/src/tools/hook-chatgpt.js +489 -0
  49. package/src/tools/hook-gemini.js +454 -0
  50. package/src/tools/index.js +19 -0
  51. package/src/tools/ipc-bridge.js +31 -0
  52. package/src/tools/ping.js +60 -0
  53. package/src/tools/r-reset.js +28 -0
  54. package/src/tools/screenshot-tools.js +28 -0
  55. package/src/tools/system-tools.js +531 -0
  56. package/src/tools/window-tools.js +882 -0
  57. package/src/ui.html +914 -0
  58. package/src/utils/auth.js +81 -0
  59. package/src/utils/cdp-utils.js +8 -0
  60. package/src/utils/download-manager.js +41 -0
  61. package/src/utils/process-utils.js +185 -0
  62. package/src/utils/snapshot-utils.js +56 -0
  63. package/src/utils/window-monitor.js +605 -0
  64. package/src/utils/window-state.js +137 -0
  65. package/src/utils/window-utils.js +336 -0
  66. package/update-desktop.sh +33 -0
@@ -0,0 +1,489 @@
1
+ const { BrowserWindow } = require("electron");
2
+ const { z } = require("zod");
3
+ const { sendCDP } = require("../utils/cdp-utils");
4
+
5
+ function registerTools(registerTool) {
6
+ registerTool(
7
+ "get_chatgpt_web_conversations",
8
+ "List chatgpt conversations from indexedDB, returns id, title, updateTime, total",
9
+ z.object({
10
+ win_id: z.number().optional().default(1).describe("窗口 ID"),
11
+ limit: z.number().optional().default(10).describe("return limit"),
12
+ strip: z.number().optional().default(0).describe("return strip"),
13
+ }),
14
+ async ({ win_id, limit, strip }) => {
15
+ try {
16
+ const win = BrowserWindow.fromId(win_id);
17
+ if (!win) throw new Error(`未找到窗口 ${win_id}`);
18
+
19
+ const result = await win.webContents.executeJavaScript(`(async () => {
20
+ const href = location.href;
21
+ const urlMatch = href.match(/\\/c\\/([a-f0-9-]+)/);
22
+ const currentId = urlMatch ? urlMatch[1] : null;
23
+ const rows = await window._g.getIndexedDBRows("ConversationsDatabase", "conversations");
24
+ rows.sort((a, b) => b.updateTime - a.updateTime);
25
+ const sliced = rows.slice(${strip}, ${strip} + ${limit});
26
+ return JSON.stringify({ total: rows.length, conversations: sliced.map(c => ({
27
+ id: c.id,
28
+ title: c.title,
29
+ updateTime: c.updateTime,
30
+ updateDate: new Date(c.updateTime * 1000).toISOString(),
31
+ isCurrent: c.id === currentId
32
+ })) });
33
+ })()`);
34
+
35
+ const data = JSON.parse(result);
36
+ return {
37
+ content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
38
+ };
39
+ } catch (error) {
40
+ return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true };
41
+ }
42
+ },
43
+ { tag: "ChatGPT" }
44
+ );
45
+
46
+ registerTool(
47
+ "get_chatgpt_web_current_messages",
48
+ "Get messages from the most recent chatgpt conversation in indexedDB",
49
+ z.object({
50
+ win_id: z.number().optional().default(1).describe("窗口 ID"),
51
+ }),
52
+ async ({ win_id }) => {
53
+ try {
54
+ const win = BrowserWindow.fromId(win_id);
55
+ if (!win) throw new Error(`未找到窗口 ${win_id}`);
56
+
57
+ const result = await win.webContents.executeJavaScript(`(async () => {
58
+ const href = location.href;
59
+ const rows = await window._g.getIndexedDBRows("ConversationsDatabase", "conversations");
60
+ const urlMatch = href.match(/\\/c\\/([a-f0-9-]+)/);
61
+ let c;
62
+ if (urlMatch) {
63
+ c = rows.find(r => r.id === urlMatch[1]);
64
+ }
65
+ if (!c) {
66
+ rows.sort((a, b) => b.updateTime - a.updateTime);
67
+ c = rows[0];
68
+ }
69
+ if (!c) return JSON.stringify({ error: "No conversations found", href, isInConversationPage: !!urlMatch });
70
+ return JSON.stringify({
71
+ href,
72
+ isInConversationPage: !!urlMatch,
73
+ id: c.id,
74
+ title: c.title,
75
+ updateTime: c.updateTime,
76
+ updateDate: new Date(c.updateTime * 1000).toISOString(),
77
+ messages: (c.messages || []).slice().reverse()
78
+ });
79
+ })()`);
80
+
81
+ const data = JSON.parse(result);
82
+ return {
83
+ content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
84
+ };
85
+ } catch (error) {
86
+ return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true };
87
+ }
88
+ },
89
+ { tag: "ChatGPT" }
90
+ );
91
+ registerTool(
92
+ "get_chatgpt_web_messages_by_id",
93
+ "Get messages from a specific chatgpt conversation by id",
94
+ z.object({
95
+ win_id: z.number().optional().default(1).describe("窗口 ID"),
96
+ conversation_id: z.string().describe("Conversation ID"),
97
+ }),
98
+ async ({ win_id, conversation_id }) => {
99
+ try {
100
+ const win = BrowserWindow.fromId(win_id);
101
+ if (!win) throw new Error(`未找到窗口 ${win_id}`);
102
+
103
+ const result = await win.webContents.executeJavaScript(`(async () => {
104
+ if (!window._g) {
105
+ window._g = {};
106
+ window._g.getIndexedDBRows = function(dbName, storeName, limit) {
107
+ limit = limit || 100;
108
+ return new Promise(function(resolve, reject) {
109
+ var req = indexedDB.open(dbName);
110
+ req.onsuccess = function() {
111
+ var db = req.result;
112
+ var tx = db.transaction(storeName, 'readonly');
113
+ var store = tx.objectStore(storeName);
114
+ var results = [];
115
+ var cursorReq = store.openCursor();
116
+ cursorReq.onsuccess = function(e) {
117
+ var cursor = e.target.result;
118
+ if (cursor && results.length < limit) {
119
+ results.push(cursor.value);
120
+ cursor.continue();
121
+ } else { resolve(results); }
122
+ };
123
+ };
124
+ req.onerror = function() { reject(req.error); };
125
+ });
126
+ };
127
+ }
128
+ const rows = await window._g.getIndexedDBRows("ConversationsDatabase", "conversations");
129
+ const c = rows.find(r => r.id === "${conversation_id}");
130
+ if (!c) return JSON.stringify({ error: "Conversation not found" });
131
+ return JSON.stringify({
132
+ id: c.id,
133
+ title: c.title,
134
+ updateTime: c.updateTime,
135
+ updateDate: new Date(c.updateTime * 1000).toISOString(),
136
+ messages: (c.messages || []).slice().reverse()
137
+ });
138
+ })()`);
139
+
140
+ const data = JSON.parse(result);
141
+ return {
142
+ content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
143
+ };
144
+ } catch (error) {
145
+ return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true };
146
+ }
147
+ },
148
+ { tag: "ChatGPT" }
149
+ );
150
+
151
+ registerTool(
152
+ "open_chatgpt_web_chat_by_id",
153
+ "Navigate to a specific chatgpt conversation by id",
154
+ z.object({
155
+ win_id: z.number().optional().default(1).describe("窗口 ID"),
156
+ conversation_id: z.string().describe("Conversation ID"),
157
+ }),
158
+ async ({ win_id, conversation_id }) => {
159
+ try {
160
+ const win = BrowserWindow.fromId(win_id);
161
+ if (!win) throw new Error(`未找到窗口 ${win_id}`);
162
+
163
+ const url = `https://chatgpt.com/c/${conversation_id}`;
164
+ await win.webContents.executeJavaScript(`location.href = "${url}"`);
165
+ return {
166
+ content: [{ type: "text", text: `Navigated to ${url}` }],
167
+ };
168
+ } catch (error) {
169
+ return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true };
170
+ }
171
+ },
172
+ { tag: "ChatGPT" }
173
+ );
174
+
175
+ registerTool(
176
+ "is_chatgpt_logged",
177
+ "Check if chatgpt is logged in by inspecting cookies and page state",
178
+ z.object({
179
+ win_id: z.number().optional().default(1).describe("窗口 ID"),
180
+ }),
181
+ async ({ win_id }) => {
182
+ try {
183
+ const win = BrowserWindow.fromId(win_id);
184
+ if (!win) throw new Error(`未找到窗口 ${win_id}`);
185
+
186
+ const result = await win.webContents.executeJavaScript(`(async () => {
187
+ const href = location.href;
188
+ const hasLoginBtn = !!document.querySelector('[data-testid="login-button"]');
189
+ const hasPromptTextarea = !!document.querySelector('textarea[name="prompt-textarea"]');
190
+ const isLogged = !hasLoginBtn && hasPromptTextarea;
191
+ return JSON.stringify({ href, isLogged });
192
+ })()`);
193
+
194
+ const data = JSON.parse(result);
195
+ return {
196
+ content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
197
+ };
198
+ } catch (error) {
199
+ return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true };
200
+ }
201
+ },
202
+ { tag: "ChatGPT" }
203
+ );
204
+
205
+ registerTool(
206
+ "chatgpt_web_status",
207
+ "Get chatgpt web page status: login state, current URL, conversation info, prompt textarea state",
208
+ z.object({
209
+ win_id: z.number().optional().default(1).describe("窗口 ID"),
210
+ }),
211
+ async ({ win_id }) => {
212
+ try {
213
+ const win = BrowserWindow.fromId(win_id);
214
+ if (!win) throw new Error(`未找到窗口 ${win_id}`);
215
+
216
+ const result = await win.webContents.executeJavaScript(`(async () => {
217
+ const href = location.href;
218
+ const hasLoginBtn = !!document.querySelector('[data-testid="login-button"]');
219
+ const hasPromptTextarea = !!document.querySelector('textarea[name="prompt-textarea"]');
220
+ const isLogged = !hasLoginBtn && hasPromptTextarea;
221
+ const urlMatch = href.match(/\\/c\\/([a-f0-9-]+)/);
222
+ const isInConversationPage = !!urlMatch;
223
+ const conversationId = urlMatch ? urlMatch[1] : null;
224
+ const title = document.title;
225
+ const promptValue = hasPromptTextarea ? (document.querySelector('#prompt-textarea')?.innerText || document.querySelector('textarea[name="prompt-textarea"]').value) : null;
226
+ const sendBtn = document.querySelector('button[data-testid="send-button"]');
227
+ const voiceBtn = document.querySelector('button[aria-label="Start Voice"]');
228
+ const hasSendPromptBtn = !!sendBtn;
229
+ const isOnVoiceMode = !!voiceBtn && !hasSendPromptBtn;
230
+ const promptSendBtnCanBeSend = hasSendPromptBtn && !sendBtn.disabled;
231
+ return JSON.stringify({
232
+ href,
233
+ title,
234
+ isLogged,
235
+ hasLoginBtn,
236
+ hasPromptTextarea,
237
+ isInConversationPage,
238
+ conversationId,
239
+ promptValue,
240
+ hasSendPromptBtn,
241
+ isOnVoiceMode,
242
+ promptSendBtnCanBeSend
243
+ });
244
+ })()`);
245
+
246
+ const data = JSON.parse(result);
247
+ return {
248
+ content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
249
+ };
250
+ } catch (error) {
251
+ return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true };
252
+ }
253
+ },
254
+ { tag: "ChatGPT" }
255
+ );
256
+
257
+ registerTool(
258
+ "chatgpt_web_clear_prompt",
259
+ "Clear the chatgpt prompt input",
260
+ z.object({
261
+ win_id: z.number().optional().default(1).describe("窗口 ID"),
262
+ }),
263
+ async ({ win_id }) => {
264
+ try {
265
+ const win = BrowserWindow.fromId(win_id);
266
+ if (!win) throw new Error(`未找到窗口 ${win_id}`);
267
+
268
+ await win.webContents.executeJavaScript(`(async () => {
269
+ const div = document.querySelector('#prompt-textarea');
270
+ if (div) { div.innerHTML = '<p><br></p>'; div.dispatchEvent(new Event('input', { bubbles: true })); }
271
+ const textarea = document.querySelector('textarea[name="prompt-textarea"]');
272
+ if (textarea) { textarea.value = ''; textarea.dispatchEvent(new Event('input', { bubbles: true })); }
273
+ })()`);
274
+
275
+ return {
276
+ content: [{ type: "text", text: "Prompt cleared" }],
277
+ };
278
+ } catch (error) {
279
+ return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true };
280
+ }
281
+ },
282
+ { tag: "ChatGPT" }
283
+ );
284
+
285
+ registerTool(
286
+ "chatgpt_web_set_prompt",
287
+ "Set the chatgpt prompt input value",
288
+ z.object({
289
+ win_id: z.number().optional().default(1).describe("窗口 ID"),
290
+ text: z.string().describe("Text to set in prompt"),
291
+ }),
292
+ async ({ win_id, text }) => {
293
+ try {
294
+ const win = BrowserWindow.fromId(win_id);
295
+ if (!win) throw new Error(`未找到窗口 ${win_id}`);
296
+
297
+ await win.webContents.executeJavaScript(
298
+ `document.querySelector('#prompt-textarea')?.focus()`
299
+ );
300
+ await win.webContents.executeJavaScript(`document.execCommand('selectAll')`);
301
+ await sendCDP(win.webContents, "Input.insertText", { text });
302
+
303
+ return {
304
+ content: [{ type: "text", text: `Prompt set to: ${text}` }],
305
+ };
306
+ } catch (error) {
307
+ return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true };
308
+ }
309
+ },
310
+ { tag: "ChatGPT" }
311
+ );
312
+
313
+ registerTool(
314
+ "chatgpt_web_click_send",
315
+ "Click the chatgpt send prompt button",
316
+ z.object({
317
+ win_id: z.number().optional().default(1).describe("窗口 ID"),
318
+ }),
319
+ async ({ win_id }) => {
320
+ try {
321
+ const win = BrowserWindow.fromId(win_id);
322
+ if (!win) throw new Error(`未找到窗口 ${win_id}`);
323
+
324
+ const hasSendBtn = await win.webContents.executeJavaScript(
325
+ `!!document.querySelector('button[data-testid="send-button"]')`
326
+ );
327
+ if (!hasSendBtn) {
328
+ return {
329
+ content: [
330
+ {
331
+ type: "text",
332
+ text: JSON.stringify({ success: false, error: "Send button not found" }, null, 2),
333
+ },
334
+ ],
335
+ };
336
+ }
337
+
338
+ await sendCDP(win.webContents, "Input.dispatchKeyEvent", { type: "keyDown", key: "Enter" });
339
+ await sendCDP(win.webContents, "Input.dispatchKeyEvent", { type: "keyUp", key: "Enter" });
340
+
341
+ return {
342
+ content: [{ type: "text", text: JSON.stringify({ success: true }, null, 2) }],
343
+ };
344
+ } catch (error) {
345
+ return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true };
346
+ }
347
+ },
348
+ { tag: "ChatGPT" }
349
+ );
350
+
351
+ registerTool(
352
+ "chatgpt_web_ask",
353
+ "Ask chatgpt a question - checks login, sets prompt, sends, waits for response, returns reply",
354
+ z.object({
355
+ win_id: z.number().optional().default(1).describe("窗口 ID"),
356
+ text: z.string().describe("Question to ask"),
357
+ waitMs: z.number().optional().default(10000).describe("Wait time for response in ms"),
358
+ }),
359
+ async ({ win_id, text, waitMs }) => {
360
+ try {
361
+ const win = BrowserWindow.fromId(win_id);
362
+ if (!win) throw new Error(`未找到窗口 ${win_id}`);
363
+
364
+ const statusResult = await win.webContents.executeJavaScript(`(async () => {
365
+ const href = location.href;
366
+ const hasLoginBtn = !!document.querySelector('[data-testid="login-button"]');
367
+ const hasPromptTextarea = !!document.querySelector('textarea[name="prompt-textarea"]');
368
+ const sendBtn = document.querySelector('button[data-testid="send-button"]');
369
+ const hasSendPromptBtn = !!sendBtn;
370
+ const isOnVoiceMode = !hasSendPromptBtn;
371
+ const isLogged = !hasLoginBtn && hasPromptTextarea;
372
+ const isOnChatGPT = href.includes('chatgpt.com');
373
+ return JSON.stringify({ href, isLogged, hasPromptTextarea, hasSendPromptBtn, isOnVoiceMode, isOnChatGPT });
374
+ })()`);
375
+
376
+ const status = JSON.parse(statusResult);
377
+
378
+ if (!status.isLogged) {
379
+ return {
380
+ content: [
381
+ { type: "text", text: JSON.stringify({ error: "Not logged in", status }, null, 2) },
382
+ ],
383
+ };
384
+ }
385
+
386
+ const urlMatch = status.href.match(/\/c\/([a-f0-9-]+)/);
387
+ const isInConversation = !!urlMatch;
388
+
389
+ if (!isInConversation) {
390
+ await win.webContents.loadURL("https://chatgpt.com/");
391
+ await new Promise((r) => setTimeout(r, 3000));
392
+ }
393
+
394
+ if (!status.hasSendPromptBtn && !status.hasPromptTextarea) {
395
+ await win.webContents.loadURL("https://chatgpt.com/");
396
+ await new Promise((r) => setTimeout(r, 3000));
397
+ }
398
+
399
+ await win.webContents.executeJavaScript(`if (!window._g) window._g = {};`);
400
+
401
+ const injectHelperCode = `
402
+ (function() {
403
+ if (!window._g) window._g = {};
404
+ window._g.getIndexedDBRows = function(dbName, storeName, limit) {
405
+ limit = limit || 100;
406
+ return new Promise(function(resolve, reject) {
407
+ var req = indexedDB.open(dbName);
408
+ req.onsuccess = function() {
409
+ var db = req.result;
410
+ var tx = db.transaction(storeName, 'readonly');
411
+ var store = tx.objectStore(storeName);
412
+ var results = [];
413
+ var cursorReq = store.openCursor();
414
+ cursorReq.onsuccess = function(e) {
415
+ var cursor = e.target.result;
416
+ if (cursor && results.length < limit) {
417
+ results.push(cursor.value);
418
+ cursor.continue();
419
+ } else {
420
+ resolve(results);
421
+ }
422
+ };
423
+ };
424
+ req.onerror = function() { reject(req.error); };
425
+ });
426
+ };
427
+ })();
428
+ `;
429
+
430
+ await win.webContents.executeJavaScript(
431
+ "document.querySelector('#prompt-textarea')?.focus()"
432
+ );
433
+ await sendCDP(win.webContents, "Input.insertText", { text });
434
+
435
+ await new Promise((r) => setTimeout(r, 500));
436
+
437
+ await sendCDP(win.webContents, "Input.dispatchKeyEvent", { type: "keyDown", key: "Enter" });
438
+ await sendCDP(win.webContents, "Input.dispatchKeyEvent", { type: "keyUp", key: "Enter" });
439
+
440
+ var replyText = null;
441
+ var startTime = Date.now();
442
+
443
+ while (Date.now() - startTime < waitMs) {
444
+ await new Promise((r) => setTimeout(r, 1000));
445
+
446
+ var checkCode =
447
+ '(function(){var a=document.querySelectorAll("[data-message-author-role=\\"assistant\\"]");if(a.length>=2){var b=a[a.length-1];if(b.querySelector("[aria-label=\\"Copy\\"]")||b.querySelector("[aria-label=\\"Good response\\"]")){return"ok"}else{return"no-btn"}}return"waiting"})()';
448
+ var checkResult = await win.webContents.executeJavaScript(checkCode);
449
+
450
+ if (checkResult === "ok") {
451
+ var getCode =
452
+ '(async function(){var rows=await window._g.getIndexedDBRows("ConversationsDatabase","conversations");rows.sort(function(a,b){return b.updateTime-a.updateTime});var msgs=rows[0]?rows[0].messages:[];return msgs.length>0?msgs[msgs.length-1].text:""})()';
453
+ replyText = await win.webContents.executeJavaScript(getCode);
454
+ var duration = Date.now() - startTime;
455
+ return {
456
+ content: [
457
+ {
458
+ type: "text",
459
+ text: JSON.stringify({ reply: replyText, duration_ms: duration }, null, 2),
460
+ },
461
+ ],
462
+ };
463
+ }
464
+ }
465
+
466
+ if (!replyText) {
467
+ var getCode =
468
+ '(async function(){var rows=await window._g.getIndexedDBRows("ConversationsDatabase","conversations");rows.sort(function(a,b){return b.updateTime-a.updateTime});var msgs=rows[0]?rows[0].messages:[];return msgs.length>0?msgs[msgs.length-1].text:""})()';
469
+ replyText = await win.webContents.executeJavaScript(getCode);
470
+ }
471
+
472
+ var duration = Date.now() - startTime;
473
+ return {
474
+ content: [
475
+ {
476
+ type: "text",
477
+ text: JSON.stringify({ reply: replyText, duration_ms: duration }, null, 2),
478
+ },
479
+ ],
480
+ };
481
+ } catch (error) {
482
+ return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true };
483
+ }
484
+ },
485
+ { tag: "ChatGPT" }
486
+ );
487
+ }
488
+
489
+ module.exports = registerTools;