cicy-desktop 2.1.35 → 2.1.37

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.35",
3
+ "version": "2.1.37",
4
4
  "description": "CiCy - AI-powered operating system browser",
5
5
  "main": "src/main.js",
6
6
  "bin": {
@@ -59,7 +59,11 @@ function writePrivateChromeConfig(nextConfig) {
59
59
  if (!fs.existsSync(dir)) {
60
60
  fs.mkdirSync(dir, { recursive: true });
61
61
  }
62
- fs.writeFileSync(PRIVATE_CHROME_JSON, JSON.stringify(nextConfig || {}, null, 2));
62
+ // chrome.json now holds per-account passwords + TOTP secrets — keep it
63
+ // owner-only (0600). mode on writeFileSync only applies on create, so chmod
64
+ // explicitly to also tighten a pre-existing world/group-readable file.
65
+ fs.writeFileSync(PRIVATE_CHROME_JSON, JSON.stringify(nextConfig || {}, null, 2), { mode: 0o600 });
66
+ try { fs.chmodSync(PRIVATE_CHROME_JSON, 0o600); } catch {}
63
67
  }
64
68
 
65
69
  function listPrivateChromeEntries({ includeHidden = false } = {}) {
@@ -97,10 +101,19 @@ function normalizePrivateChromeEntry(profileKey, accountIdx, entry) {
97
101
  const port = typeof safeEntry.port === "number" ? safeEntry.port : null;
98
102
  const proxyUrl = normalizePrivateProxy(safeEntry.proxy);
99
103
  const platform = safeEntry.platform && typeof safeEntry.platform === "object" ? safeEntry.platform : {};
100
- // Free-text label + account tags (e.g. ["github","gmail"]) for
101
- // `list profile with <svc>` written via chrome_set_profile_meta.
104
+ // Free-text note + a service→credentials map for `list profile with <svc>` —
105
+ // written via chrome_set_profile_meta. Each value is {account,password,totp}.
106
+ // Legacy normalization: array → {}; a bare string value → {account:<string>}.
102
107
  const note = typeof safeEntry.note === "string" ? safeEntry.note : "";
103
- const accounts = Array.isArray(safeEntry.accounts) ? safeEntry.accounts : [];
108
+ const rawAccounts =
109
+ safeEntry.accounts && typeof safeEntry.accounts === "object" && !Array.isArray(safeEntry.accounts)
110
+ ? safeEntry.accounts
111
+ : {};
112
+ const accounts = {};
113
+ for (const [svc, val] of Object.entries(rawAccounts)) {
114
+ if (typeof val === "string") accounts[svc] = { account: val };
115
+ else if (val && typeof val === "object" && !Array.isArray(val)) accounts[svc] = val;
116
+ }
104
117
 
105
118
  return {
106
119
  profileKey,
@@ -662,11 +675,24 @@ function registerChromeTools(registerTool) {
662
675
 
663
676
  registerTool(
664
677
  "chrome_set_profile_meta",
665
- "设置 ~/cicy-ai/db/chrome.json 中指定 accountIdx 的 note(备注)/ accounts(账号标签,用于 list profile with <svc>)",
678
+ "设置 ~/cicy-ai/db/chrome.json 中指定 accountIdx 的 note(备注)/ accounts(服务→{account,password,totp} map,用于 list profile with <svc> + 自动 2FA)",
666
679
  z.object({
667
680
  accountIdx: z.number().describe("账户索引"),
668
681
  note: z.string().optional().describe("自由文本备注;省略则不动"),
669
- accounts: z.array(z.string()).optional().describe("账号标签数组,如 ['github','gmail'];去重小写;省略则不动"),
682
+ accounts: z
683
+ .record(
684
+ z
685
+ .object({
686
+ account: z.string().optional(),
687
+ password: z.string().optional(),
688
+ totp: z.string().optional(),
689
+ })
690
+ .partial()
691
+ )
692
+ .optional()
693
+ .describe(
694
+ "服务→{account,password,totp} map,如 {github:{account:'octocat',password:'..',totp:'BASE32'}};字段级合并,字段空值删该字段,svc 清空则删该服务;省略则整体不动"
695
+ ),
670
696
  }),
671
697
  async ({ accountIdx, note, accounts } = {}) => {
672
698
  const data = readPrivateChromeConfig();
@@ -674,13 +700,33 @@ function registerChromeTools(registerTool) {
674
700
  if (!data[key]) {
675
701
  return toToolResult({ error: `Missing chrome.json entry: ${key}` }, { isError: true });
676
702
  }
677
- data[key] = {
678
- ...data[key],
679
- ...(note !== undefined ? { note: String(note) } : {}),
680
- ...(accounts !== undefined
681
- ? { accounts: [...new Set(accounts.map((a) => String(a).trim().toLowerCase()).filter(Boolean))] }
682
- : {}),
683
- };
703
+ const patch = { ...data[key], ...(note !== undefined ? { note: String(note) } : {}) };
704
+ if (accounts !== undefined) {
705
+ // Field-level merge into the existing service→credentials map.
706
+ // Empty field value deletes that field; a service with no fields left
707
+ // is removed entirely. Legacy string value normalizes to {account}.
708
+ const base =
709
+ data[key].accounts && typeof data[key].accounts === "object" && !Array.isArray(data[key].accounts)
710
+ ? data[key].accounts
711
+ : {};
712
+ const next = { ...base };
713
+ for (const [rawSvc, fieldPatch] of Object.entries(accounts)) {
714
+ const svc = String(rawSvc).trim().toLowerCase();
715
+ if (!svc) continue;
716
+ let cur = next[svc];
717
+ if (typeof cur === "string") cur = { account: cur }; // 1.3.0 compat
718
+ cur = cur && typeof cur === "object" && !Array.isArray(cur) ? { ...cur } : {};
719
+ for (const [f, v] of Object.entries(fieldPatch || {})) {
720
+ const val = String(v ?? "").trim();
721
+ if (val === "") delete cur[f];
722
+ else cur[f] = val;
723
+ }
724
+ if (Object.keys(cur).length === 0) delete next[svc];
725
+ else next[svc] = cur;
726
+ }
727
+ patch.accounts = next;
728
+ }
729
+ data[key] = patch;
684
730
  writePrivateChromeConfig(data);
685
731
  return toToolResult({ success: true, profileKey: key, privateConfig: data[key] });
686
732
  },