cicy-desktop 2.1.237 → 2.1.239

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/docs/REST-API.md DELETED
@@ -1,276 +0,0 @@
1
- # REST API
2
-
3
- This document describes the current HTTP API exposed by the CiCy Desktop worker.
4
-
5
- Source of truth:
6
- - worker entry: `src/main.js`
7
- - OpenAPI/docs routes: `src/server/express-app.js`
8
-
9
- ## Endpoint summary
10
-
11
- ### Public endpoints
12
-
13
- These do **not** require auth:
14
-
15
- - `GET /ping` — basic health check
16
- - `GET /docs` — Swagger-style API UI
17
- - `GET /openapi.json` — generated OpenAPI spec
18
-
19
- ### Authenticated RPC endpoints
20
-
21
- These **do** require auth:
22
-
23
- - `GET /rpc/tools`
24
- - `POST /rpc/tools/call`
25
- - `POST /rpc/:toolName`
26
- - `POST /rpc/upload/*`
27
- - `POST /rpc/exec/:type`
28
- - `GET /files`
29
- - `GET /api/worker`
30
- - `GET /api/agents`
31
- - `GET /api/artifacts`
32
-
33
- ## Authentication
34
-
35
- Authenticated routes expect:
36
-
37
- ```http
38
- Authorization: Bearer <token>
39
- ```
40
-
41
- Tokens are usually stored in `~/global.json` and are the same tokens used by `cicy-rpc`.
42
-
43
- If auth fails, the worker responds with:
44
-
45
- ```json
46
- {
47
- "error": "Unauthorized"
48
- }
49
- ```
50
-
51
- ## Core RPC routes
52
-
53
- ### `GET /rpc/tools`
54
-
55
- Returns the current tool catalog.
56
-
57
- Example:
58
-
59
- ```bash
60
- curl http://localhost:8101/rpc/tools \
61
- -H "Authorization: Bearer $TOKEN"
62
- ```
63
-
64
- JSON response shape:
65
-
66
- ```json
67
- {
68
- "tools": [
69
- {
70
- "name": "open_window",
71
- "description": "...",
72
- "inputSchema": {
73
- "type": "object"
74
- }
75
- }
76
- ]
77
- }
78
- ```
79
-
80
- YAML is also supported by setting:
81
-
82
- ```http
83
- Accept: application/yaml
84
- ```
85
-
86
- ### `POST /rpc/tools/call`
87
-
88
- Calls a tool by name using a generic wrapper endpoint.
89
-
90
- Example:
91
-
92
- ```bash
93
- curl -X POST http://localhost:8101/rpc/tools/call \
94
- -H "Authorization: Bearer $TOKEN" \
95
- -H "Content-Type: application/json" \
96
- -d '{
97
- "name": "open_window",
98
- "arguments": {
99
- "url": "https://example.com"
100
- }
101
- }'
102
- ```
103
-
104
- Request shape:
105
-
106
- ```json
107
- {
108
- "name": "tool_name",
109
- "arguments": {
110
- "key": "value"
111
- }
112
- }
113
- ```
114
-
115
- ### `POST /rpc/:toolName`
116
-
117
- Calls a tool directly.
118
-
119
- Example:
120
-
121
- ```bash
122
- curl -X POST http://localhost:8101/rpc/open_window \
123
- -H "Authorization: Bearer $TOKEN" \
124
- -H "Content-Type: application/json" \
125
- -d '{
126
- "url": "https://example.com",
127
- "accountIdx": 0
128
- }'
129
- ```
130
-
131
- This is the simplest REST form when you already know the tool name.
132
-
133
- ## Response format
134
-
135
- Successful tool responses are wrapped like this:
136
-
137
- ```json
138
- {
139
- "result": {
140
- "content": [
141
- {
142
- "type": "text",
143
- "text": "Pong"
144
- }
145
- ]
146
- }
147
- }
148
- ```
149
-
150
- Some tools return text content, some return structured JSON encoded in text, and some may return image content.
151
-
152
- If the client sends:
153
-
154
- ```http
155
- Accept: application/yaml
156
- ```
157
-
158
- then tool responses are returned as YAML.
159
-
160
- ## JSON and YAML support
161
-
162
- Current worker behavior:
163
-
164
- - `GET /rpc/tools` supports JSON and YAML output via `Accept`
165
- - `POST /rpc/tools/call` accepts JSON, and also accepts YAML when `Content-Type: application/yaml`
166
- - `POST /rpc/:toolName` accepts JSON, and also accepts YAML when `Content-Type: application/yaml`
167
- - tool responses can be returned as YAML when `Accept: application/yaml`
168
-
169
- Example YAML tool call:
170
-
171
- ```bash
172
- curl -X POST http://localhost:8101/rpc/exec_js \
173
- -H "Authorization: Bearer $TOKEN" \
174
- -H "Content-Type: application/yaml" \
175
- -H "Accept: application/yaml" \
176
- --data-binary $'win_id: 1\ncode: document.title\n'
177
- ```
178
-
179
- ## Common examples
180
-
181
- ### Health check
182
-
183
- ```bash
184
- curl http://localhost:8101/ping
185
- ```
186
-
187
- ### Ping tool
188
-
189
- ```bash
190
- curl -X POST http://localhost:8101/rpc/ping \
191
- -H "Authorization: Bearer $TOKEN" \
192
- -H "Content-Type: application/json" \
193
- -d '{}'
194
- ```
195
-
196
- ### Get windows
197
-
198
- ```bash
199
- curl -X POST http://localhost:8101/rpc/get_windows \
200
- -H "Authorization: Bearer $TOKEN" \
201
- -H "Content-Type: application/json" \
202
- -d '{}'
203
- ```
204
-
205
- ### Open a window
206
-
207
- ```bash
208
- curl -X POST http://localhost:8101/rpc/open_window \
209
- -H "Authorization: Bearer $TOKEN" \
210
- -H "Content-Type: application/json" \
211
- -d '{
212
- "url": "https://example.com",
213
- "accountIdx": 0,
214
- "reuseWindow": true
215
- }'
216
- ```
217
-
218
- ### Execute JavaScript
219
-
220
- ```bash
221
- curl -X POST http://localhost:8101/rpc/exec_js \
222
- -H "Authorization: Bearer $TOKEN" \
223
- -H "Content-Type: application/json" \
224
- -d '{
225
- "win_id": 1,
226
- "code": "document.title"
227
- }'
228
- ```
229
-
230
- ## OpenAPI and interactive docs
231
-
232
- The worker serves generated API docs directly:
233
-
234
- - `GET /docs`
235
- - `GET /openapi.json`
236
-
237
- Examples:
238
-
239
- ```bash
240
- open http://localhost:8101/docs
241
- curl http://localhost:8101/openapi.json
242
- ```
243
-
244
- ## Errors
245
-
246
- ### 401 Unauthorized
247
-
248
- ```json
249
- {
250
- "error": "Unauthorized"
251
- }
252
- ```
253
-
254
- ### 400 Invalid YAML
255
-
256
- ```json
257
- {
258
- "error": "Invalid YAML: ..."
259
- }
260
- ```
261
-
262
- ### 500 Tool execution error
263
-
264
- ```json
265
- {
266
- "error": "..."
267
- }
268
- ```
269
-
270
- If a tool raises a Zod validation error, the worker returns a `result` payload with `isError: true` instead of a generic HTTP 500.
271
-
272
- ## Related docs
273
-
274
- - [Root README](../README.md)
275
- - [CLI split](../skills/cicy-cli/README.md)
276
- - [RPC CLI README](../packages/cicy-rpc/README.md)
@@ -1,204 +0,0 @@
1
- # Backend Selector — Design
2
-
3
- Status: **draft for review**. Source of truth for `src/backends/` and the
4
- multi-window backend model. Adopted from the Genymotion shape: the desktop is
5
- a launcher / shell; the real environments (local sidecar, cloud cicy-code
6
- instances) live behind it as switchable backends.
7
-
8
- ## Vision
9
-
10
- One installable `cicy-desktop`. After launch the user lands on a **Launcher**
11
- window listing every backend they can reach:
12
-
13
- - **Local** — bundled `cicy-code` daemon spawned by the sidecar (already
14
- wired). Always present, always single instance.
15
- - **Cloud (catalog)** — instances the user owns on cicy-ai.com. Populated
16
- by the cloud catalog API once it exists. (Phase 2)
17
- - **Manual** — arbitrary URL + token the user pasted in. Lives forever
18
- in the local registry until removed.
19
-
20
- Each backend opens in its own `BrowserWindow`. Multiple windows can coexist
21
- targeting different backends (one local + two cloud, etc.). Closing the last
22
- local window keeps the sidecar running; quitting the app stops it.
23
-
24
- ## Phases
25
-
26
- | Phase | Scope | Blockers |
27
- |-------|-------|----------|
28
- | **1** | Local sidecar + manual backends + multi-window + native HTML launcher | none |
29
- | **2** | Cloud catalog: login to cicy-ai.com, pull "my instances", auto-add to registry | cicy-ai.com catalog endpoint must exist |
30
- | **3** | Polish: per-window status pill, reconnect button, backend health monitor, drag-reorder | none, can be incremental |
31
-
32
- ## Phase 1 — detailed design
33
-
34
- ### Data model
35
-
36
- ```ts
37
- type Backend = {
38
- id: string; // uuid; "local" reserved for the sidecar entry
39
- name: string; // user-visible label
40
- kind: "local" | "manual"; // Phase 2 adds "cloud"
41
- url: string; // base URL (no token); for local = http://127.0.0.1:8008
42
- token?: string; // bearer; absent for local (sidecar reads ~/global.json itself)
43
- addedAt: string; // ISO timestamp
44
- lastUsedAt?: string;
45
- };
46
- ```
47
-
48
- Persistence: `<userData>/backends.json`, written atomically. Schema versioned
49
- in a top-level `{ "version": 1, "backends": [...] }` envelope.
50
-
51
- The `local` entry is **auto-upserted** on every startup so the user cannot
52
- accidentally delete it; the Manage dialog hides the delete button for `local`.
53
-
54
- ### Modules
55
-
56
- ```
57
- src/backends/
58
- registry.js # BackendRegistry — file-backed CRUD + auto-upsert local
59
- window-manager.js # openWindowForBackend / closeAll / focus
60
- launcher.html # native HTML launcher (no React)
61
- launcher.js # launcher window's IPC + render logic (renderer-side)
62
- ipc.js # main-side IPC handlers backends:list / add / remove / open
63
- ```
64
-
65
- ### Sidecar lifecycle changes (small)
66
-
67
- `src/sidecar/cicy-code.js` already starts on `whenReady`. Phase 1 keeps that —
68
- the sidecar starts unconditionally on app launch so the local backend is
69
- "warm." When the catalog API ships (Phase 2) and the user wants to skip the
70
- local sidecar entirely, we'll add `--no-sidecar` and a per-launcher toggle.
71
-
72
- No multi-spawn risk: `sidecar.start()` already early-returns when `child` is
73
- non-null or when `:8008` already has a listener.
74
-
75
- ### UI flows
76
-
77
- **On launch.** No START_URL set → open launcher window (loads
78
- `backends/launcher.html`). The launcher renders the registry list and
79
- provides "Open", "+ Add backend", "Manage".
80
-
81
- If a `--backend <id>` argv is present, skip the launcher and open that
82
- backend directly. (Lets the existing `cicy-dektop.command` Desktop launcher
83
- keep its "always open my default" behavior.)
84
-
85
- **Open Window.** From launcher or menu → `backends.open(id)` IPC →
86
- `window-manager.openWindowForBackend(backend)`:
87
-
88
- - For `local`: await `sidecar.start()`, then `createWindow({ url: <sidecar URL with token from ~/global.json> })`. URL identical to the current single-window default.
89
- - For `manual` / `cloud`: `createWindow({ url: \`${backend.url}/console/chrome?token=${backend.token}\` })`. Token is injected at the URL level; window has no other env coupling.
90
-
91
- Each window gets a `Window.backendId` attached (via a `WeakMap` keyed on the
92
- BrowserWindow). On close, the entry is removed.
93
-
94
- **Add backend.** Form fields: name, URL, token. Validates URL parses, probes
95
- `<url>/health` with the token, on success writes to registry + refreshes
96
- list. No probe → still saves (offline edit).
97
-
98
- **Manage.** Lists all non-`local` entries with delete button. Local entry
99
- shown read-only at the top.
100
-
101
- ### IPC contract
102
-
103
- ```
104
- backends:list → Backend[]
105
- backends:add (input: {name, url, token, kind:"manual"}) → Backend
106
- backends:remove (id: string) → boolean
107
- backends:open (id: string) → {windowId: number}
108
- backends:probe ({url, token}) → {ok: bool, version?: string}
109
- ```
110
-
111
- All registered in `src/backends/ipc.js`; exposed to the launcher renderer
112
- via `contextBridge` in a small preload.
113
-
114
- ### Menu changes
115
-
116
- ```
117
- File
118
- New Local Window Cmd+N
119
- Open Backend Window… Cmd+Shift+N → launcher
120
- Manage Backends…
121
- ----
122
- Quit
123
- ```
124
-
125
- ### Sequence: app cold start with no argv
126
-
127
- ```
128
- electronApp.whenReady
129
- ├─ ensureDesktopLauncher (existing)
130
- ├─ sidecarCicyCode.start (existing)
131
- ├─ server.listen 8101 (existing MCP)
132
- └─ openLauncherWindow() ← new (replaces auto-createWindow(START_URL))
133
- └─ user clicks "Open" on a backend
134
- → windowManager.openWindowForBackend(backend)
135
- → BrowserWindow loading backend URL
136
- ```
137
-
138
- Existing behavior preserved: if `START_URL` env / argv is set, skip launcher
139
- and open it directly (keeps `node bin/cicy-desktop --url …` working).
140
-
141
- ## Phase 2 — cloud catalog (sketch only)
142
-
143
- Once cicy-ai.com exposes:
144
-
145
- ```
146
- GET https://cicy-ai.com/v1/instances (auth: Bearer <user token>)
147
- → { instances: [ { id, name, url, token, region, ... } ] }
148
- ```
149
-
150
- we add:
151
-
152
- - `src/auth/cicy-ai.js` — OAuth or token-paste flow against cicy-ai.com,
153
- stores user token in `<userData>/cicy-ai-auth.json` (mode 0600).
154
- - `BackendRegistry.refreshFromCloud()` — pulls /v1/instances, upserts
155
- entries with `kind: "cloud"`. Removes cloud entries that disappeared.
156
- - Launcher gains a "Sign in to cicy-ai.com" button + "Refresh" action.
157
-
158
- Cloud backends differ from manual only in:
159
- - They can be re-fetched and updated automatically.
160
- - Manage shows them read-only (server is the source of truth).
161
-
162
- ## Phase 3 — polish
163
-
164
- - Window title bar shows backend name + green/red dot for health (200 on
165
- `<url>/health` within 5 s).
166
- - "Reconnect" menu action for the active window.
167
- - Background poller: every 30 s probe each backend with a listening window
168
- open; surface disconnects as a notification.
169
- - Launcher: drag to reorder, search box, group by kind.
170
-
171
- ## Open questions
172
-
173
- 1. **Default backend on first launch.** Open launcher window, or auto-open
174
- the local backend (current behavior)? Lean toward launcher so the user
175
- sees their list immediately, but a "Open local by default" preference
176
- keeps the one-click path for power users.
177
- 2. **Sidecar autostart toggle.** When the user wants to use cloud-only and
178
- the local sidecar wastes resources. Adding `prefs.sidecarAutostart` is
179
- trivial; defer until someone asks.
180
- 3. **Per-backend window preferences.** Window position, zoom, devtools
181
- state — currently keyed on URL by `WindowState`. Re-keying on backendId
182
- would survive URL token rotation but is more code. Defer to Phase 3.
183
- 4. **Master/Worker cluster integration.** `src/master/` + `src/cluster/`
184
- currently treat the desktop as a worker registering to a master. That
185
- is **orthogonal** to backend selection; both can coexist. Worth
186
- re-evaluating in Phase 3 whether the cluster code is still needed at
187
- all once cloud catalog covers fleet view.
188
-
189
- ## Out of scope
190
-
191
- - Auto-update / version-pin per backend.
192
- - Backend-specific code-signing chains.
193
- - Cross-machine session sync.
194
- - Replacing the existing `src/master/` admin UI.
195
-
196
- ## Next concrete steps once approved
197
-
198
- 1. `src/backends/registry.js` + tests.
199
- 2. `src/backends/ipc.js` + `window-manager.js`.
200
- 3. `src/backends/launcher.html` + launcher renderer.
201
- 4. Wire menu items + initial-launch behavior into `src/main.js`.
202
- 5. Manual smoke: launch app → see launcher → open local → opens window →
203
- add a fake manual backend pointing at the same local URL → open second
204
- window → verify both windows render the chrome console independently.
@@ -1,142 +0,0 @@
1
- # Worker Chrome HTTP 代理路由(/chrome)说明文档
2
-
3
- ## 背景 / 目标
4
-
5
- 现状:worker 的 HTTP 服务端口(例如 8101)已承载 REST/RPC(`/rpc/*`),但各 Chrome profile 的 CDP 实际监听在各自的本地 debugger port(例如 11001)。外部目前只能通过 `/rpc/chrome_*` 工具间接操作,无法通过 worker 的稳定入口直接访问 Chrome 的常用 HTTP 端点(如 `/json/version`、`/json/list`、`/json/activate/*`)或发起任意 CDP method call。
6
-
7
- 目标:在不改变现有 `/rpc/:toolName` 调用方式的前提下,新增 worker 侧 HTTP 路由,让外部可以通过统一的 8101(或 worker 实际监听端口)入口,按 `accountIdx` 路由到对应 profile 的 debugger port。
8
-
9
- ## 新增能力(HTTP 路由)
10
-
11
- 以下路由均挂载在 worker 的 HTTP 服务下,并与 `/rpc/*` 一样受 `authMiddleware` 保护(Bearer token)。
12
-
13
- - `GET /chrome/:accountIdx/json/version`
14
- - `GET /chrome/:accountIdx/json/list`
15
- - `POST /chrome/:accountIdx/json/activate/:targetId`
16
- - `POST /chrome/:accountIdx/cdp/call`
17
-
18
- > 首版只做 HTTP facade(调用现有 helper),不做 websocket upgrade 透传。
19
-
20
- ## 端口解析规则(accountIdx -> debuggerPort)
21
-
22
- 与现有 `/rpc` 工具行为对齐,优先级如下:
23
-
24
- 1. 若 `~/cicy-ai/db/chrome.json` 中存在 `account_<idx>.port`,优先使用该 port
25
- 2. 否则回退到 `runtime-registry` 中 `registry.get(accountIdx)?.debuggerPort`
26
- 3. 都没有则返回错误(HTTP 404)
27
-
28
- 这部分被抽成共享逻辑,避免 `/rpc/chrome_*` 与 `/chrome/*` 对同一账号解析出不同端口。
29
-
30
- ## 认证
31
-
32
- 所有 `/chrome/*` 路由均复用既有 `authMiddleware`(与 `/rpc/*` 一致)。
33
-
34
- 请求示例(token 来自 `~/global.json` 的 `api_token`,或你自己的注入方式):
35
-
36
- ```bash
37
- curl -H "Authorization: Bearer <token>" \
38
- http://127.0.0.1:<PORT>/chrome/1/json/version
39
- ```
40
-
41
- ## API 细节与示例
42
-
43
- ### 1) 获取版本信息
44
-
45
- `GET /chrome/:accountIdx/json/version`
46
-
47
- 返回:Chrome 原始 `/json/version` JSON(不改写字段)。
48
-
49
- ```bash
50
- curl -H "Authorization: Bearer <token>" \
51
- http://127.0.0.1:<PORT>/chrome/1/json/version
52
- ```
53
-
54
- ### 2) 获取 targets 列表
55
-
56
- `GET /chrome/:accountIdx/json/list`
57
-
58
- 返回:Chrome 原始 `/json/list` 数组。
59
-
60
- ```bash
61
- curl -H "Authorization: Bearer <token>" \
62
- http://127.0.0.1:<PORT>/chrome/1/json/list
63
- ```
64
-
65
- ### 3) 激活某个 target
66
-
67
- `POST /chrome/:accountIdx/json/activate/:targetId`
68
-
69
- 返回:统一返回 JSON(避免 text/plain 不一致):
70
-
71
- ```json
72
- { "ok": true, "text": "Target activated" }
73
- ```
74
-
75
- ```bash
76
- curl -X POST -H "Authorization: Bearer <token>" \
77
- http://127.0.0.1:<PORT>/chrome/1/json/activate/<targetId>
78
- ```
79
-
80
- ### 4) 发起任意 CDP method call
81
-
82
- `POST /chrome/:accountIdx/cdp/call`
83
-
84
- Body:
85
-
86
- ```json
87
- { "method": "Browser.getVersion", "params": {}, "target": "optional" }
88
- ```
89
-
90
- 返回:
91
-
92
- ```json
93
- { "result": { ... } }
94
- ```
95
-
96
- ```bash
97
- curl -X POST \
98
- -H "Authorization: Bearer <token>" \
99
- -H "Content-Type: application/json" \
100
- -d '{"method":"Browser.getVersion","params":{}}' \
101
- http://127.0.0.1:<PORT>/chrome/1/cdp/call
102
- ```
103
-
104
- ## 错误语义(HTTP)
105
-
106
- - `accountIdx` 非整数:`400`
107
- `{"error":"accountIdx must be an integer"}`
108
- - account 找不到 port(private config + runtime 都没有):`404`
109
- `{"error":"Missing debuggerPort for accountIdx=<n>"}`
110
- - 上游 debugger port 不可达、或 CDP 调用失败:`502`
111
- `{"error":"<message>","debuggerPort":11001,...}`
112
- - `/cdp/call` 缺少 `method`:`400`
113
- `{"error":"Missing method"}`
114
-
115
- ## 非目标(本次不做)
116
-
117
- - 不做 websocket proxy / upgrade
118
- 因此也不改写 `/json/version` 和 `/json/list` 里的 `webSocketDebuggerUrl`,避免“看起来能连,实际无法 upgrade”的误导。
119
- - 不引入通用反向代理依赖,不做字节级 raw reverse proxy。
120
-
121
- ## 代码变更点(关键文件)
122
-
123
- - 新增:`src/server/chrome-proxy-routes.js`
124
- 实现 `/chrome/:accountIdx/...` 路由
125
- - 新增:`src/chrome/debugger-port-resolver.js`
126
- 统一端口解析优先级(private config > runtime registry)
127
- - 修改:`src/main.js`
128
- 挂载:`app.use("/chrome", authMiddleware, createChromeProxyRoutes(...))`
129
- - 修改:`src/tools/chrome-tools.js`
130
- 让 `chrome_get_targets` / `chrome_cdp_call` 复用同一端口解析逻辑(保持行为一致)
131
- - 新增测试:`tests/rpc/chrome-proxy-routes.test.js`
132
- 覆盖成功路径与关键错误语义
133
-
134
- ## 测试验证
135
-
136
- 已新增并通过的单测(只跑该文件):
137
-
138
- ```bash
139
- npx jest tests/rpc/chrome-proxy-routes.test.js --runInBand
140
- ```
141
-
142
- > 注:完整 `npm test` 当前在仓库内存在与本次变更无关的既有失败(例如其他 RPC suite 报 `sleep is not a function`),不属于本次引入回归。