vg-coder-cli 2.0.71 → 2.0.73

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 CHANGED
@@ -219,6 +219,8 @@ Server có thể chủ động list / đóng / mở tab AI Studio trong từng p
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
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
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) |
222
+ | `POST` | `/api/launcher/wait-ready` | `{ chromeId, timeoutMs?, requireEmail?, requireWorker? }` | Block sync cho đến khi launcher có email scraped + ≥1 idle worker. Thay 2 vòng poll bằng 1 call. (v2.0.68+) |
223
+ | `GET` | `/api/worker/extension-info` | `?chromeId=<uuid>` \| `?workerLabel=<email>` | Dump extension metadata (`installType`, `version`, `permissions`, `hostPermissions`) qua content-script. Dùng để diagnose duplicate installs (Load Unpacked vs Web Store). (v2.0.71+) |
222
224
 
223
225
  **Pin precedence**: `chromeId` > `workerLabel` > default. `chromeId` chỉ exact-match một
224
226
  launcher cụ thể — dùng để address **profile chưa bind email** (mới cài extension,
@@ -364,6 +366,109 @@ Pattern recommend cho client: bỏ qua `actual_model` ở open-tab response, ki
364
366
  tra `task.result.actualModel === requested_model` sau mỗi task done. Nếu khác
365
367
  → AI Studio đã silently fallback, retry với model khác hoặc fail-fast.
366
368
 
369
+ ### Sync ready check — `/api/launcher/wait-ready` (v2.0.68+)
370
+
371
+ Thay vì 2 vòng poll (`/api/launchers` để chờ email scrape + `/api/workers` để
372
+ chờ worker idle), endpoint này block 1 lần đến khi launcher sẵn sàng dispatch
373
+ task. Counter pattern thông thường:
374
+
375
+ ```bash
376
+ # 1. open-tab in chromeId X
377
+ curl -X POST -d '{"chromeId":"X","model":"gemini-3.1-pro-preview"}' \
378
+ http://127.0.0.1:6868/api/launcher/open-tab
379
+
380
+ # 2. wait until launcher email bound + worker registered + idle (one sync call)
381
+ curl -X POST -d '{"chromeId":"X","timeoutMs":15000}' \
382
+ http://127.0.0.1:6868/api/launcher/wait-ready
383
+ # → { ok, launcher: {email, ...}, worker: {email, status, ...}, elapsedMs }
384
+
385
+ # 3. submit task pinned to chromeId
386
+ curl -F prompt="..." -F chromeId=X http://127.0.0.1:6868/api/tasks
387
+ ```
388
+
389
+ Body fields:
390
+
391
+ | Field | Default | Mô tả |
392
+ |---|---|---|
393
+ | `chromeId` | required | UUID Chrome profile |
394
+ | `timeoutMs` | 15000 | clamp `[1000, 60000]` |
395
+ | `requireEmail` | `true` | đợi launcher.email != null (DOM scrape xong) |
396
+ | `requireWorker` | `true` | đợi ≥1 worker `idle` + chromeId match + email bound |
397
+
398
+ Status codes:
399
+ - **200**: `{ ok: true, launcher, worker, elapsedMs }` — sẵn sàng
400
+ - **404** `launcher_not_found`: chromeId chưa từng match launcher (immediate)
401
+ - **504** `wait_ready_timeout`: điều kiện không thoả trong timeoutMs (trả về `details` để debug)
402
+
403
+ ### Extension diagnostics — `/api/worker/extension-info` (v2.0.71+)
404
+
405
+ Dump metadata của extension đang chạy trong tab AI Studio — `installType`,
406
+ `version`, `runtimeId`, `permissions`. Dùng khi:
407
+
408
+ - Phát hiện profile có duplicate install (Load Unpacked + Web Store cùng tồn tại)
409
+ - Verify extension version sau khi update npm package
410
+ - Diagnose "tabId null" (extension cũ chưa propagate field)
411
+
412
+ ```bash
413
+ curl -s "http://127.0.0.1:6868/api/worker/extension-info?workerLabel=alice@gmail.com" | jq
414
+ ```
415
+
416
+ Response:
417
+
418
+ ```json
419
+ {
420
+ "ok": true,
421
+ "info": {
422
+ "runtimeId": "comfeilpfnlaoijgndaikpniioglmonf",
423
+ "version": "1.0.0",
424
+ "name": "VetGo Pro",
425
+ "manifestVersion": 3,
426
+ "installType": "development", // "development" = Load Unpacked; "normal" = Web Store
427
+ "enabled": true,
428
+ "permissions": ["alarms", "cookies", ...],
429
+ "hostPermissions": ["<all_urls>"]
430
+ },
431
+ "tabUrl": "https://aistudio.google.com/...",
432
+ "vetgo": {
433
+ "chromeId": "...",
434
+ "tabId": 437926742,
435
+ "extInfo": { ... } // same content as .info, source of truth
436
+ }
437
+ }
438
+ ```
439
+
440
+ `.info` lấy từ `window.vetgo.extInfo` (background.ts inject lúc CONTROLLER script
441
+ load). Page-context content-script không có `chrome.runtime.sendMessage` → đây là
442
+ duy nhất cách lấy metadata từ extension về.
443
+
444
+ **Trường hợp thường gặp**: `installType="normal"` + `runtimeId` khác với mọi profile
445
+ khác → Web Store install cũ vẫn còn active, đè Load Unpacked. Fix: `chrome://extensions`
446
+ → Remove Web Store version → restart Chrome.
447
+
448
+ ### ChromeId stability — `storage.local` migration (v2.0.72+)
449
+
450
+ Trước v2.0.72: chromeId lưu trong `chrome.storage.sync` (đồng bộ qua Google
451
+ account). Nếu reinstall extension trong profile có account từng cài extension
452
+ khác → storage.sync sync ngược UUID cũ về → launcher SW init mint UUID mới
453
+ nhưng background.ts CONTROLLER handler đọc UUID đã sync ngược → 2 chromeId
454
+ khác nhau cho cùng 1 profile.
455
+
456
+ Từ v2.0.72: dùng `chrome.storage.local` (per-profile, không cross account).
457
+ Migration tự động: lần đầu boot extension, nếu `storage.local.id` rỗng nhưng
458
+ `storage.sync.id` có → copy `.sync.id` sang `.local.id`. Sau đó `.local.id`
459
+ là source of truth.
460
+
461
+ Verify chromeId nhất quán:
462
+
463
+ ```bash
464
+ curl -s -X POST -d '{"all":true,"cmd":"storage.local.get","args":{"keys":["id"]}}' \
465
+ -H 'Content-Type: application/json' \
466
+ http://127.0.0.1:6868/api/launcher/exec \
467
+ | jq '.[] | {launcher: .launcher.chromeId, local: .value.id, ok: (.launcher.chromeId == .value.id)}'
468
+ ```
469
+
470
+ Tất cả `ok: true` = pipeline đúng.
471
+
367
472
  ### Modal auto-handling
368
473
 
369
474
  AI Studio thỉnh thoảng pop modal chặn task. Worker tự detect + click button đúng (poll mỗi 400 ms):
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vg-coder-cli",
3
- "version": "2.0.71",
3
+ "version": "2.0.73",
4
4
  "description": "🚀 CLI tool to analyze projects, concatenate source files, count tokens, and export HTML with syntax highlighting and copy functionality",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -1009,13 +1009,12 @@ class TaskQueue {
1009
1009
  _autoLaunchBrowser(task = null) {
1010
1010
  const now = Date.now();
1011
1011
  if (now - this.launchedAt < AUTO_LAUNCH_DEBOUNCE_MS) return;
1012
- this.launchedAt = now;
1013
1012
 
1014
1013
  // Prefer asking a connected launcher (extension background SW in the right
1015
- // Chrome profile) to open / focus the AI Studio tab. Falls back to OS
1016
- // `open` if no launcher matches the pin or none are connected.
1014
+ // Chrome profile) to open / focus the AI Studio tab.
1017
1015
  const launcher = this._pickLauncher(task);
1018
1016
  if (launcher) {
1017
+ this.launchedAt = now;
1019
1018
  const tag = launcher.email || `chromeId=${launcher.chromeId.slice(0, 8)}…`;
1020
1019
  console.log(chalk.cyan(`[TaskQueue] No matching worker — asking launcher ${tag} to open AI Studio`));
1021
1020
  try {
@@ -1033,10 +1032,37 @@ class TaskQueue {
1033
1032
  return;
1034
1033
  } catch (err) {
1035
1034
  console.log(chalk.yellow(`[TaskQueue] Launcher emit failed: ${err.message}`));
1036
- // fall through to OS open
1035
+ // fall through to OS open ONLY if task has no explicit pin
1036
+ }
1037
+ }
1038
+
1039
+ // If the task has an explicit pin (chromeId or workerLabel) and no launcher
1040
+ // matches, do NOT fall back to OS `open` — that command opens the URL in
1041
+ // whatever Chrome profile is currently focused on the host (potentially a
1042
+ // profile that uninstalled the extension or belongs to a different user),
1043
+ // leaking the task target into an unrelated profile and respawning every
1044
+ // 30s as long as the task stays queued. Fail the task instead.
1045
+ const hasExplicitPin = !!(task?.chromeId || task?.workerLabel);
1046
+ if (hasExplicitPin) {
1047
+ const pinDesc = task.chromeId ? `chromeId=${task.chromeId.slice(0, 8)}…` : `workerLabel=${task.workerLabel}`;
1048
+ console.log(chalk.yellow(`[TaskQueue] Task ${task.id} pinned to ${pinDesc} but no matching launcher — failing without OS-open fallback`));
1049
+ // Mark task failed so _drain doesn't keep re-triggering autoLaunch.
1050
+ const t = this.cache.get(task.id);
1051
+ if (t && t.status === 'queued') {
1052
+ t.status = 'failed';
1053
+ t.error = { code: 'launcher_not_found', message: `No connected launcher matches pin ${pinDesc}` };
1054
+ t.timing.finishedAt = Date.now();
1055
+ t.timing.durationMs = t.timing.finishedAt - (t.timing.startedAt || t.timing.createdAt);
1056
+ this.pending = this.pending.filter(p => p.id !== task.id);
1057
+ store.saveTask(t).catch(() => {});
1058
+ webhook.deliver(t).catch(() => {});
1037
1059
  }
1060
+ return;
1038
1061
  }
1039
1062
 
1063
+ // No pin at all — safe to OS-open default URL since the task accepts
1064
+ // any profile. Apply debounce so we don't spam tabs across the host.
1065
+ this.launchedAt = now;
1040
1066
  let cmd;
1041
1067
  switch (process.platform) {
1042
1068
  case 'darwin': cmd = `open "${AUTO_LAUNCH_URL}"`; break;