vcorp-chrome 2.2.0 → 2.2.1

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.
@@ -668,6 +668,7 @@ const updateTask = (jobId, fn) => serial(async () => {
668
668
  const HOST = 'ai.vcorp.desktop';
669
669
  const DESK = 'cloudDesktop'; // storage.local:启用桌面能力时宿主报的设备
670
670
  const DESK_PENDING = 'cloudDesktopPending'; // 为补上 nativeMessaging 绑定而重载,重载后接着启用
671
+ const DESK_ERR = 'cloudDesktopError'; // 上次启用失败的原因,给侧栏看。重载后的那次启用没人接它抛的错
671
672
  const DESK_SNAPS = 4; // 每个任务记几份快照的户口。户口按 ref 存名字,大应用一份上百 KB
672
673
 
673
674
  const deskInfo = async () => (await chrome.storage.local.get(DESK))[DESK] || null;
@@ -717,6 +718,7 @@ async function desktopChild() {
717
718
  }
718
719
 
719
720
  async function enableDesktop() {
721
+ await chrome.storage.local.remove(DESK_ERR);
720
722
  // 运行时才授予的 nativeMessaging,Chrome 不给已经在跑的 service worker 补上绑定,重载后才有
721
723
  if (!chrome.runtime.sendNativeMessage) {
722
724
  if (!(await chrome.permissions.contains({ permissions: ['nativeMessaging'] }))) {
@@ -727,9 +729,16 @@ async function enableDesktop() {
727
729
  chrome.runtime.reload();
728
730
  return;
729
731
  }
730
- const r = await hostCall({ op: 'hello' });
731
- const device = cleanDevice(r.device);
732
- if (!device || device.kind !== 'desktop') throw err('DEVICE_OFFLINE', '桌面宿主报上来的设备不对');
732
+ let device;
733
+ let r;
734
+ try {
735
+ r = await hostCall({ op: 'hello' });
736
+ device = cleanDevice(r.device);
737
+ if (!device || device.kind !== 'desktop') throw err('DEVICE_OFFLINE', '桌面宿主报上来的设备不对');
738
+ } catch (e) {
739
+ await chrome.storage.local.set({ [DESK_ERR]: (e.message || String(e)).replace('请用户在终端', '请在终端') });
740
+ throw e;
741
+ }
733
742
  await chrome.storage.local.set({ [DESK]: { device, ready: !!r.ready, at: Date.now() } });
734
743
  await log({ ev: 'desktop_on', msg: device.id });
735
744
  // 重连一次,服务器在 hello 里看到这台设备
@@ -737,7 +746,7 @@ async function enableDesktop() {
737
746
  }
738
747
 
739
748
  async function disableDesktop() {
740
- await chrome.storage.local.remove(DESK);
749
+ await chrome.storage.local.remove([DESK, DESK_ERR]);
741
750
  await log({ ev: 'desktop_off' });
742
751
  disconnect(); retryDelay = 0; void connect();
743
752
  }
@@ -1156,8 +1165,8 @@ async function uploadMedia(p, tid, sid, label) {
1156
1165
  const fromPanel = (sender) => !sender.tab && typeof sender.url === 'string' && sender.url.startsWith(chrome.runtime.getURL(''));
1157
1166
 
1158
1167
  async function panelState() {
1159
- const [cfg, { [STATUS]: status }, { [CLAIM]: claimed }, tasks, { [LOG]: list = [] }] = await Promise.all([
1160
- getCfg(), chrome.storage.session.get(STATUS), chrome.storage.session.get(CLAIM), getTasks(), chrome.storage.local.get(LOG),
1168
+ const [cfg, { [STATUS]: status }, { [CLAIM]: claimed }, tasks, { [LOG]: list = [], [DESK_ERR]: deskErr }] = await Promise.all([
1169
+ getCfg(), chrome.storage.session.get(STATUS), chrome.storage.session.get(CLAIM), getTasks(), chrome.storage.local.get([LOG, DESK_ERR]),
1161
1170
  ]);
1162
1171
  return {
1163
1172
  cfg: cfg && { origin: cfg.origin, account: cfg.account, sites: allowOf(cfg), auto: cfg.auto, autoConfirm: !!cfg.autoConfirm, paused: cfg.paused, pairedAt: cfg.pairedAt },
@@ -1165,7 +1174,7 @@ async function panelState() {
1165
1174
  claim: claimed && claimed.expiresAt > Date.now() ? { origin: claimed.origin, account: claimed.account } : null,
1166
1175
  tasks: Object.values(tasks).sort((a, b) => b.lastAt - a.lastAt).map(({ desk: _desk, ...t }) => t),
1167
1176
  folders: await folders.listFolders().catch(() => []),
1168
- desktop: await deskInfo().then((d) => (d && chrome.runtime.sendNativeMessage ? d : null)),
1177
+ desktop: await deskInfo().then((d) => (d && chrome.runtime.sendNativeMessage ? d : deskErr ? { error: deskErr } : null)),
1169
1178
  log: list.slice(-40).reverse(),
1170
1179
  defaultOrigin: policy.DEFAULT_ORIGIN,
1171
1180
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": 3,
3
3
  "name": "vcorp-chrome",
4
- "version": "2.2.0",
4
+ "version": "2.2.1",
5
5
  "description": "让 AI agent 操控你已登录的 Chrome。基于花叔的 huashu-chrome(MIT)修改。",
6
6
  "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAq6xRqEwG4yTuHRG1Zah3j//T3B72XnazyZ+9fOCa5P4bBLPmNBN7YAGL5WnjtPVJefXT9r2Ol0qvNztayDG3dMjK6AZoWccgKa5rhxcvWgFUjanXtmBBQ0qgYwNhuEJSkdinLLtyUg6YmK7Mn72NWpfa/NfqNHdK5AHslvvtJ50iQAYmabl2Nd3vOJ9DxtLBt8a2DfoGPWizgEB3+gkqm2PB8LywwwoiGKd7acr1wZEWmWK9/s0YWdzIstjlvk5AO5XFn6EtEzW8tAAOwBka9239pcyj0kEogWctHLJn7p9VFru38r+I1USyGn6u9XQ68n9edWddxBYokETCqEMT9QIDAQAB",
7
7
  "minimum_chrome_version": "116",
@@ -442,7 +442,9 @@ $('deskOn').onclick = async () => {
442
442
  const granted = await chrome.permissions.request({ permissions: ['nativeMessaging'] }).catch(() => false);
443
443
  if (!granted) { $('deskState').textContent = '没有拿到权限,桌面能力保持关闭'; return; }
444
444
  $('deskState').textContent = '启用中…第一次启用要重载扩展,侧栏若关掉了,重新打开即可';
445
- await cloudCall('desktopEnable');
445
+ const r = await cloudCall('desktopEnable');
446
+ // 失败原因写在桌面这一栏,不要留着「启用中…」,也不用在顶上再报一遍
447
+ if (r && !r.ok && r.error) { $('deskState').textContent = r.error; $('cloudErr').hidden = true; }
446
448
  };
447
449
  $('deskOff').onclick = async () => {
448
450
  await cloudCall('desktopDisable');
@@ -453,7 +455,7 @@ $('deskOff').onclick = async () => {
453
455
 
454
456
  chrome.storage.onChanged.addListener((changes, area) => {
455
457
  const keys0 = Object.keys(changes);
456
- if (keys0.some((k) => ['cloud', 'cloudTasks', 'cloudLog', 'cloudStatus', 'cloudClaim', 'folderTick', 'cloudDesktop'].includes(k))) void cloudCall('state');
458
+ if (keys0.some((k) => ['cloud', 'cloudTasks', 'cloudLog', 'cloudStatus', 'cloudClaim', 'folderTick', 'cloudDesktop', 'cloudDesktopError'].includes(k))) void cloudCall('state');
457
459
  if (area !== 'session') return;
458
460
  const keys = Object.keys(changes);
459
461
  if (keys.includes(`panel:${tabId}`) || keys.includes('liveSids')) renderPage();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vcorp-chrome",
3
- "version": "2.2.0",
3
+ "version": "2.2.1",
4
4
  "description": "让任何 AI agent 操控你自己的 Chrome——带着全部登录态。MCP + Chrome 扩展,Claude Code / Codex / Cursor / Gemini CLI 通用。",
5
5
  "type": "module",
6
6
  "bin": {
@@ -2,7 +2,7 @@
2
2
  //
3
3
  // Chrome 找宿主靠一份清单:Windows 在注册表 HKCU 下登记清单路径,macOS / Linux 把清单放进
4
4
  // 浏览器约定的目录。清单的 path 指向一个启动脚本,脚本用装它时的那个 node 跑 native-host/host.js。
5
- // allowed_origins 只写本扩展:扩展 id 由 manifest.json 里的 key 算出来,不手抄。
5
+ // allowed_origins 只写本扩展:开发者模式加载的 id 由 manifest.json 里的 key 算出来,再加上商店版的 id。
6
6
  import fs from 'node:fs';
7
7
  import os from 'node:os';
8
8
  import path from 'node:path';
@@ -28,6 +28,14 @@ function ourExtensionId() {
28
28
  return extensionId(mf.key);
29
29
  }
30
30
 
31
+ // 商店版的 id 是商店分配的:打商店包时 manifest.key 被剥掉(scripts/pack-extension.mjs),算不出来,只能写死
32
+ export const STORE_EXTENSION_ID = 'migojdmaegllchbambngejkclndannnf';
33
+
34
+ // 清单放行的来源:开发者模式加载的(id 由 key 算出)和商店装的,两种都是本扩展
35
+ export function allowedOrigins(id = ourExtensionId()) {
36
+ return [...new Set([id, STORE_EXTENSION_ID])].map((x) => `chrome-extension://${x}/`);
37
+ }
38
+
31
39
  const REG_KEYS = [
32
40
  `HKCU\\Software\\Google\\Chrome\\NativeMessagingHosts\\${HOST_NAME}`,
33
41
  `HKCU\\Software\\Microsoft\\Edge\\NativeMessagingHosts\\${HOST_NAME}`,
@@ -57,7 +65,7 @@ export function install() {
57
65
  description: 'vcorp-chrome desktop host',
58
66
  path: launcher,
59
67
  type: 'stdio',
60
- allowed_origins: [`chrome-extension://${id}/`],
68
+ allowed_origins: allowedOrigins(id),
61
69
  }, null, 2));
62
70
  const placed = [];
63
71
  if (win) {