cicy-desktop 2.1.359 → 2.1.360

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": "cicy-desktop",
3
- "version": "2.1.359",
3
+ "version": "2.1.360",
4
4
  "description": "CiCy - AI-powered operating system browser",
5
5
  "main": "src/main.js",
6
6
  "bin": {
@@ -392,29 +392,53 @@ function register({ sidecarLogPath } = {}) {
392
392
  setInterval(() => { reconcileDocker().catch(() => {}); }, 60000); // keep fresh + self-heal
393
393
  }
394
394
 
395
- // 宿主 Chrome 代理(始终开启):从容器 cp mihomo 配置 → host-mihomo 在宿主起/续。
396
- // 已在跑也同步一次配置(容器侧节点可能被云端更新),变了才重启。幂等。二进制不在会自动下。
395
+ // 宿主 Chrome / Electron profile 代理(始终开启,且**不依赖 WSL 或容器**)。
396
+ //
397
+ // 每个 profile 的会话被指向 127.0.0.1:(20000+idx),所以这个 mihomo 必须存在,
398
+ // 否则那些 webview 一律 ERR_PROXY_CONNECTION_FAILED 白板。以前这里要先从
399
+ // cicy-code 容器里 cp 出 mihomo.yaml,没有容器(没装 WSL / WSL 坏了)就什么都不做 ——
400
+ // xs-master 就是这样:没有 WSL、没有容器,mihomo 从来没被装过,profile 1 的
401
+ // Telegram webview 一直白板(2026-09-13 实测)。
402
+ //
403
+ // 现在的语义:**默认装、默认起**。容器配置能读到就用它(云端下发的真实节点),
404
+ // 读不到就用宿主已有的 mihomo-host.yaml;都没有就按本机实际 profile 生成一份
405
+ // DIRECT 兜底配置先跑起来。等容器/云端配置到位,下一轮 reconcile 会覆盖它。
406
+ function hostProfileIdxs() {
407
+ try {
408
+ const store = require("../profiles/profile-store");
409
+ const ids = [];
410
+ for (const b of ["chrome", "electron"]) {
411
+ for (const p of store.listProfiles(b) || []) {
412
+ const n = Number(p && (p.accountIdx ?? p.idx));
413
+ if (Number.isInteger(n) && n >= 1) ids.push(n);
414
+ }
415
+ }
416
+ return [...new Set(ids)];
417
+ } catch (e) { return []; }
418
+ }
419
+
397
420
  async function maybeStartChromeProxy() {
398
421
  if (!APP_DOCKER_SUPPORTED) return;
399
422
  // 「独立管理」开关:标记文件在 → 不从容器同步覆盖宿主配置,host mihomo
400
423
  // 用宿主自己的 mihomo-host.yaml 独立跑(没跑就用现有宿主配置起来)。幂等。
401
424
  if (hostMihomo.standalonePinned()) {
402
- if (!hostMihomo.running() && hostMihomo.binPresent()) {
403
- try { await hostMihomo.enable({ containerYaml: null }); }
425
+ if (!hostMihomo.running()) {
426
+ try { await hostMihomo.enable({ containerYaml: null, profileIdxs: hostProfileIdxs() }); }
404
427
  catch (e) { log.warn(`[chrome-proxy] standalone start failed: ${e.message}`); }
405
428
  }
406
429
  return;
407
430
  }
431
+ // 容器读不到不再是致命的 —— catch 成 null,继续往独立模式走。
408
432
  const [yaml, selections] = await Promise.all([
409
- appDocker.readMihomoConfig(APP_CONTAINER),
410
- appDocker.readMihomoSelections(APP_CONTAINER),
433
+ appDocker.readMihomoConfig(APP_CONTAINER).catch(() => null),
434
+ appDocker.readMihomoSelections(APP_CONTAINER).catch(() => ({})),
411
435
  ]);
412
436
  if (hostMihomo.binPresent() && hostMihomo.running()) {
413
- if (hostMihomo.writeConfig(yaml)) hostMihomo.start({ force: true });
414
- await hostMihomo.syncSelections(selections);
437
+ if (yaml) { if (hostMihomo.writeConfig(yaml)) hostMihomo.start({ force: true }); await hostMihomo.syncSelections(selections); }
415
438
  return;
416
439
  }
417
- await hostMihomo.enable({ containerYaml: yaml, selections });
440
+ try { await hostMihomo.enable({ containerYaml: yaml, selections, profileIdxs: hostProfileIdxs() }); }
441
+ catch (e) { log.warn(`[chrome-proxy] enable failed: ${e.message}`); }
418
442
  }
419
443
 
420
444
  ipcMain.handle("sidecar:status", async () => {
package/src/main.js CHANGED
@@ -44,15 +44,23 @@ electronApp.on("web-contents-created", (_e, wc) => {
44
44
  // attachContextMenu). Wire copy directly on the guest. COPY-ONLY and WITHOUT
45
45
  // preventDefault, so the gotty terminal's Ctrl+C=SIGINT and any web app's own
46
46
  // copy handler still fire; wc.copy() is a no-op when there's no selection.
47
+ // 域名注入的**唯一统一入口**:凡是承载站点页面的 webContents 都走 domain-inject。
48
+ // · webview —— <webview> guest(如搜群后台里内嵌的 Telegram)
49
+ // · browserView —— 浏览器标签页 和 矩阵面板的格子(panel-cells.js 建的)
50
+ // BrowserWindow 自己的主 webContents 由 window-utils.js 挂,这里不重复。
51
+ //
52
+ // 以前这里只判 webview,于是 **标签页和面板格子一个都没注入**(都是 BrowserView):
53
+ // 实测 xs-master 的 desktop.cicy-ai.com / hub / 矩阵面板格子,__cicyInjected 全是 false,
54
+ // electronRPC 未定义 —— 放进 inject 目录的脚本在这些页面里从来不生效(2026-09-13)。
55
+ try {
56
+ const _t = wc.getType && wc.getType();
57
+ if (_t === "webview" || _t === "browserView") {
58
+ const { applyDomainInject } = require("./utils/domain-inject");
59
+ wc.on("dom-ready", () => applyDomainInject(wc));
60
+ }
61
+ } catch (_) {}
47
62
  try {
48
63
  if (wc.getType && wc.getType() === "webview") {
49
- // 所有 <webview> guest 都走 domain-inject:inject 文件读取逻辑原本只挂在
50
- // BrowserWindow 的主 webContents 上(window-utils.js),tab 里嵌套的 webview 拿不到。
51
- // 这里在 app 级 web-contents-created 上统一给每个 webview 挂 dom-ready 注入。
52
- try {
53
- const { applyDomainInject } = require("./utils/domain-inject");
54
- wc.on("dom-ready", () => applyDomainInject(wc));
55
- } catch (_) {}
56
64
  wc.on("before-input-event", (_ev, input) => {
57
65
  if (
58
66
  input.type === "keyDown" &&
@@ -290,12 +290,47 @@ function start({ force = false } = {}) {
290
290
  // without WSL at all. Only "no container config AND no host config" leaves us
291
291
  // nothing to start. Standalone has no authoritative selection source, so the
292
292
  // selection sync is skipped rather than overriding whatever the host holds.
293
- async function enable({ containerYaml, selections = {}, emit } = {}) {
293
+ // 没有任何配置可用时的兜底:按机器上实际存在的 profile 生成 listener
294
+ // (约定 端口 = 20000 + accountIdx,免鉴权、只绑 127.0.0.1),规则 MATCH,DIRECT。
295
+ //
296
+ // 为什么要有它:没有 WSL / 没有 cicy-code 容器的机器(xs-master 就是)拿不到云端
297
+ // 下发的 mihomo.yaml,原来这里直接抛 noContainerConfig —— 于是 mihomo 连装都不装,
298
+ // 那些 profile 的会话指向 127.0.0.1:2000N 却无人监听,webview 全部
299
+ // ERR_PROXY_CONNECTION_FAILED 白板(2026-09-13 在 xs-master 实测)。
300
+ // 先 DIRECT 让它通,之后容器/云端真配置一到,reconcile 循环会覆盖掉这份兜底。
301
+ function defaultHostConfig(idxs) {
302
+ const list = (Array.isArray(idxs) && idxs.length ? idxs : [1])
303
+ .map((n) => Number(n)).filter((n) => Number.isInteger(n) && n >= 1 && n <= 999);
304
+ const host = {
305
+ "mixed-port": HOST_MIXED,
306
+ "allow-lan": false,
307
+ "log-level": "warning",
308
+ "external-controller": `127.0.0.1:${HOST_CTRL}`,
309
+ secret: "",
310
+ dns: { enable: false },
311
+ listeners: [...new Set(list)].sort((a, b) => a - b).map((n) => ({
312
+ name: `chrome-profile-${n}`, type: "mixed", port: 20000 + n, listen: "127.0.0.1",
313
+ })),
314
+ proxies: [],
315
+ "proxy-groups": [],
316
+ rules: ["MATCH,DIRECT"],
317
+ };
318
+ return yaml.dump(host, { lineWidth: -1 });
319
+ }
320
+
321
+ function writeDefaultConfig(idxs) {
322
+ const next = defaultHostConfig(idxs);
323
+ fs.mkdirSync(path.dirname(HOST_CONFIG), { recursive: true });
324
+ fs.writeFileSync(HOST_CONFIG, next);
325
+ return true;
326
+ }
327
+
328
+ async function enable({ containerYaml, selections = {}, emit, profileIdxs } = {}) {
294
329
  await ensureBinary({ emit });
295
330
  let changed = false, standalone = false;
296
331
  if (containerYaml) changed = writeConfig(containerYaml);
297
332
  else if (fs.existsSync(HOST_CONFIG)) standalone = true;
298
- else throw new Error(tt("noContainerConfig"));
333
+ else { changed = writeDefaultConfig(profileIdxs); standalone = true; } // 完全独立:自己造一份能跑的
299
334
  const res = start({ force: changed });
300
335
  const synced = standalone ? { updated: [] } : await syncSelections(selections);
301
336
  emit && emit({ phase: "chrome-proxy", status: "running", message: tt("ready") });
@@ -303,6 +338,8 @@ async function enable({ containerYaml, selections = {}, emit } = {}) {
303
338
  }
304
339
 
305
340
  module.exports = {
341
+ defaultHostConfig,
342
+ writeDefaultConfig,
306
343
  VER, assetUrl, binPath, binPresent, ensureBinary,
307
344
  buildHostConfig, writeConfig, planSelectionUpdates, syncSelections,
308
345
  start, stop, running, enable, standalonePinned,