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,158 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const yaml = require('js-yaml');
6
+
7
+ // 从服务器获取工具列表和 schemas
8
+ async function getToolsWithSchemas() {
9
+ const http = require('http');
10
+ const tokenPath = path.join(require('os').homedir(), 'data/electron/token.txt');
11
+ const token = fs.readFileSync(tokenPath, 'utf8').trim();
12
+
13
+ // 获取工具列表
14
+ const tools = await new Promise((resolve, reject) => {
15
+ http.get('http://localhost:8101/rpc/tools', {
16
+ headers: { Authorization: `Bearer ${token}` }
17
+ }, (res) => {
18
+ let data = '';
19
+ res.on('data', chunk => data += chunk);
20
+ res.on('end', () => resolve(JSON.parse(data).tools));
21
+ }).on('error', reject);
22
+ });
23
+
24
+ // 获取 schemas
25
+ const schemas = await new Promise((resolve, reject) => {
26
+ http.get('http://localhost:8101/rpc/schemas', {
27
+ headers: { Authorization: `Bearer ${token}` }
28
+ }, (res) => {
29
+ let data = '';
30
+ res.on('data', chunk => data += chunk);
31
+ res.on('end', () => resolve(JSON.parse(data).schemas));
32
+ }).on('error', reject);
33
+ });
34
+
35
+ return tools.map(tool => ({
36
+ ...tool,
37
+ schema: schemas[tool.name] || { type: 'object' }
38
+ }));
39
+ }
40
+
41
+ async function generateOpenAPI() {
42
+ const tools = await getToolsWithSchemas();
43
+
44
+ const openapi = {
45
+ openapi: '3.0.0',
46
+ info: {
47
+ title: 'Electron MCP REST API',
48
+ version: '1.0.0',
49
+ description: `REST API for Electron MCP tools - ${tools.length} tools available`,
50
+ },
51
+ servers: [
52
+ {
53
+ url: 'http://localhost:8101',
54
+ description: 'Local server',
55
+ },
56
+ {
57
+ url: 'https://gcp-8101.cicy.de5.net',
58
+ description: 'Remote server',
59
+ },
60
+ ],
61
+ components: {
62
+ securitySchemes: {
63
+ bearerAuth: {
64
+ type: 'http',
65
+ scheme: 'bearer',
66
+ bearerFormat: 'token',
67
+ },
68
+ },
69
+ },
70
+ security: [{ bearerAuth: [] }],
71
+ paths: {}
72
+ };
73
+
74
+ // 添加 /rpc/tools 端点
75
+ openapi.paths['/rpc/tools'] = {
76
+ get: {
77
+ summary: 'List all available tools',
78
+ tags: ['Tools'],
79
+ security: [{ bearerAuth: [] }],
80
+ responses: {
81
+ 200: {
82
+ description: 'List of tools',
83
+ content: {
84
+ 'application/json': {
85
+ schema: {
86
+ type: 'object',
87
+ properties: {
88
+ tools: {
89
+ type: 'array',
90
+ items: {
91
+ type: 'object',
92
+ properties: {
93
+ name: { type: 'string' },
94
+ description: { type: 'string' }
95
+ }
96
+ }
97
+ }
98
+ }
99
+ }
100
+ }
101
+ }
102
+ }
103
+ }
104
+ }
105
+ };
106
+
107
+ // 为每个工具生成端点
108
+ for (const tool of tools) {
109
+ openapi.paths[`/rpc/${tool.name}`] = {
110
+ post: {
111
+ description: tool.description,
112
+ tags: ['Tools'],
113
+ security: [{ bearerAuth: [] }],
114
+ requestBody: {
115
+ required: true,
116
+ content: {
117
+ 'application/json': {
118
+ schema: tool.schema
119
+ }
120
+ }
121
+ },
122
+ responses: {
123
+ 200: {
124
+ description: 'Tool execution result',
125
+ content: {
126
+ 'application/json': {
127
+ schema: {
128
+ type: 'object',
129
+ properties: {
130
+ content: {
131
+ type: 'array',
132
+ items: {
133
+ type: 'object',
134
+ properties: {
135
+ type: { type: 'string' },
136
+ text: { type: 'string' }
137
+ }
138
+ }
139
+ }
140
+ }
141
+ }
142
+ }
143
+ }
144
+ },
145
+ 404: { description: 'Tool not found' },
146
+ 500: { description: 'Execution error' }
147
+ }
148
+ }
149
+ };
150
+ }
151
+
152
+ // 保存为 YAML
153
+ const yamlContent = yaml.dump(openapi, { lineWidth: -1 });
154
+ fs.writeFileSync('openapi.yml', yamlContent);
155
+ console.log('✅ Generated: openapi.yml');
156
+ }
157
+
158
+ generateOpenAPI().catch(console.error);
package/jest.config.js ADDED
@@ -0,0 +1,10 @@
1
+ module.exports = {
2
+ testEnvironment: "node",
3
+ testMatch: ["<rootDir>/tests/**/*.test.js"],
4
+ testTimeout: 30000,
5
+ maxWorkers: 1,
6
+ globalSetup: "<rootDir>/jest.setup.global.js",
7
+ globalTeardown: "<rootDir>/jest.teardown.global.js",
8
+ forceExit: true,
9
+ detectOpenHandles: false,
10
+ };
@@ -0,0 +1,13 @@
1
+ const { startTestServer } = require('./tests/mcp/setup-test-server');
2
+
3
+ module.exports = async () => {
4
+ console.log('\n🚀 Starting test server...\n');
5
+ try {
6
+ await startTestServer();
7
+ console.log('\n✅ Test server ready\n');
8
+ } catch (error) {
9
+ console.error('\n❌ Failed to start test server:', error.message);
10
+ console.log('\n⚠️ Continuing without SSE connection...\n');
11
+ // 不抛出错误,让测试继续(服务器已启动,只是 SSE 连接失败)
12
+ }
13
+ };
@@ -0,0 +1,7 @@
1
+ const { stopTestServer } = require('./tests/mcp/setup-test-server');
2
+
3
+ module.exports = async () => {
4
+ console.log('\n🛑 Stopping test server...\n');
5
+ await stopTestServer();
6
+ console.log('\n✅ Test server stopped\n');
7
+ };
package/package.json ADDED
@@ -0,0 +1,75 @@
1
+ {
2
+ "name": "cicy-desktop",
3
+ "version": "1.0.8",
4
+ "description": "CiCy - AI-powered operating system browser",
5
+ "main": "src/main.js",
6
+ "bin": {
7
+ "cicy": "./bin/cicy"
8
+ },
9
+ "scripts": {
10
+ "start": "electron .",
11
+ "format": "prettier --write \"src/**/*.js\" \"tests/**/*.js\"",
12
+ "format:check": "prettier --check \"src/**/*.js\" \"tests/**/*.js\"",
13
+ "test": "jest",
14
+ "test:ls": "node tests/test-ls.js",
15
+ "build": "electron-builder --publish never",
16
+ "build:win": "electron-builder --win --publish never",
17
+ "build:linux": "electron-builder --linux --publish never"
18
+ },
19
+ "build": {
20
+ "appId": "com.electron.mcp",
21
+ "productName": "Electron MCP",
22
+ "directories": {
23
+ "output": "dist"
24
+ },
25
+ "files": [
26
+ "src/**/*",
27
+ "package.json"
28
+ ],
29
+ "win": {
30
+ "target": [
31
+ "nsis"
32
+ ]
33
+ },
34
+ "linux": {
35
+ "target": [
36
+ "deb",
37
+ "AppImage"
38
+ ],
39
+ "category": "Development"
40
+ }
41
+ },
42
+ "keywords": [
43
+ "cicy",
44
+ "ai",
45
+ "browser",
46
+ "electron",
47
+ "mcp"
48
+ ],
49
+ "author": "cicybot",
50
+ "license": "MIT",
51
+ "dependencies": {
52
+ "@modelcontextprotocol/sdk": "^1.25.3",
53
+ "chrome-remote-interface": "^0.33.0",
54
+ "cors": "^2.8.5",
55
+ "electron": "^41.0.2",
56
+ "electron-context-menu": "^4.0.4",
57
+ "electron-log": "^5.2.4",
58
+ "express": "^4.18.2",
59
+ "js-beautify": "^1.15.4",
60
+ "js-yaml": "^4.1.1",
61
+ "serve-index": "^1.9.2",
62
+ "swagger-jsdoc": "^6.2.8",
63
+ "swagger-ui-express": "^5.0.1",
64
+ "zod": "3.25"
65
+ },
66
+ "devDependencies": {
67
+ "@babel/core": "^7.29.0",
68
+ "@babel/preset-env": "^7.29.0",
69
+ "axios": "^1.13.5",
70
+ "electron-builder": "^26.7.0",
71
+ "jest": "^29.7.0",
72
+ "prettier": "^3.8.1",
73
+ "supertest": "^6.3.3"
74
+ }
75
+ }
package/service.sh ADDED
@@ -0,0 +1,164 @@
1
+ #!/bin/bash
2
+
3
+ # Electron MCP 服务管理脚本
4
+
5
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6
+ PROJECT_DIR="$SCRIPT_DIR"
7
+ PID_FILE="$PROJECT_DIR/.electron-mcp.pid"
8
+ LOG_FILE="$HOME/logs/electron-mcp-service.log"
9
+
10
+ # 创建日志目录
11
+ mkdir -p "$(dirname "$LOG_FILE")"
12
+
13
+ # 显示帮助信息
14
+ show_help() {
15
+ echo "Electron MCP 服务管理"
16
+ echo ""
17
+ echo "用法: $0 {start|stop|restart|status|logs}"
18
+ echo ""
19
+ echo "命令:"
20
+ echo " start - 启动服务"
21
+ echo " stop - 停止服务"
22
+ echo " restart - 重启服务"
23
+ echo " status - 查看状态"
24
+ echo " logs - 查看日志"
25
+ echo ""
26
+ }
27
+
28
+ # 启动服务
29
+ start_service() {
30
+ if [ -f "$PID_FILE" ]; then
31
+ local pid=$(cat "$PID_FILE")
32
+ if ps -p "$pid" > /dev/null 2>&1; then
33
+ echo "✅ 服务已在运行 (PID: $pid)"
34
+ return 0
35
+ else
36
+ rm -f "$PID_FILE"
37
+ fi
38
+ fi
39
+
40
+ echo "🚀 正在启动 Electron MCP 服务..."
41
+
42
+ cd "$PROJECT_DIR"
43
+
44
+ # 检查依赖
45
+ if [ ! -d "node_modules" ]; then
46
+ echo "📦 正在安装依赖..."
47
+ npm install
48
+ fi
49
+
50
+ # 后台启动服务
51
+ nohup npm start > "$LOG_FILE" 2>&1 &
52
+ local pid=$!
53
+
54
+ # 保存 PID
55
+ echo "$pid" > "$PID_FILE"
56
+
57
+ # 等待服务启动
58
+ sleep 3
59
+
60
+ if ps -p "$pid" > /dev/null 2>&1; then
61
+ echo "✅ 服务启动成功 (PID: $pid)"
62
+ echo "📋 端口: 8101"
63
+ echo "📋 日志: $LOG_FILE"
64
+ echo "📋 MCP 端点: http://localhost:8101/mcp"
65
+ echo "📋 API 文档: http://localhost:8101/docs"
66
+ else
67
+ echo "❌ 服务启动失败"
68
+ rm -f "$PID_FILE"
69
+ return 1
70
+ fi
71
+ }
72
+
73
+ # 停止服务
74
+ stop_service() {
75
+ if [ ! -f "$PID_FILE" ]; then
76
+ echo "⚠️ 服务未运行"
77
+ return 0
78
+ fi
79
+
80
+ local pid=$(cat "$PID_FILE")
81
+
82
+ if ps -p "$pid" > /dev/null 2>&1; then
83
+ echo "🛑 正在停止服务 (PID: $pid)..."
84
+ kill "$pid"
85
+
86
+ # 等待进程结束
87
+ local count=0
88
+ while ps -p "$pid" > /dev/null 2>&1 && [ $count -lt 10 ]; do
89
+ sleep 1
90
+ count=$((count + 1))
91
+ done
92
+
93
+ if ps -p "$pid" > /dev/null 2>&1; then
94
+ echo "⚠️ 强制停止服务..."
95
+ kill -9 "$pid"
96
+ fi
97
+
98
+ echo "✅ 服务已停止"
99
+ else
100
+ echo "⚠️ 服务进程不存在"
101
+ fi
102
+
103
+ rm -f "$PID_FILE"
104
+ }
105
+
106
+ # 查看状态
107
+ show_status() {
108
+ if [ -f "$PID_FILE" ]; then
109
+ local pid=$(cat "$PID_FILE")
110
+ if ps -p "$pid" > /dev/null 2>&1; then
111
+ echo "✅ 服务运行中 (PID: $pid)"
112
+ echo "📋 端口: 8101"
113
+ echo "📋 日志: $LOG_FILE"
114
+
115
+ # 检查端口是否监听
116
+ if lsof -i :8101 > /dev/null 2>&1; then
117
+ echo "📋 端口 8101 正在监听"
118
+ else
119
+ echo "⚠️ 端口 8101 未监听"
120
+ fi
121
+ else
122
+ echo "❌ 服务进程不存在 (PID 文件存在但进程已死)"
123
+ rm -f "$PID_FILE"
124
+ fi
125
+ else
126
+ echo "⚠️ 服务未运行"
127
+ fi
128
+ }
129
+
130
+ # 查看日志
131
+ show_logs() {
132
+ if [ -f "$LOG_FILE" ]; then
133
+ echo "📋 日志文件: $LOG_FILE"
134
+ echo "----------------------------------------"
135
+ tail -f "$LOG_FILE"
136
+ else
137
+ echo "⚠️ 日志文件不存在: $LOG_FILE"
138
+ fi
139
+ }
140
+
141
+ # 主逻辑
142
+ case "$1" in
143
+ start)
144
+ start_service
145
+ ;;
146
+ stop)
147
+ stop_service
148
+ ;;
149
+ restart)
150
+ stop_service
151
+ sleep 2
152
+ start_service
153
+ ;;
154
+ status)
155
+ show_status
156
+ ;;
157
+ logs)
158
+ show_logs
159
+ ;;
160
+ *)
161
+ show_help
162
+ exit 1
163
+ ;;
164
+ esac
package/src/config.js ADDED
@@ -0,0 +1,8 @@
1
+ module.exports = {
2
+ config: {
3
+ port: null,
4
+ logsDir: null,
5
+ logFilePath: null,
6
+ oneWindow: false,
7
+ },
8
+ };
@@ -0,0 +1,135 @@
1
+ console.log("hi cicy v4");
2
+
3
+ window.__cicyInjected = true;
4
+ window.__cicyTime = new Date().toISOString();
5
+
6
+ // 创建全局工具对象
7
+ window._g = window._g || {};
8
+
9
+ // ========================================
10
+ // Electron RPC Bridge - control everything
11
+ // ========================================
12
+ try {
13
+ const { ipcRenderer } = require('electron');
14
+ window.electronRPC = (tool, args) => ipcRenderer.invoke('rpc', tool, args || {});
15
+ window._g.rpc = window.electronRPC;
16
+ console.log('[RPC] electronRPC ready');
17
+ } catch(e) {
18
+ console.log('[RPC] not in electron:', e.message);
19
+ }
20
+
21
+ // ========================================
22
+ // 剪贴板权限支持
23
+ // ========================================
24
+
25
+ // 确保剪贴板 API 可用
26
+ if (navigator.clipboard) {
27
+ console.log("[Clipboard] Clipboard API is available");
28
+
29
+ // 测试剪贴板权限
30
+ navigator.permissions.query({ name: 'clipboard-read' }).then(result => {
31
+ console.log(`[Clipboard] Read permission: ${result.state}`);
32
+ }).catch(e => {
33
+ console.log("[Clipboard] Read permission query failed:", e.message);
34
+ });
35
+
36
+ navigator.permissions.query({ name: 'clipboard-write' }).then(result => {
37
+ console.log(`[Clipboard] Write permission: ${result.state}`);
38
+ }).catch(e => {
39
+ console.log("[Clipboard] Write permission query failed:", e.message);
40
+ });
41
+ } else {
42
+ console.log("[Clipboard] Clipboard API is not available");
43
+ }
44
+
45
+ // 添加剪贴板工具函数
46
+ window._g.clipboard = {
47
+ // 读取剪贴板文本
48
+ readText: async () => {
49
+ try {
50
+ return await navigator.clipboard.readText();
51
+ } catch (e) {
52
+ console.error("[Clipboard] Read text failed:", e);
53
+ throw e;
54
+ }
55
+ },
56
+
57
+ // 写入剪贴板文本
58
+ writeText: async (text) => {
59
+ try {
60
+ await navigator.clipboard.writeText(text);
61
+ console.log("[Clipboard] Text written successfully");
62
+ } catch (e) {
63
+ console.error("[Clipboard] Write text failed:", e);
64
+ throw e;
65
+ }
66
+ },
67
+
68
+ // 读取剪贴板(包括图片)
69
+ read: async () => {
70
+ try {
71
+ return await navigator.clipboard.read();
72
+ } catch (e) {
73
+ console.error("[Clipboard] Read failed:", e);
74
+ throw e;
75
+ }
76
+ },
77
+
78
+ // 写入剪贴板(包括图片)
79
+ write: async (data) => {
80
+ try {
81
+ await navigator.clipboard.write(data);
82
+ console.log("[Clipboard] Data written successfully");
83
+ } catch (e) {
84
+ console.error("[Clipboard] Write failed:", e);
85
+ throw e;
86
+ }
87
+ }
88
+ };
89
+
90
+ // ========================================
91
+ // IndexedDB 基础工具
92
+ // ========================================
93
+
94
+ window._g.getIndexedDBRows = async (dbName, storeName, limit = 100) => {
95
+ const db = await new Promise((resolve, reject) => {
96
+ const request = indexedDB.open(dbName);
97
+ request.onsuccess = () => resolve(request.result);
98
+ request.onerror = () => reject(request.error);
99
+ });
100
+ const tx = db.transaction(storeName, "readonly");
101
+ const store = tx.objectStore(storeName);
102
+ const results = [];
103
+ return new Promise((resolve) => {
104
+ const request = store.openCursor();
105
+ request.onsuccess = (e) => {
106
+ const cursor = e.target.result;
107
+ if (cursor && results.length < limit) {
108
+ results.push(cursor.value);
109
+ cursor.continue();
110
+ } else {
111
+ resolve(results);
112
+ }
113
+ };
114
+ });
115
+ };
116
+
117
+ window._g.listIndexedDB = async () => {
118
+ const dbs = await indexedDB.databases();
119
+ const result = {};
120
+ for (const dbInfo of dbs) {
121
+ try {
122
+ const db = await new Promise((resolve, reject) => {
123
+ const req = indexedDB.open(dbInfo.name);
124
+ req.onsuccess = () => resolve(req.result);
125
+ req.onerror = () => reject(req.error);
126
+ setTimeout(() => reject(new Error("timeout")), 1000);
127
+ });
128
+ result[dbInfo.name] = Array.from(db.objectStoreNames);
129
+ db.close();
130
+ } catch (e) {
131
+ result[dbInfo.name] = "error: " + e.message;
132
+ }
133
+ }
134
+ return result;
135
+ };