vg-coder-cli 2.0.69 → 2.0.70

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vg-coder-cli",
3
- "version": "2.0.69",
3
+ "version": "2.0.70",
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": {
@@ -848,6 +848,51 @@ class ApiServer {
848
848
  } catch (e) { res.status(launcherErr(e)).json({ error: e.message }); }
849
849
  });
850
850
 
851
+ // Proxy a chrome.* command through a worker (content-script → SW
852
+ // message bridge). Use when the launcher SW socket is offline but the
853
+ // worker socket is alive — sending the message also wakes the SW.
854
+ // Body: { chromeId? | workerLabel?, cmd, args?, timeoutMs? }
855
+ // Available cmds (mirror of launcher.exec, run via SW message):
856
+ // tabs.query | tabs.remove | tabs.update | tabs.reload
857
+ // storage.sync.get | storage.local.get | runtime.reload | wake
858
+ this.app.post('/api/worker/exec', async (req, res) => {
859
+ try {
860
+ const body = req.body || {};
861
+ if (!body.cmd) return res.status(400).json({ error: 'cmd required' });
862
+ const opts = workerOpts(req); // accepts label/workerLabel/chromeId via req
863
+ // workerOpts only reads label/workerLabel — pass chromeId via opts
864
+ // directly when body has it (workers are addressed by socket, not
865
+ // chromeId, so we resolve chromeId → workerLabel client-side here).
866
+ if (body.chromeId && !opts.workerLabel) {
867
+ // Find any worker matching chromeId; route via its socketId.
868
+ const w = [...taskQueue.workers.values()].find(w => w.chromeId === body.chromeId);
869
+ if (w) opts.socketId = w.id;
870
+ else return res.status(404).json({ error: 'worker_not_found', chromeId: body.chromeId });
871
+ }
872
+ const timeoutMs = Math.min(Math.max(parseInt(body.timeoutMs, 10) || 10_000, 1_000), 30_000);
873
+ const result = await taskQueue.requestWorker('worker:exec', { cmd: body.cmd, args: body.args }, opts, timeoutMs);
874
+ res.json(result);
875
+ } catch (e) { res.status(503).json({ error: e.message }); }
876
+ });
877
+
878
+ // Dump extension metadata (id, version, install type, permissions) via
879
+ // the worker → SW message bridge. Useful when multiple installs of the
880
+ // same extension exist (Web Store + Load Unpacked) and we need to know
881
+ // which one is actually running.
882
+ this.app.get('/api/worker/extension-info', async (req, res) => {
883
+ try {
884
+ const opts = workerOpts(req);
885
+ const chromeIdQuery = (req.query?.chromeId || '').toString().trim();
886
+ if (chromeIdQuery && !opts.workerLabel) {
887
+ const w = [...taskQueue.workers.values()].find(w => w.chromeId === chromeIdQuery);
888
+ if (w) opts.socketId = w.id;
889
+ else return res.status(404).json({ error: 'worker_not_found', chromeId: chromeIdQuery });
890
+ }
891
+ const result = await taskQueue.requestWorker('worker:ext-info', {}, opts, 5_000);
892
+ res.json(result);
893
+ } catch (e) { res.status(503).json({ error: e.message }); }
894
+ });
895
+
851
896
  this.app.get('/api/worker/status', (req, res) => {
852
897
  const label = (req.query?.label || req.query?.workerLabel || '').toString().toLowerCase().trim();
853
898
  res.json(taskQueue.workerStatus(label || undefined));
@@ -496,6 +496,59 @@ function connect() {
496
496
  } catch (e) { ack && ack({ ok: false, error: e.message, stack: e.stack }); }
497
497
  });
498
498
 
499
+ // Proxy chrome.* commands to the extension background SW via runtime
500
+ // message passing. Receiving the message also wakes the SW if it was
501
+ // evicted — that's the recovery path for "launcher offline" cases
502
+ // where the launcher socket is dead but the content-script socket
503
+ // (this worker) is alive.
504
+ socket.on('worker:exec', async ({ cmd, args } = {}, ack) => {
505
+ try {
506
+ if (!cmd) return ack && ack({ ok: false, error: 'cmd_required' });
507
+ if (typeof chrome === 'undefined' || !chrome.runtime?.sendMessage) {
508
+ return ack && ack({ ok: false, error: 'no_chrome_runtime' });
509
+ }
510
+ const reply = await new Promise((resolve) => {
511
+ try {
512
+ chrome.runtime.sendMessage({ action: 'WORKER_EXEC', cmd, args: args || {} }, (resp) => {
513
+ if (chrome.runtime.lastError) {
514
+ resolve({ ok: false, error: chrome.runtime.lastError.message });
515
+ return;
516
+ }
517
+ resolve(resp || { ok: false, error: 'no_response' });
518
+ });
519
+ } catch (err) {
520
+ resolve({ ok: false, error: err?.message || String(err) });
521
+ }
522
+ });
523
+ ack && ack(reply);
524
+ } catch (e) { ack && ack({ ok: false, error: e.message }); }
525
+ });
526
+
527
+ // Dump extension metadata via SW message — version, installType, ID,
528
+ // permissions. Useful when the user has multiple extension installs
529
+ // (Web Store + Load Unpacked) and we need to know which is live.
530
+ socket.on('worker:ext-info', async (_payload, ack) => {
531
+ try {
532
+ if (typeof chrome === 'undefined' || !chrome.runtime?.sendMessage) {
533
+ return ack && ack({ ok: false, error: 'no_chrome_runtime' });
534
+ }
535
+ const reply = await new Promise((resolve) => {
536
+ try {
537
+ chrome.runtime.sendMessage({ action: 'WORKER_EXT_INFO' }, (resp) => {
538
+ if (chrome.runtime.lastError) {
539
+ resolve({ ok: false, error: chrome.runtime.lastError.message });
540
+ return;
541
+ }
542
+ resolve(resp || { ok: false, error: 'no_response' });
543
+ });
544
+ } catch (err) {
545
+ resolve({ ok: false, error: err?.message || String(err) });
546
+ }
547
+ });
548
+ ack && ack({ ok: true, info: reply });
549
+ } catch (e) { ack && ack({ ok: false, error: e.message }); }
550
+ });
551
+
499
552
  socket.on('debug:logs', ({ since = 0, level = null, limit = 100, clear = false } = {}, ack) => {
500
553
  try {
501
554
  let logs = logBuffer.filter(e => e.at > Number(since || 0));