Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

vg-coder-cli

Package Overview
Dependencies
Maintainers
1
Versions
106
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

vg-coder-cli - npm Package Compare versions

Comparing version
2.0.68
to
2.0.69
+1
-1
package.json
{
"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",

@@ -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