cicy-desktop 2.1.360 → 2.1.362

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.360",
3
+ "version": "2.1.362",
4
4
  "description": "CiCy - AI-powered operating system browser",
5
5
  "main": "src/main.js",
6
6
  "bin": {
@@ -5,45 +5,57 @@
5
5
  <meta name="viewport" content="width=device-width,initial-scale=1">
6
6
  <title>IP 核对</title>
7
7
  <style>
8
- :root { color-scheme: dark; --bg:#0A0A0A; --cell:#141416; --line:rgba(255,255,255,.10); --fg:#e6e6e9; --muted:#8b8b92; --ok:#3fb950; --warn:#f0b429; --bad:#ff5c5c; --accent:#4aa3ff; font-family: Inter,"Segoe UI","PingFang SC","Microsoft YaHei",sans-serif; }
8
+ /* 和面板「设置」弹窗同一套语汇:居中卡片、同样的圆角边框、同样的 标签/值 两栏排版。
9
+ 所有 overlay(接码 / IP 核对 / 加群面板)看起来要像同一个东西,不是三张不同的网页。 */
10
+ :root { color-scheme: dark; --bg:#0A0A0A; --cell:#141416; --line:rgba(255,255,255,.10); --fg:#e6e6e9;
11
+ --muted:#8b8b92; --ok:#3fb950; --warn:#f0b429; --bad:#ff5c5c; --accent:#4aa3ff;
12
+ font-family: Inter,"Segoe UI","PingFang SC","Microsoft YaHei",sans-serif; }
9
13
  * { box-sizing:border-box; }
10
- html,body { margin:0; width:100%; height:100%; background:var(--bg); color:var(--fg); overflow:auto; }
11
- .wrap { padding:10px 12px; display:flex; flex-direction:column; gap:12px; }
12
- .card { border:1px solid var(--line); border-radius:10px; background:var(--cell); padding:10px 12px; }
13
- .card h3 { margin:0 0 8px; font-size:12px; color:var(--muted); font-weight:600; letter-spacing:.02em; }
14
- .r { display:flex; align-items:center; gap:8px; font-size:12.5px; line-height:2; }
15
- .r .k { flex:none; width:104px; color:var(--muted); }
16
- .r .v { flex:1; min-width:0; font-family:ui-monospace,monospace; word-break:break-all; }
17
- .r .v b { color:var(--fg); }
18
- .mut { color:var(--muted); }
14
+ html,body { margin:0; width:100%; height:100%; background:var(--bg); color:var(--fg); }
15
+ /* 外层浮层已经是一张卡片(带标题栏),这里只铺内容,别再套一层框 */
16
+ body { overflow:auto; padding:0 16px 16px; }
17
+ .card { width:100%; background:transparent; border:0; padding:0; }
18
+ .head { display:none; }
19
+ .lbl { display:block; font-size:12px; color:var(--muted); margin:14px 0 6px; }
20
+ .lbl:first-of-type { margin-top:0; }
21
+ .box { border:1px solid var(--line); border-radius:10px; padding:12px 14px; background:#0f0f11;
22
+ display:flex; flex-direction:column; gap:4px; }
23
+ .r { display:flex; gap:12px; font-size:12px; line-height:1.65; align-items:baseline; }
24
+ .r .k { flex:none; width:92px; color:var(--muted); }
25
+ .r .v { flex:1; min-width:0; color:var(--fg); word-break:break-all; font-family:ui-monospace,monospace; user-select:all; }
26
+ .r .v b { font-weight:600; }
27
+ .mut { color:var(--muted); font-family:inherit; }
19
28
  .ok { color:var(--ok); } .warn { color:var(--warn); } .bad { color:var(--bad); }
20
- .badge { display:inline-block; padding:1px 8px; border-radius:999px; font-size:11px; font-weight:600; }
21
- .badge.dc { background:rgba(255,92,92,.15); color:#ff9e9e; border:1px solid rgba(255,92,92,.4); }
22
- .badge.res { background:rgba(63,185,80,.15); color:#7ee787; border:1px solid rgba(63,185,80,.4); }
29
+ .badge { display:inline-block; padding:1px 9px; border-radius:999px; font-size:11px; font-weight:600;
30
+ font-family:inherit; }
31
+ .badge.dc { background:rgba(255,92,92,.16); color:#ff9e9e; }
32
+ .badge.res { background:rgba(63,185,80,.16); color:#7ee787; }
23
33
  .badge.unk { background:var(--line); color:var(--muted); }
24
34
  a { color:var(--accent); text-decoration:none; } a:hover { text-decoration:underline; }
25
35
  .links { display:flex; flex-wrap:wrap; gap:6px 14px; font-size:12px; }
26
36
  </style>
27
37
  </head>
28
38
  <body>
29
- <div class="wrap">
30
- <div class="card">
31
- <h3>出口 IP</h3>
39
+ <div class="card">
40
+ <div class="head"><span>IP 核对</span></div>
41
+
42
+ <label class="lbl">出口 IP</label>
43
+ <div class="box">
32
44
  <div class="r"><span class="k">代理探测出口</span><span class="v" id="proxyip"><span class="mut">—</span></span></div>
33
45
  <div class="r"><span class="k">浏览器真实出口</span><span class="v" id="realip"><span class="mut">读取中…</span></span></div>
34
46
  <div class="r"><span class="k">一致性</span><span class="v" id="match"><span class="mut">—</span></span></div>
35
47
  </div>
36
- <div class="card">
37
- <h3>IP 情报</h3>
48
+
49
+ <label class="lbl">IP 情报</label>
50
+ <div class="box">
38
51
  <div class="r"><span class="k">归属地</span><span class="v" id="geo"><span class="mut">—</span></span></div>
39
52
  <div class="r"><span class="k">ASN / 运营商</span><span class="v" id="asn"><span class="mut">—</span></span></div>
40
53
  <div class="r"><span class="k">类型</span><span class="v" id="type"><span class="mut">—</span></span></div>
41
54
  <div class="r"><span class="k">数据源</span><span class="v" id="src"><span class="mut">—</span></span></div>
42
55
  </div>
43
- <div class="card">
44
- <h3>完整风控 / 详情(新窗口)</h3>
45
- <div class="links" id="links"><span class="mut">拿到真实出口 IP 后生成…</span></div>
46
- </div>
56
+
57
+ <label class="lbl">完整风控 / 详情(新窗口)</label>
58
+ <div class="box"><div class="links" id="links"><span class="mut">拿到真实出口 IP 后生成…</span></div></div>
47
59
  </div>
48
60
  <script>
49
61
  const qs = new URLSearchParams(location.search);
package/src/main.js CHANGED
@@ -84,6 +84,12 @@ electronApp.commandLine.appendSwitch("ignore-certificate-errors");
84
84
  // remotely (HMR-fallback when CSS / a renderer-side change needs a hard refresh).
85
85
  // Without this Electron rejects CDP WS handshakes with HTTP 403.
86
86
  electronApp.commandLine.appendSwitch("remote-allow-origins", "*");
87
+ // 关掉 WebAuthn / 通行密钥。矩阵里的账号登录一律走密码 + TOTP:
88
+ // accounts.meta.com 这类站点一进登录页就调 navigator.credentials.get(),Windows 弹出
89
+ // 「使用密钥登录 · 请将安全密钥插入 USB 端口」这个系统模态框 —— 机器上根本没有安全密钥,
90
+ // 框又是系统级的、页面脚本关不掉,自动登录就卡死在那儿(实测 2026-09-21,FB #96)。
91
+ // 关掉这个特性后 navigator.credentials / PublicKeyCredential 直接不存在,站点自动回落到密码。
92
+ electronApp.commandLine.appendSwitch("disable-features", "WebAuthentication,WebAuthenticationRemoteDesktopSupport");
87
93
  if (process.platform === "linux") {
88
94
  process.env["ELECTRON_DISABLE_SECURITY_WARNINGS"] = "true";
89
95
  // electronApp.commandLine.appendSwitch("disable-setuid-sandbox");
@@ -63,14 +63,17 @@ const appliedProxy = new Set(); // partitions whose proxy is already configured
63
63
  function ensureCellSessionProxy(idx) {
64
64
  const part = partitionFor(idx);
65
65
  if (appliedProxy.has(part)) return;
66
- appliedProxy.add(part);
67
- if (idx === 0) return; // profile 0 stays direct — managed by window-utils, don't touch
66
+ if (idx === 0) { appliedProxy.add(part); return; } // profile 0 stays direct — managed by window-utils
68
67
  try {
69
68
  const profileStore = require("../profiles/profile-store");
70
69
  let rules = "";
71
70
  try { const p = profileStore.getProfile("electron", idx); rules = profileStore.proxyRules(p && p.proxy) || ""; } catch (e) {}
72
71
  if (!rules) { try { rules = require("../config").config.proxy || ""; } catch (e) {} }
72
+ // 没拿到规则就**不要**记成「已应用」—— profile 的代理可能只是当时还没启用。
73
+ // 记了之后再启用代理,这里会被缓存挡住永远不再设置,分区就一直走直连
74
+ // (实测:新分配的 #75/#77 启用代理后出口仍是本机 IP)。
73
75
  if (!rules) return;
76
+ appliedProxy.add(part);
74
77
  session.fromPartition(part)
75
78
  .setProxy({ proxyRules: rules, proxyBypassRules: "127.0.0.1,localhost,[::1]" })
76
79
  .catch(() => {});
@@ -385,9 +388,23 @@ class PanelCells {
385
388
  }
386
389
  }
387
390
 
391
+ // 面板页里的 <webview partition="persist:sandbox-N"> 用的是同一批 partition,但它不走
392
+ // PanelCells,没人给它设代理 —— 不设就是直连,账号的真实出口 IP 直接暴露。
393
+ // 面板一打开就把所有 electron profile 的 partition 代理都绑上,webview 和 BrowserView
394
+ // 两种格子都覆盖到。ensureCellSessionProxy 自带去重,重复调用无副作用。
395
+ function ensureAllProfileProxies() {
396
+ try {
397
+ const profileStore = require("../profiles/profile-store");
398
+ for (const p of profileStore.listProfiles("electron") || []) {
399
+ const idx = Number(p && p.accountIdx);
400
+ if (Number.isInteger(idx) && idx > 0) ensureCellSessionProxy(idx);
401
+ }
402
+ } catch (e) {}
403
+ }
404
+
388
405
  function cellsForTab(manager, tab) {
389
406
  let pc = registry.get(tab.id);
390
- if (!pc) { pc = new PanelCells(manager, tab); registry.set(tab.id, pc); }
407
+ if (!pc) { pc = new PanelCells(manager, tab); registry.set(tab.id, pc); ensureAllProfileProxies(); }
391
408
  return pc;
392
409
  }
393
410
 
@@ -414,6 +431,10 @@ function installIpc(findTab) {
414
431
  ipcMain.handle("panelcells:states", (e) => { const pc = ctx(e); return pc ? pc.states() : []; });
415
432
  ipcMain.handle("panelcells:profiles", (e) => {
416
433
  if (!ctx(e)) return [];
434
+ // 面板页开局必调这个接口。借它把所有 profile 的 partition 代理绑一遍 ——
435
+ // 页面里的 <webview partition="persist:sandbox-N"> 不走 PanelCells,没人给它设代理,
436
+ // 不设就是直连,账号真实出口 IP 直接暴露。ensureCellSessionProxy 自带去重。
437
+ ensureAllProfileProxies();
417
438
  try {
418
439
  return require("../profiles/profile-store").listProfiles("electron")
419
440
  .filter((p) => Number.isInteger(Number(p.accountIdx)) && Number(p.accountIdx) > 0 && Number(p.accountIdx) !== 9)
@@ -423,6 +444,9 @@ function installIpc(findTab) {
423
444
  proxy: p.proxy && p.proxy.enabled ? String(p.proxy.url || "") : "",
424
445
  note: String(p.note || ""),
425
446
  login: { phone: String((p.telegramLogin || {}).phone || ""), codeUrl: String((p.telegramLogin || {}).codeUrl || "") },
447
+ // 客服标记来自 agg 台账(tg_accounts.is_cs),由同步写进 profile;面板据此分组,
448
+ // 不在面板本地各存各的 —— 那样换台机器看就对不上了。
449
+ cs: !!((p.telegramLogin || {}).isCs),
426
450
  telegram: telegramIdentity.telegramIdentityFromProfile(p),
427
451
  facebook: facebookIdentity.facebookIdentityFromProfile(p),
428
452
  tiktok: tiktokIdentity.tiktokIdentityFromProfile(p),
@@ -160,10 +160,18 @@ function buildTabWebPreferences(accountIdx, partition, target, opts = {}) {
160
160
  // home is system-driven (openHomeWindow), never caller-URL-reachable, so it
161
161
  // keeps its privileges without a URL check.
162
162
  if (opts.home) { wp.preload = HOMEPAGE_PRELOAD; wp.webviewTag = true; wp.allowRunningInsecureContent = true; wp.sandbox = false; }
163
- // split panel page: its cells are main-managed BrowserViews (panel-cells.js),
164
- // driven over panelAPI IPC. No webviewTag needed (that path is the file://-dev
165
- // fallback below). Sandbox stays ON the preload only uses contextBridge/ipc.
166
- else if (typeof target === "string" && (target.startsWith(PANEL_URL_BASE) || target.startsWith("cicyui://panel"))) { wp.preload = PANEL_PRELOAD; }
163
+ // split panel page: cells may be main-managed BrowserViews (panel-cells.js, over
164
+ // panelAPI IPC) OR in-page <webview>. BrowserViews are painted by the main process
165
+ // at viewport coordinates, so they do not scroll with the page and are not clipped
166
+ // by the grid's overflow with more than a screenful of cells that is unusable.
167
+ // <webview> lives in the DOM and scrolls/clips for free, so the panel needs
168
+ // webviewTag on. sandbox must go off for the tag to be usable; the preload still
169
+ // only exposes contextBridge/ipcRenderer, and the panel page is our own origin.
170
+ else if (typeof target === "string" && (target.startsWith(PANEL_URL_BASE) || target.startsWith("cicyui://panel"))) {
171
+ wp.preload = PANEL_PRELOAD;
172
+ wp.webviewTag = true;
173
+ wp.sandbox = false;
174
+ }
167
175
  // Every other profile-0 tab carries the electronRPC bridge (WEBVIEW_PRELOAD),
168
176
  // but the bridge is INERT until the page's origin is authorized: the first
169
177
  // rpc:guarded call from a non-allowlisted origin pops a consent modal
@@ -5,45 +5,57 @@
5
5
  <meta name="viewport" content="width=device-width,initial-scale=1">
6
6
  <title>IP 核对</title>
7
7
  <style>
8
- :root { color-scheme: dark; --bg:#0A0A0A; --cell:#141416; --line:rgba(255,255,255,.10); --fg:#e6e6e9; --muted:#8b8b92; --ok:#3fb950; --warn:#f0b429; --bad:#ff5c5c; --accent:#4aa3ff; font-family: Inter,"Segoe UI","PingFang SC","Microsoft YaHei",sans-serif; }
8
+ /* 和面板「设置」弹窗同一套语汇:居中卡片、同样的圆角边框、同样的 标签/值 两栏排版。
9
+ 所有 overlay(接码 / IP 核对 / 加群面板)看起来要像同一个东西,不是三张不同的网页。 */
10
+ :root { color-scheme: dark; --bg:#0A0A0A; --cell:#141416; --line:rgba(255,255,255,.10); --fg:#e6e6e9;
11
+ --muted:#8b8b92; --ok:#3fb950; --warn:#f0b429; --bad:#ff5c5c; --accent:#4aa3ff;
12
+ font-family: Inter,"Segoe UI","PingFang SC","Microsoft YaHei",sans-serif; }
9
13
  * { box-sizing:border-box; }
10
- html,body { margin:0; width:100%; height:100%; background:var(--bg); color:var(--fg); overflow:auto; }
11
- .wrap { padding:10px 12px; display:flex; flex-direction:column; gap:12px; }
12
- .card { border:1px solid var(--line); border-radius:10px; background:var(--cell); padding:10px 12px; }
13
- .card h3 { margin:0 0 8px; font-size:12px; color:var(--muted); font-weight:600; letter-spacing:.02em; }
14
- .r { display:flex; align-items:center; gap:8px; font-size:12.5px; line-height:2; }
15
- .r .k { flex:none; width:104px; color:var(--muted); }
16
- .r .v { flex:1; min-width:0; font-family:ui-monospace,monospace; word-break:break-all; }
17
- .r .v b { color:var(--fg); }
18
- .mut { color:var(--muted); }
14
+ html,body { margin:0; width:100%; height:100%; background:var(--bg); color:var(--fg); }
15
+ /* 外层浮层已经是一张卡片(带标题栏),这里只铺内容,别再套一层框 */
16
+ body { overflow:auto; padding:0 16px 16px; }
17
+ .card { width:100%; background:transparent; border:0; padding:0; }
18
+ .head { display:none; }
19
+ .lbl { display:block; font-size:12px; color:var(--muted); margin:14px 0 6px; }
20
+ .lbl:first-of-type { margin-top:0; }
21
+ .box { border:1px solid var(--line); border-radius:10px; padding:12px 14px; background:#0f0f11;
22
+ display:flex; flex-direction:column; gap:4px; }
23
+ .r { display:flex; gap:12px; font-size:12px; line-height:1.65; align-items:baseline; }
24
+ .r .k { flex:none; width:92px; color:var(--muted); }
25
+ .r .v { flex:1; min-width:0; color:var(--fg); word-break:break-all; font-family:ui-monospace,monospace; user-select:all; }
26
+ .r .v b { font-weight:600; }
27
+ .mut { color:var(--muted); font-family:inherit; }
19
28
  .ok { color:var(--ok); } .warn { color:var(--warn); } .bad { color:var(--bad); }
20
- .badge { display:inline-block; padding:1px 8px; border-radius:999px; font-size:11px; font-weight:600; }
21
- .badge.dc { background:rgba(255,92,92,.15); color:#ff9e9e; border:1px solid rgba(255,92,92,.4); }
22
- .badge.res { background:rgba(63,185,80,.15); color:#7ee787; border:1px solid rgba(63,185,80,.4); }
29
+ .badge { display:inline-block; padding:1px 9px; border-radius:999px; font-size:11px; font-weight:600;
30
+ font-family:inherit; }
31
+ .badge.dc { background:rgba(255,92,92,.16); color:#ff9e9e; }
32
+ .badge.res { background:rgba(63,185,80,.16); color:#7ee787; }
23
33
  .badge.unk { background:var(--line); color:var(--muted); }
24
34
  a { color:var(--accent); text-decoration:none; } a:hover { text-decoration:underline; }
25
35
  .links { display:flex; flex-wrap:wrap; gap:6px 14px; font-size:12px; }
26
36
  </style>
27
37
  </head>
28
38
  <body>
29
- <div class="wrap">
30
- <div class="card">
31
- <h3>出口 IP</h3>
39
+ <div class="card">
40
+ <div class="head"><span>IP 核对</span></div>
41
+
42
+ <label class="lbl">出口 IP</label>
43
+ <div class="box">
32
44
  <div class="r"><span class="k">代理探测出口</span><span class="v" id="proxyip"><span class="mut">—</span></span></div>
33
45
  <div class="r"><span class="k">浏览器真实出口</span><span class="v" id="realip"><span class="mut">读取中…</span></span></div>
34
46
  <div class="r"><span class="k">一致性</span><span class="v" id="match"><span class="mut">—</span></span></div>
35
47
  </div>
36
- <div class="card">
37
- <h3>IP 情报</h3>
48
+
49
+ <label class="lbl">IP 情报</label>
50
+ <div class="box">
38
51
  <div class="r"><span class="k">归属地</span><span class="v" id="geo"><span class="mut">—</span></span></div>
39
52
  <div class="r"><span class="k">ASN / 运营商</span><span class="v" id="asn"><span class="mut">—</span></span></div>
40
53
  <div class="r"><span class="k">类型</span><span class="v" id="type"><span class="mut">—</span></span></div>
41
54
  <div class="r"><span class="k">数据源</span><span class="v" id="src"><span class="mut">—</span></span></div>
42
55
  </div>
43
- <div class="card">
44
- <h3>完整风控 / 详情(新窗口)</h3>
45
- <div class="links" id="links"><span class="mut">拿到真实出口 IP 后生成…</span></div>
46
- </div>
56
+
57
+ <label class="lbl">完整风控 / 详情(新窗口)</label>
58
+ <div class="box"><div class="links" id="links"><span class="mut">拿到真实出口 IP 后生成…</span></div></div>
47
59
  </div>
48
60
  <script>
49
61
  const qs = new URLSearchParams(location.search);