cicy-desktop 2.1.240 → 2.1.242

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.240",
3
+ "version": "2.1.242",
4
4
  "description": "CiCy - AI-powered operating system browser",
5
5
  "main": "src/main.js",
6
6
  "bin": {
@@ -139,10 +139,10 @@
139
139
  "//optionalDependencies": "(2026-06 回调): mac/linux 改回 native cicy-code(:8008,colima 在 16G mac 上压垮内存)→ 重新内置 cicy-code-<plat> / cicy-mihomo-<plat>,localbin.fromBundle 零网络 seed(npm 仅作更新通道,带 npmmirror→npmjs 回退)。这些由 scripts/sync-runtime-deps.cjs 在 tag-push 时同步到最新版。npm 的 os/cpu 字段保证每个平台只装自己那份。Windows 仍走 docker(WSL :8009),它那份 cicy-code-windows 用不到但 bundle 着无害。",
140
140
  "optionalDependencies": {
141
141
  "electron": "41.0.3",
142
- "cicy-code-darwin-x64": "2.3.202",
143
- "cicy-code-darwin-arm64": "2.3.202",
144
- "cicy-code-linux-x64": "2.3.202",
145
- "cicy-code-linux-arm64": "2.3.202",
142
+ "cicy-code-darwin-x64": "2.3.203",
143
+ "cicy-code-darwin-arm64": "2.3.203",
144
+ "cicy-code-linux-x64": "2.3.203",
145
+ "cicy-code-linux-arm64": "2.3.203",
146
146
  "cicy-code-windows-x64": "2.3.193",
147
147
  "cicy-mihomo-darwin-x64": "1.10.4",
148
148
  "cicy-mihomo-darwin-arm64": "1.10.4",
@@ -572,8 +572,10 @@ async function pullCustomTeams() {
572
572
  for (const [k, v] of Object.entries(nodes)) { if (v?.cloud_team_id != null) byCloudId[String(v.cloud_team_id)] = k; }
573
573
  let added = 0, updated = 0;
574
574
  for (const t of list.teams) {
575
- if (t.kind !== "custom") continue;
576
- const host = String(t.host_url || t.hostUrl || "").trim();
575
+ // custom teams (remote URL) OR any team with a zero-trust gateway address:
576
+ // gateway teams are reached at <slug>.gw.cicy-ai.com (preferred over host_url).
577
+ if (t.kind !== "custom" && !t.gateway_url) continue;
578
+ const host = String(t.gateway_url || t.host_url || t.hostUrl || "").trim();
577
579
  if (!host) continue;
578
580
  const tid = t.teamId || t.id || null;
579
581
  const localId = tid != null ? byCloudId[String(tid)] : null;
@@ -1099,4 +1101,26 @@ function avatarForUrl(url) {
1099
1101
  return "";
1100
1102
  }
1101
1103
 
1102
- module.exports = { list, openTeam, reloadTeam, closeLocalWindows, addTeam, removeTeam, updateTeam, upgradeTeam, syncAllLocalTeams, setAvatar, getAvatars, avatarForUrl };
1104
+ // Remove teams tied to a cloud ACCOUNT (remote base_url pulled from the cloud
1105
+ // via pullCustomTeams, or user-added remote nodes) so switching/leaving an account
1106
+ // doesn't leak them onto the next user. LOCAL machine teams on THIS device
1107
+ // (localhost base_url) are kept — they belong to the box, not the account. The
1108
+ // next login re-pulls the new account's own teams fresh.
1109
+ async function purgeAccountTeams() {
1110
+ let removed = 0;
1111
+ try {
1112
+ await writeNodes((nodes) => {
1113
+ for (const id of Object.keys(nodes)) {
1114
+ let host = "";
1115
+ try { host = new URL(String(nodes[id]?.base_url || "")).hostname.toLowerCase(); } catch {}
1116
+ const isLocal = host === "" || host === "127.0.0.1" || host === "localhost" || host === "::1";
1117
+ if (!isLocal) { delete nodes[id]; removed++; }
1118
+ }
1119
+ return nodes;
1120
+ });
1121
+ } catch (e) { log.warn(`[local-teams] purgeAccountTeams failed: ${e.message}`); }
1122
+ if (removed) { _cache = null; _cacheUntil = 0; log.info(`[local-teams] purged ${removed} account team(s) (login/logout)`); }
1123
+ return removed;
1124
+ }
1125
+
1126
+ module.exports = { list, openTeam, reloadTeam, closeLocalWindows, addTeam, removeTeam, updateTeam, upgradeTeam, syncAllLocalTeams, purgeAccountTeams, setAvatar, getAvatars, avatarForUrl };
package/src/main.js CHANGED
@@ -998,6 +998,17 @@ electronApp.whenReady().then(async () => {
998
998
  // renderer. A completed email login is thus indistinguishable downstream.
999
999
  const onAuthResult = (payload) => {
1000
1000
  if (payload && payload.token) {
1001
+ // Account isolation: if a DIFFERENT user is signing in, drop the previous
1002
+ // account's cloud/remote teams so they don't leak onto this account. The
1003
+ // login below re-pulls this account's own teams fresh. Local machine
1004
+ // teams (localhost) are kept — they belong to the device.
1005
+ try {
1006
+ const prevUid = String((readGlobalConfig(GLOBAL_JSON)?.desktopAuth?.userId) || "");
1007
+ const nextUid = payload.userId != null ? String(payload.userId) : "";
1008
+ if (prevUid && nextUid && prevUid !== nextUid) {
1009
+ require("./backends/local-teams").purgeAccountTeams().catch(() => {});
1010
+ }
1011
+ } catch {}
1001
1012
  saveDesktopAuth(payload);
1002
1013
  // Now that we have a real owner-bound login token, report this
1003
1014
  // machine to the cloud (best-effort; safe to call repeatedly —
@@ -1067,6 +1078,8 @@ electronApp.whenReady().then(async () => {
1067
1078
  __ipcMainAuth.handle("auth:logout", () => {
1068
1079
  try {
1069
1080
  updateGlobalConfig(GLOBAL_JSON, (c) => { delete c.desktopAuth; return c; });
1081
+ // Drop this account's cloud/remote teams on logout (local machines stay).
1082
+ require("./backends/local-teams").purgeAccountTeams().catch(() => {});
1070
1083
  log.info("[auth] desktop login cleared (explicit logout)");
1071
1084
  } catch (e) { log.warn(`[auth] logout clear failed: ${e.message}`); }
1072
1085
  return { ok: true };
@@ -1123,6 +1136,19 @@ electronApp.whenReady().then(async () => {
1123
1136
  .catch((e) => log.warn(`[cloud] device-info/register (on launch) failed: ${e.message}`));
1124
1137
  } catch (e) { log.warn(`[cloud] device-info launch hook failed: ${e.message}`); }
1125
1138
 
1139
+ // Periodic device heartbeat → lets the ops backend show per-device liveness.
1140
+ // Re-POST /api/device/register every 60s (idempotent upsert by owner+deviceId;
1141
+ // cloud records last_seen on each call → "active if now - last_seen < N min").
1142
+ // Runs in MAIN, not the renderer, so it keeps beating even when the homepage
1143
+ // window is hidden/minimized. Best-effort, no-op when not logged in;
1144
+ // registerDevice reads cached deviceInfo (no network detection per beat).
1145
+ try {
1146
+ const cc = require("./cloud/cloud-client");
1147
+ const HEARTBEAT_MS = Math.max(15000, Number(process.env.CICY_DEVICE_HEARTBEAT_MS || 60000));
1148
+ const beat = setInterval(() => { try { cc.registerDevice().catch(() => {}); } catch (_) {} }, HEARTBEAT_MS);
1149
+ if (beat && typeof beat.unref === "function") beat.unref();
1150
+ } catch (e) { log.warn(`[cloud] device heartbeat setup failed: ${e.message}`); }
1151
+
1126
1152
  // Cloud↔desktop title reconcile. The homepage drives the FAST cadence by window
1127
1153
  // visibility (聚焦 ~3s / 切回立即,见 App.jsx + localTeams:syncCloud IPC). This
1128
1154
  // 30s timer is just the SLOW fallback for when no homepage window is open / it's
@@ -143,7 +143,15 @@ async function start({ port = 8008, logPath = null, emit, version = null } = {})
143
143
  };
144
144
  // --helper removed : boot cicy-code in normal mode (full tmux-based
145
145
  // multi-agent), not the single headless 团队助手.
146
- const child = spawn(exe, ["--desktop"], { stdio, detached: true, windowsHide: true, env });
146
+ const args = ["--desktop"];
147
+ // Host role: if a zero-trust gateway is provisioned (~/cicy-ai/db/gateway.json
148
+ // with url+token), boot with --gateway so this machine dials OUT and becomes
149
+ // reachable through the gateway (no inbound port). Absent → plain local boot.
150
+ try {
151
+ const gwPath = require("path").join(require("os").homedir(), "cicy-ai", "db", "gateway.json");
152
+ if (require("fs").existsSync(gwPath)) args.push("--gateway");
153
+ } catch { /* best-effort */ }
154
+ const child = spawn(exe, args, { stdio, detached: true, windowsHide: true, env });
147
155
  child.unref();
148
156
  try { fs.writeFileSync(PID_FILE, String(child.pid)); } catch {}
149
157
  console.log(`[native-sidecar] spawned ${exe} --desktop pid=${child.pid} port=${port} log=${logPath || "(none)"}`);