vg-coder-cli
Advanced tools
+1
-1
| { | ||
| "name": "vg-coder-cli", | ||
| "version": "2.0.68", | ||
| "version": "2.0.69", | ||
| "description": "🚀 CLI tool to analyze projects, concatenate source files, count tokens, and export HTML with syntax highlighting and copy functionality", | ||
@@ -5,0 +5,0 @@ "main": "src/index.js", |
+63
-20
@@ -128,2 +128,3 @@ const { exec } = require('child_process'); | ||
| chromeId: w.chromeId || null, | ||
| tabId: typeof w.tabId === 'number' ? w.tabId : null, | ||
| status: w.status, | ||
@@ -184,2 +185,6 @@ currentTaskId: w.currentTaskId, | ||
| existing.chromeId = meta?.chromeId || this._emailToChromeId(email) || existing.chromeId || null; | ||
| // tabId enables surgical close on recycle (only this tab vs all | ||
| // AI Studio tabs in the profile). Older extension versions may not | ||
| // send it — fall back to the previously stored value or null. | ||
| if (typeof meta?.tabId === 'number') existing.tabId = meta.tabId; | ||
| if (wasUnknown && email !== existing.email) { | ||
@@ -207,2 +212,5 @@ // shouldn't happen — kept for clarity (email already assigned above) | ||
| chromeId: meta?.chromeId || this._emailToChromeId(email) || null, | ||
| // tabId from extension. When known, recycle closes only this tab — | ||
| // when null, recycle falls back to close-all-AI-Studio-tabs in profile. | ||
| tabId: typeof meta?.tabId === 'number' ? meta.tabId : null, | ||
| meta: { ...(meta || {}), registeredAt: Date.now() }, | ||
@@ -548,2 +556,3 @@ status: 'idle', | ||
| const workerChromeId = worker.chromeId || null; | ||
| const workerTabId = typeof worker.tabId === 'number' ? worker.tabId : null; | ||
| worker.currentTaskId = null; | ||
@@ -555,3 +564,3 @@ worker.status = 'idle'; | ||
| // discard result; still recycle the tab so next task gets a clean slate | ||
| this._recycleWorkerTab(workerEmail, workerChromeId); | ||
| this._recycleWorkerTab(workerEmail, workerChromeId, workerTabId); | ||
| setImmediate(() => this._drain()); | ||
@@ -577,3 +586,3 @@ return; | ||
| // launcher is the only deterministic way to lock the model per task. | ||
| this._recycleWorkerTab(workerEmail, workerChromeId); | ||
| this._recycleWorkerTab(workerEmail, workerChromeId, workerTabId); | ||
| // Drain right away so other idle workers pick up queued tasks while | ||
@@ -593,2 +602,3 @@ // this worker's tab is being recycled (~5s). | ||
| const workerChromeId = worker.chromeId || null; | ||
| const workerTabId = typeof worker.tabId === 'number' ? worker.tabId : null; | ||
| await this._failOrRequeue(task, worker, error); | ||
@@ -600,3 +610,3 @@ // Only recycle if the worker isn't in cooldown (rate-limited workers | ||
| if (!w || w.status !== 'rate_limited') { | ||
| this._recycleWorkerTab(workerEmail, workerChromeId); | ||
| this._recycleWorkerTab(workerEmail, workerChromeId, workerTabId); | ||
| } else { | ||
@@ -608,13 +618,14 @@ setTimeout(() => this._drain(), INTER_TASK_GAP_MS); | ||
| /** | ||
| * Ask the launcher in the given profile to close all AI Studio tabs and | ||
| * open a fresh one. The browser-side close fires worker:disconnect → | ||
| * clearWorker; the open creates a new tab whose worker registers ~3-5s | ||
| * later. _drain runs naturally on register so any queued task picks up | ||
| * the fresh worker. Fire-and-forget — errors are logged but don't bubble. | ||
| * Recycle the worker's tab: close it, then (unless close-only TTL) reopen | ||
| * a fresh one with the pinned model. Fire-and-forget — errors are logged | ||
| * but don't bubble. | ||
| * | ||
| * Prefers chromeId routing when provided (exact profile) — falls back to | ||
| * workerEmail when chromeId is unknown (e.g. extension hasn't propagated | ||
| * chromeId yet in Phase 1). | ||
| * Routing precedence: | ||
| * - chromeId pins the exact Chrome profile (falls back to email if absent) | ||
| * - tabId pins the exact AI Studio tab in that profile, so user-opened | ||
| * tabs in the same profile are NOT closed alongside the worker tab. | ||
| * When tabId is null (older extension version), close-all behavior is | ||
| * used as before — backward compatible. | ||
| */ | ||
| _recycleWorkerTab(workerEmail, chromeId = null) { | ||
| _recycleWorkerTab(workerEmail, chromeId = null, tabId = null) { | ||
| if (!workerEmail || workerEmail.startsWith('unknown:')) return; | ||
@@ -627,7 +638,10 @@ // Cancel any pending idle-close — we're about to recycle, no need to close | ||
| // by chromeId when present (exact profile), else by email (covers the | ||
| // chromeId-unknown path). | ||
| // chromeId-unknown path). When tabId is known, only mark the worker for | ||
| // that specific tab — other workers in the same profile (other tabs) | ||
| // stay idle and can keep accepting tasks. | ||
| for (const w of this.workers.values()) { | ||
| const matches = chromeId | ||
| ? w.chromeId === chromeId | ||
| : w.email === workerEmail; | ||
| let matches; | ||
| if (tabId != null) matches = w.tabId === tabId; | ||
| else if (chromeId) matches = w.chromeId === chromeId; | ||
| else matches = w.email === workerEmail; | ||
| if (matches && w.status === 'idle') w.status = 'recycling'; | ||
@@ -638,2 +652,5 @@ } | ||
| const pinKey = chromeId || workerEmail; | ||
| // Surgical close: pass tabId so the launcher closes only the worker's | ||
| // tab. Without tabId, launcher falls back to close-all (legacy behavior). | ||
| const closePayload = tabId != null ? { tabId } : {}; | ||
| // If WORKER_IDLE_TTL_MS === 1, treat post-task recycle as close-only: | ||
@@ -644,3 +661,3 @@ // skip reopen; let _autoLaunchBrowser open a fresh tab when next task lands. | ||
| try { | ||
| await this.requestLauncher('launcher:close_tab', {}, routeOpts, 5_000); | ||
| await this.requestLauncher('launcher:close_tab', closePayload, routeOpts, 5_000); | ||
| if (closeOnly) { | ||
@@ -709,5 +726,31 @@ console.log(chalk.cyan(`[TaskQueue] Closed tab for ${workerEmail} (VG_WORKER_IDLE_TTL_MS=1)`)); | ||
| } | ||
| console.log(chalk.gray(`[TaskQueue] Idle TTL expired for ${workerEmail} — closing tab`)); | ||
| this.requestLauncher('launcher:close_tab', {}, { workerLabel: workerEmail }, 5_000) | ||
| .catch(err => console.log(chalk.yellow(`[TaskQueue] Idle close ${workerEmail} failed: ${err.message}`))); | ||
| // Surgical close: collect tabIds of all idle workers in this email | ||
| // and close them one-by-one so user-opened tabs in the same profile | ||
| // survive. If a worker has no tabId (older extension), fall back to | ||
| // close-all-by-email for that worker. | ||
| const tabIdsByLauncher = new Map(); // routeOpts JSON → [tabId, ...] | ||
| let needCloseAll = false; | ||
| for (const w of this.workers.values()) { | ||
| if (w.email !== workerEmail) continue; | ||
| if (w.status !== 'idle') continue; | ||
| const route = w.chromeId ? { chromeId: w.chromeId } : { workerLabel: workerEmail }; | ||
| const key = JSON.stringify(route); | ||
| if (typeof w.tabId === 'number') { | ||
| if (!tabIdsByLauncher.has(key)) tabIdsByLauncher.set(key, { route, tabIds: [] }); | ||
| tabIdsByLauncher.get(key).tabIds.push(w.tabId); | ||
| } else { | ||
| needCloseAll = true; | ||
| } | ||
| } | ||
| console.log(chalk.gray(`[TaskQueue] Idle TTL expired for ${workerEmail} — closing tab(s)`)); | ||
| if (needCloseAll) { | ||
| this.requestLauncher('launcher:close_tab', {}, { workerLabel: workerEmail }, 5_000) | ||
| .catch(err => console.log(chalk.yellow(`[TaskQueue] Idle close ${workerEmail} failed: ${err.message}`))); | ||
| } | ||
| for (const { route, tabIds } of tabIdsByLauncher.values()) { | ||
| for (const tabId of tabIds) { | ||
| this.requestLauncher('launcher:close_tab', { tabId }, route, 5_000) | ||
| .catch(err => console.log(chalk.yellow(`[TaskQueue] Idle close tab ${tabId} failed: ${err.message}`))); | ||
| } | ||
| } | ||
| }, WORKER_IDLE_TTL_MS); | ||
@@ -714,0 +757,0 @@ if (t.unref) t.unref(); |
@@ -372,2 +372,6 @@ // Task Worker — runs inside the AI Studio page (shadow-DOM bundled). | ||
| const chromeId = (window.vetgo && window.vetgo.chromeId) || null; | ||
| // tabId injected by background.ts during CONTROLLER script load. | ||
| // Server uses it for surgical tab close in _recycleWorkerTab so user | ||
| // tabs in the same profile don't get closed alongside the worker tab. | ||
| const tabId = (window.vetgo && typeof window.vetgo.tabId === 'number') ? window.vetgo.tabId : null; | ||
| // pinnedModel: lấy từ URL ?model=X — server dùng để re-establish pin | ||
@@ -386,5 +390,6 @@ // sau khi worker register với email thật (không lúc open-tab vì lúc đó | ||
| chromeId, | ||
| tabId, | ||
| pinnedModel | ||
| }); | ||
| console.log(`[TaskWorker] Initial email: ${initialEmail || '(pending)'}, chromeId: ${chromeId || '(none)'}`); | ||
| console.log(`[TaskWorker] Initial email: ${initialEmail || '(pending)'}, chromeId: ${chromeId || '(none)'}, tabId: ${tabId || '(none)'}`); | ||
@@ -413,2 +418,3 @@ if (heartbeatTimer) clearInterval(heartbeatTimer); | ||
| chromeId: (window.vetgo && window.vetgo.chromeId) || null, | ||
| tabId: (window.vetgo && typeof window.vetgo.tabId === 'number') ? window.vetgo.tabId : null, | ||
| pinnedModel: pm | ||
@@ -415,0 +421,0 @@ }); |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
AI-detected potential malware
Supply chain riskAI has identified this package as malware. This is a strong signal that the package may be malicious.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 2 instances in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 5 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 2 instances in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
AI-detected potential malware
Supply chain riskAI has identified this package as malware. This is a strong signal that the package may be malicious.
Found 2 instances in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 2 instances in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 5 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 2 instances in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
10831216
0.04%18242
0.27%2
-33.33%37
-5.13%