vg-coder-cli 2.0.65 → 2.0.67
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/INTEGRATION.md +94 -0
- package/package.json +1 -1
- package/src/server/api-server.js +11 -6
- package/src/server/task-queue.js +11 -1
- package/src/server/views/vg-coder/background.js +108 -16
package/INTEGRATION.md
CHANGED
|
@@ -217,6 +217,8 @@ Server có thể chủ động list / đóng / mở tab AI Studio trong từng p
|
|
|
217
217
|
| `GET` | `/api/launcher/tabs` | `?chromeId=<uuid>` \| `?label=<email>` (optional) | List tabs trong profile (hoặc all profiles nếu bỏ pin) |
|
|
218
218
|
| `POST` | `/api/launcher/close-tab` | `{ chromeId? \| workerLabel?, tabId?, all? }` | Đóng tab cụ thể, hoặc tất cả tab AI Studio nếu bỏ `tabId` |
|
|
219
219
|
| `POST` | `/api/launcher/open-tab` | `{ chromeId? \| workerLabel?, model?, url?, active? }` | Mở tab mới. `model` mặc định `gemini-3-flash-preview`. Response v2.0.52+ kèm `requested_model` / `actual_model` / `fallback_occurred` (URL-based, **không reliable** với AI Studio versions mới — xem note) |
|
|
220
|
+
| `GET` | `/api/launcher/debug` | `?chromeId=<uuid>` \| `?workerLabel=<email>` (optional) | Dump launcher SW state: `swChromeId`, `syncStorage`, `localStorage`, AI Studio tabs, windows count, runtime info. Bỏ pin → broadcast all launchers. (v2.0.65+) |
|
|
221
|
+
| `POST` | `/api/launcher/exec` | `{ chromeId? \| workerLabel? \| all?, cmd, args?, timeoutMs? }` | Chạy 1 lệnh `chrome.*` đã định nghĩa sẵn trong launcher SW. (v2.0.66+, thay cho `eval` bị Manifest V3 CSP chặn) |
|
|
220
222
|
|
|
221
223
|
**Pin precedence**: `chromeId` > `workerLabel` > default. `chromeId` chỉ exact-match một
|
|
222
224
|
launcher cụ thể — dùng để address **profile chưa bind email** (mới cài extension,
|
|
@@ -243,6 +245,98 @@ curl -X POST -d '{"workerLabel":"alice@gmail.com","model":"gemini-3-flash-previe
|
|
|
243
245
|
-H 'Content-Type: application/json' http://127.0.0.1:6868/api/launcher/open-tab
|
|
244
246
|
```
|
|
245
247
|
|
|
248
|
+
### Remote launcher debug (v2.0.65+)
|
|
249
|
+
|
|
250
|
+
Hai endpoint dùng để inspect / điều khiển launcher SW từ xa thay vì phải mở
|
|
251
|
+
DevTools trong từng Chrome profile:
|
|
252
|
+
|
|
253
|
+
**`GET /api/launcher/debug`** — dump state cố định:
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
# Một launcher cụ thể
|
|
257
|
+
curl -s "http://127.0.0.1:6868/api/launcher/debug?chromeId=6b420bac-..." | jq
|
|
258
|
+
|
|
259
|
+
# Broadcast 5 launchers (bỏ pin)
|
|
260
|
+
curl -s http://127.0.0.1:6868/api/launcher/debug | jq
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Response per launcher:
|
|
264
|
+
|
|
265
|
+
```json
|
|
266
|
+
{
|
|
267
|
+
"ok": true,
|
|
268
|
+
"data": {
|
|
269
|
+
"swChromeId": "6b420bac-...",
|
|
270
|
+
"runtimeId": "comfeilpfnlaoijgndaikpniioglmonf",
|
|
271
|
+
"extVersion": "1.0.0",
|
|
272
|
+
"extName": "VetGo Pro",
|
|
273
|
+
"syncStorage": { "id": "6b420bac-..." },
|
|
274
|
+
"localStorage": {},
|
|
275
|
+
"profileInfo": null,
|
|
276
|
+
"socketId": "KFh5YmiCbYTGBmAzAAAB",
|
|
277
|
+
"serverUrl": "http://127.0.0.1:6868",
|
|
278
|
+
"tabs": {
|
|
279
|
+
"aistudio": [{ "id": 437926431, "url": "...", "status": "complete", "active": true }],
|
|
280
|
+
"totalCount": 12
|
|
281
|
+
},
|
|
282
|
+
"windows": 2,
|
|
283
|
+
"userAgent": "Mozilla/5.0 ..."
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
Dùng để verify chromeId pipeline (launcher SW chromeId == `chrome.storage.sync.id`),
|
|
289
|
+
phát hiện profile có nhiều cửa sổ / quá nhiều tab, debug AI Studio tab missing.
|
|
290
|
+
|
|
291
|
+
**`POST /api/launcher/exec`** — chạy 1 lệnh đã định nghĩa sẵn trong launcher SW.
|
|
292
|
+
Manifest V3 cấm `new Function()` nên không có eval tự do — đây là tập 10 lệnh
|
|
293
|
+
chrome.* được whitelist, mỗi lệnh có schema args cố định:
|
|
294
|
+
|
|
295
|
+
| `cmd` | `args` | Trả về |
|
|
296
|
+
|---|---|---|
|
|
297
|
+
| `tabs.query` | `chrome.tabs.QueryInfo` (`{ url?, active?, ... }`) | Mảng tab info `{ id, windowId, url, title, status, active, ... }` |
|
|
298
|
+
| `tabs.get` | `{ tabId: number }` | 1 tab info |
|
|
299
|
+
| `tabs.update` | `{ tabId: number, ...props }` (`url`, `active`, `pinned`, ...) | `{ id, url, active }` |
|
|
300
|
+
| `tabs.reload` | `{ tabId: number, bypassCache?: bool }` | `{ ok: true, tabId }` |
|
|
301
|
+
| `storage.sync.get` | `{ keys?: string \| string[] \| null }` (null = all) | `Record<string, unknown>` |
|
|
302
|
+
| `storage.sync.set` | `{ items: Record<string, unknown> }` | `{ ok: true }` |
|
|
303
|
+
| `storage.local.get` | `{ keys?: ... }` | `Record<string, unknown>` |
|
|
304
|
+
| `runtime.reload` | — | `{ ok: true, scheduled: true }` (extension reload sau ~50ms; ack có thể không tới do SW chết) |
|
|
305
|
+
| `windows.list` | — | Mảng window info `{ id, focused, type, state, incognito }` |
|
|
306
|
+
| `cookies.get` | `{ url: string, name?: string }` | 1 cookie nếu `name`, mảng cookies nếu chỉ `url`. Mỗi cookie: `{ name, value, domain, path, expirationDate, secure, httpOnly }` |
|
|
307
|
+
|
|
308
|
+
Unknown `cmd` → `{ ok: false, error: "unknown_cmd", available: [...] }` (discovery).
|
|
309
|
+
|
|
310
|
+
Ví dụ:
|
|
311
|
+
|
|
312
|
+
```bash
|
|
313
|
+
# Phải có chromeId nào sync với storage UUID không?
|
|
314
|
+
curl -s -X POST -H 'Content-Type: application/json' \
|
|
315
|
+
-d '{"chromeId":"6b420bac-...","cmd":"storage.sync.get"}' \
|
|
316
|
+
http://127.0.0.1:6868/api/launcher/exec | jq '.value'
|
|
317
|
+
# → { "id": "6b420bac-..." }
|
|
318
|
+
|
|
319
|
+
# Profile login Google chưa?
|
|
320
|
+
curl -s -X POST -H 'Content-Type: application/json' \
|
|
321
|
+
-d '{"chromeId":"6b420bac-...","cmd":"cookies.get","args":{"url":"https://accounts.google.com","name":"SID"}}' \
|
|
322
|
+
http://127.0.0.1:6868/api/launcher/exec | jq '.value'
|
|
323
|
+
# → { "name": "SID", "value": "...", ... } nếu đã login, null nếu chưa
|
|
324
|
+
|
|
325
|
+
# Reload extension (debug last resort)
|
|
326
|
+
curl -s -X POST -H 'Content-Type: application/json' \
|
|
327
|
+
-d '{"chromeId":"6b420bac-...","cmd":"runtime.reload"}' \
|
|
328
|
+
http://127.0.0.1:6868/api/launcher/exec
|
|
329
|
+
|
|
330
|
+
# Broadcast: tab AI Studio đang mở ở profile nào?
|
|
331
|
+
curl -s -X POST -H 'Content-Type: application/json' \
|
|
332
|
+
-d '{"all":true,"cmd":"tabs.query","args":{"url":"*://aistudio.google.com/*"}}' \
|
|
333
|
+
http://127.0.0.1:6868/api/launcher/exec \
|
|
334
|
+
| jq '.[] | { chromeId: .launcher.chromeId, tabs: (.value | length) }'
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
**Thêm command mới** → sửa `execCommands` map trong [vetgo-auto/chrome/src/launcher.ts](vetgo-auto/chrome/src/launcher.ts),
|
|
338
|
+
rebuild + bump version + reload extension trong từng Chrome profile.
|
|
339
|
+
|
|
246
340
|
`open-tab` response (v2.0.52+):
|
|
247
341
|
|
|
248
342
|
```json
|
package/package.json
CHANGED
package/src/server/api-server.js
CHANGED
|
@@ -799,16 +799,21 @@ class ApiServer {
|
|
|
799
799
|
} catch (e) { res.status(launcherErr(e)).json({ error: e.message }); }
|
|
800
800
|
});
|
|
801
801
|
|
|
802
|
-
// Run
|
|
803
|
-
//
|
|
804
|
-
// a
|
|
805
|
-
|
|
802
|
+
// Run a predefined chrome.* command inside the launcher SW. Manifest V3
|
|
803
|
+
// CSP forbids new Function() in service workers, so eval was replaced by
|
|
804
|
+
// a fixed command map. Body:
|
|
805
|
+
// { chromeId? | workerLabel?, cmd, args?, timeoutMs? }
|
|
806
|
+
// Available cmds (see launcher.ts execCommands):
|
|
807
|
+
// tabs.query | tabs.get | tabs.update | tabs.reload
|
|
808
|
+
// storage.sync.get | storage.sync.set | storage.local.get
|
|
809
|
+
// runtime.reload | windows.list | cookies.get
|
|
810
|
+
this.app.post('/api/launcher/exec', async (req, res) => {
|
|
806
811
|
try {
|
|
807
812
|
const body = req.body || {};
|
|
808
|
-
if (!body.
|
|
813
|
+
if (!body.cmd) return res.status(400).json({ error: 'cmd required' });
|
|
809
814
|
const opts = launcherOpts(body) || (body.all ? { all: true } : {});
|
|
810
815
|
const timeoutMs = Math.min(Math.max(parseInt(body.timeoutMs, 10) || 10_000, 1_000), 30_000);
|
|
811
|
-
const result = await taskQueue.requestLauncher('launcher:
|
|
816
|
+
const result = await taskQueue.requestLauncher('launcher:exec', { cmd: body.cmd, args: body.args }, opts, timeoutMs);
|
|
812
817
|
res.json(result);
|
|
813
818
|
} catch (e) { res.status(launcherErr(e)).json({ error: e.message }); }
|
|
814
819
|
});
|
package/src/server/task-queue.js
CHANGED
|
@@ -818,13 +818,23 @@ class TaskQueue {
|
|
|
818
818
|
}
|
|
819
819
|
|
|
820
820
|
async _dispatch(task, worker) {
|
|
821
|
+
// Mark worker busy synchronously BEFORE any await so that concurrent
|
|
822
|
+
// _drain() ticks (each enqueue schedules its own setImmediate(_drain))
|
|
823
|
+
// don't both observe the same worker as idle and dispatch two tasks to
|
|
824
|
+
// it. Previously `worker.status = 'busy'` happened after `await
|
|
825
|
+
// store.saveTask` — the await yielded, a second drain saw worker idle,
|
|
826
|
+
// and a second task was dispatched onto the same worker. The worker can
|
|
827
|
+
// only run one task at a time, so the second one would hang in cache
|
|
828
|
+
// status='running' forever (worker.currentTaskId was overwritten and
|
|
829
|
+
// onWorkerComplete's `worker.currentTaskId !== taskId` guard rejected
|
|
830
|
+
// the late completion as stale).
|
|
821
831
|
task.status = 'running';
|
|
822
832
|
task.timing.startedAt = Date.now();
|
|
823
833
|
task.worker = { socketId: worker.id, email: worker.email, ...(worker.meta || {}) };
|
|
824
|
-
await store.saveTask(task);
|
|
825
834
|
this.cache.set(task.id, task);
|
|
826
835
|
worker.currentTaskId = task.id;
|
|
827
836
|
worker.status = 'busy';
|
|
837
|
+
await store.saveTask(task);
|
|
828
838
|
|
|
829
839
|
const port = this.io?.httpServer?.address?.()?.port || 6868;
|
|
830
840
|
const baseUrl = `http://127.0.0.1:${port}`;
|