claude-codex-wechat 0.1.23 → 0.1.24

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.
Files changed (2) hide show
  1. package/dist/server/cli.js +161 -108
  2. package/package.json +1 -1
@@ -3,7 +3,7 @@ import { createRequire as __cjsCreateRequire } from 'node:module'; const require
3
3
 
4
4
  // src/cli.ts
5
5
  import { existsSync as existsSync10, readFileSync as readFileSync9 } from "node:fs";
6
- import { dirname as dirname13, join as join13 } from "node:path";
6
+ import { dirname as dirname9, join as join14 } from "node:path";
7
7
  import { fileURLToPath as fileURLToPath2 } from "node:url";
8
8
 
9
9
  // src/daemon/bootstrap.ts
@@ -12,7 +12,7 @@ import { networkInterfaces } from "node:os";
12
12
  // src/daemon/server.ts
13
13
  import { mkdtempSync } from "node:fs";
14
14
  import { tmpdir as tmpdir2 } from "node:os";
15
- import { dirname as dirname11, join as join10 } from "node:path";
15
+ import { dirname as dirname7, join as join11 } from "node:path";
16
16
  import Fastify from "fastify";
17
17
 
18
18
  // src/channels/platforms.ts
@@ -151,10 +151,76 @@ function normalizeWechatConfig(raw, env) {
151
151
  }
152
152
 
153
153
  // src/daemon/configPersistence.ts
154
- import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
154
+ import { readFile, rm } from "node:fs/promises";
155
+ import { randomBytes as randomBytes2 } from "node:crypto";
156
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
157
+
158
+ // src/shared/atomicFile.ts
159
+ import { mkdirSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
160
+ import { mkdir, rename, unlink, writeFile } from "node:fs/promises";
155
161
  import { randomBytes } from "node:crypto";
156
- import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "node:fs";
157
- import { dirname } from "node:path";
162
+ import { basename, dirname, join as join2 } from "node:path";
163
+ function tempPathFor(path) {
164
+ return join2(dirname(path), `.${basename(path)}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`);
165
+ }
166
+ var RENAME_RETRY_CODES = /* @__PURE__ */ new Set(["EPERM", "EACCES", "EBUSY"]);
167
+ var MAX_RENAME_RETRIES = 10;
168
+ function isRetriableRenameError(error) {
169
+ if (process.platform !== "win32") return false;
170
+ const code = error?.code ?? "";
171
+ return RENAME_RETRY_CODES.has(code);
172
+ }
173
+ function sleepSync(ms) {
174
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
175
+ }
176
+ function sleep(ms) {
177
+ return new Promise((resolve2) => {
178
+ setTimeout(resolve2, ms);
179
+ });
180
+ }
181
+ function writeFileAtomicSync(path, data) {
182
+ const tmp = tempPathFor(path);
183
+ mkdirSync(dirname(path), { recursive: true });
184
+ try {
185
+ writeFileSync(tmp, data, "utf8");
186
+ for (let attempt = 0; ; attempt++) {
187
+ try {
188
+ renameSync(tmp, path);
189
+ return;
190
+ } catch (error) {
191
+ if (attempt >= MAX_RENAME_RETRIES || !isRetriableRenameError(error)) throw error;
192
+ sleepSync((attempt + 1) * 10);
193
+ }
194
+ }
195
+ } catch (error) {
196
+ try {
197
+ unlinkSync(tmp);
198
+ } catch {
199
+ }
200
+ throw error;
201
+ }
202
+ }
203
+ async function writeFileAtomic(path, data) {
204
+ const tmp = tempPathFor(path);
205
+ await mkdir(dirname(path), { recursive: true });
206
+ try {
207
+ await writeFile(tmp, data, "utf8");
208
+ for (let attempt = 0; ; attempt++) {
209
+ try {
210
+ await rename(tmp, path);
211
+ return;
212
+ } catch (error) {
213
+ if (attempt >= MAX_RENAME_RETRIES || !isRetriableRenameError(error)) throw error;
214
+ await sleep((attempt + 1) * 10);
215
+ }
216
+ }
217
+ } catch (error) {
218
+ await unlink(tmp).catch(() => void 0);
219
+ throw error;
220
+ }
221
+ }
222
+
223
+ // src/daemon/configPersistence.ts
158
224
  async function persistWechatCredentialsToConfigFile(input) {
159
225
  const currentConfig = await readConfigFile(input.configPath);
160
226
  const nextConfig = {
@@ -167,9 +233,8 @@ async function persistWechatCredentialsToConfigFile(input) {
167
233
  accountId: input.accountId
168
234
  }
169
235
  };
170
- await mkdir(dirname(input.configPath), { recursive: true });
171
- await writeFile(input.configPath, `${JSON.stringify(nextConfig, null, 2)}
172
- `, "utf8");
236
+ await writeFileAtomic(input.configPath, `${JSON.stringify(nextConfig, null, 2)}
237
+ `);
173
238
  }
174
239
  async function deleteConfigFile(configPath) {
175
240
  try {
@@ -190,9 +255,8 @@ function persistUpdateStatusToConfigFile(input) {
190
255
  ...input.status.lastCheckedAt !== void 0 ? { lastCheckedAt: input.status.lastCheckedAt } : {}
191
256
  }
192
257
  };
193
- mkdirSync(dirname(input.configPath), { recursive: true });
194
- writeFileSync(input.configPath, `${JSON.stringify(nextConfig, null, 2)}
195
- `, "utf8");
258
+ writeFileAtomicSync(input.configPath, `${JSON.stringify(nextConfig, null, 2)}
259
+ `);
196
260
  }
197
261
  async function persistBridgeDefaultsToConfigFile(input) {
198
262
  const currentConfig = await readConfigFile(input.configPath);
@@ -215,9 +279,8 @@ async function persistBridgeDefaultsToConfigFile(input) {
215
279
  }
216
280
  } : {}
217
281
  };
218
- await mkdir(dirname(input.configPath), { recursive: true });
219
- await writeFile(input.configPath, `${JSON.stringify(nextConfig, null, 2)}
220
- `, "utf8");
282
+ await writeFileAtomic(input.configPath, `${JSON.stringify(nextConfig, null, 2)}
283
+ `);
221
284
  }
222
285
  function ensureRelayAuthTokenSync(input) {
223
286
  const currentConfig = readConfigFileSync(input.configPath);
@@ -225,7 +288,7 @@ function ensureRelayAuthTokenSync(input) {
225
288
  const currentRelay = isRecord(currentTunnel?.relay) ? currentTunnel.relay : void 0;
226
289
  const existing = typeof currentRelay?.authToken === "string" && currentRelay.authToken.trim() ? currentRelay.authToken.trim() : "";
227
290
  if (existing) return existing;
228
- const authToken = `clrt_${randomBytes(12).toString("hex")}`;
291
+ const authToken = `clrt_${randomBytes2(12).toString("hex")}`;
229
292
  const nextConfig = {
230
293
  ...currentConfig,
231
294
  tunnel: {
@@ -236,9 +299,8 @@ function ensureRelayAuthTokenSync(input) {
236
299
  }
237
300
  }
238
301
  };
239
- mkdirSync(dirname(input.configPath), { recursive: true });
240
- writeFileSync(input.configPath, `${JSON.stringify(nextConfig, null, 2)}
241
- `, "utf8");
302
+ writeFileAtomicSync(input.configPath, `${JSON.stringify(nextConfig, null, 2)}
303
+ `);
242
304
  return authToken;
243
305
  }
244
306
  async function persistProviderCommandsToConfigFile(input) {
@@ -248,9 +310,8 @@ async function persistProviderCommandsToConfigFile(input) {
248
310
  ...currentConfig,
249
311
  providers: normalizedProviders
250
312
  };
251
- await mkdir(dirname(input.configPath), { recursive: true });
252
- await writeFile(input.configPath, `${JSON.stringify(nextConfig, null, 2)}
253
- `, "utf8");
313
+ await writeFileAtomic(input.configPath, `${JSON.stringify(nextConfig, null, 2)}
314
+ `);
254
315
  }
255
316
  async function readConfigFile(path) {
256
317
  try {
@@ -290,8 +351,7 @@ function normalizeProvidersForPersistence(providers) {
290
351
  }
291
352
 
292
353
  // src/channels/weixin-direct/weixinStateStore.ts
293
- import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
294
- import { dirname as dirname2 } from "node:path";
354
+ import { existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
295
355
  var PUSH_QUOTA_LIMIT = 10;
296
356
  var PUSH_WINDOW_MS = 24 * 60 * 60 * 1e3;
297
357
  var FileWeixinStateStore = class {
@@ -397,16 +457,15 @@ var FileWeixinStateStore = class {
397
457
  ...state,
398
458
  bridge: { ...state.bridge ?? {}, weixinChannel: channel }
399
459
  };
400
- mkdirSync2(dirname2(this.configPath), { recursive: true });
401
- writeFileSync2(this.configPath, `${JSON.stringify(next, null, 2)}
402
- `, "utf8");
460
+ writeFileAtomicSync(this.configPath, `${JSON.stringify(next, null, 2)}
461
+ `);
403
462
  }
404
463
  };
405
464
 
406
465
  // src/providers/claude-code/nativeSessions.ts
407
466
  import { appendFile, mkdir as mkdir2, readdir, readFile as readFile2, stat, writeFile as writeFile2 } from "node:fs/promises";
408
467
  import { homedir as homedir2 } from "node:os";
409
- import { basename, dirname as dirname3, join as join2 } from "node:path";
468
+ import { basename as basename2, dirname as dirname2, join as join3 } from "node:path";
410
469
 
411
470
  // src/session/sessionBridgeTag.ts
412
471
  function buildSessionBridgeName(input) {
@@ -433,27 +492,27 @@ function parseSessionBridgeName(value) {
433
492
 
434
493
  // src/providers/claude-code/nativeSessions.ts
435
494
  function resolveClaudeConfigDir(env = process.env) {
436
- return join2(env.HOME || homedir2(), ".claude");
495
+ return join3(env.HOME || homedir2(), ".claude");
437
496
  }
438
497
  async function listRecoverableClaudeSessions(env = process.env) {
439
- const projectsDir = join2(resolveClaudeConfigDir(env), "projects");
498
+ const projectsDir = join3(resolveClaudeConfigDir(env), "projects");
440
499
  const historyIndex = await readClaudeHistoryIndex(env);
441
500
  const candidates = [];
442
501
  try {
443
502
  const projectEntries = await readdir(projectsDir, { withFileTypes: true });
444
503
  for (const entry of projectEntries) {
445
504
  if (!entry.isDirectory() || entry.isSymbolicLink()) continue;
446
- const projectDir = join2(projectsDir, String(entry.name));
505
+ const projectDir = join3(projectsDir, String(entry.name));
447
506
  const sessionEntries = await readdir(projectDir, { withFileTypes: true }).catch(() => []);
448
507
  for (const sessionEntry of sessionEntries) {
449
508
  if (!sessionEntry.isFile() || sessionEntry.isSymbolicLink()) continue;
450
509
  const fileName = String(sessionEntry.name);
451
510
  if (!fileName.endsWith(".jsonl")) continue;
452
- const filePath = join2(projectDir, fileName);
511
+ const filePath = join3(projectDir, fileName);
453
512
  const metadata = await stat(filePath).catch(() => null);
454
513
  const parsedMeta = await readClaudeSessionMetadata(filePath).catch(() => null);
455
- const historyMeta = historyIndex.get(basename(fileName, ".jsonl"));
456
- const sessionId = basename(fileName, ".jsonl");
514
+ const historyMeta = historyIndex.get(basename2(fileName, ".jsonl"));
515
+ const sessionId = basename2(fileName, ".jsonl");
457
516
  const bridgeTag = historyMeta?.bridgeTag ?? parseSessionBridgeName(parsedMeta?.sessionName);
458
517
  const cwd = parsedMeta?.cwd ?? historyMeta?.project;
459
518
  candidates.push({
@@ -475,12 +534,12 @@ async function listRecoverableClaudeSessions(env = process.env) {
475
534
  async function findRecoverableClaudeSessionPath(sessionId, env = process.env) {
476
535
  const trimmed = sessionId.trim();
477
536
  if (!trimmed || trimmed.includes("/") || trimmed.includes("\\")) return null;
478
- const projectsDir = join2(resolveClaudeConfigDir(env), "projects");
537
+ const projectsDir = join3(resolveClaudeConfigDir(env), "projects");
479
538
  try {
480
539
  const projectEntries = await readdir(projectsDir, { withFileTypes: true });
481
540
  for (const entry of projectEntries) {
482
541
  if (!entry.isDirectory() || entry.isSymbolicLink()) continue;
483
- const sessionPath = join2(projectsDir, String(entry.name), `${trimmed}.jsonl`);
542
+ const sessionPath = join3(projectsDir, String(entry.name), `${trimmed}.jsonl`);
484
543
  try {
485
544
  const metadata = await stat(sessionPath);
486
545
  if (metadata.isFile()) return sessionPath;
@@ -528,7 +587,7 @@ async function hasClaudeSessionBridgeMetadata(input) {
528
587
  }
529
588
  async function hasClaudeHistoryDisplay(input) {
530
589
  const env = input.env ?? process.env;
531
- const historyPath = join2(resolveClaudeConfigDir(env), "history.jsonl");
590
+ const historyPath = join3(resolveClaudeConfigDir(env), "history.jsonl");
532
591
  try {
533
592
  const content = await readFile2(historyPath, "utf8");
534
593
  for (const line of content.split(/\r?\n/)) {
@@ -583,7 +642,7 @@ async function readClaudeSessionMetadata(filePath) {
583
642
  return { aiTitle, lastPrompt, sessionName, cwd };
584
643
  }
585
644
  async function readClaudeHistoryIndex(env) {
586
- const historyPath = join2(resolveClaudeConfigDir(env), "history.jsonl");
645
+ const historyPath = join3(resolveClaudeConfigDir(env), "history.jsonl");
587
646
  const index = /* @__PURE__ */ new Map();
588
647
  try {
589
648
  const content = await readFile2(historyPath, "utf8");
@@ -608,7 +667,7 @@ async function readClaudeHistoryIndex(env) {
608
667
  return index;
609
668
  }
610
669
  async function upsertClaudeHistoryDisplay(input) {
611
- const historyPath = join2(resolveClaudeConfigDir(input.env), "history.jsonl");
670
+ const historyPath = join3(resolveClaudeConfigDir(input.env), "history.jsonl");
612
671
  const lines = [];
613
672
  let replaced = false;
614
673
  let changed = false;
@@ -647,7 +706,7 @@ async function upsertClaudeHistoryDisplay(input) {
647
706
  }));
648
707
  }
649
708
  if (!changed) return false;
650
- await mkdir2(dirname3(historyPath), { recursive: true });
709
+ await mkdir2(dirname2(historyPath), { recursive: true });
651
710
  await writeFile2(historyPath, `${lines.join("\n")}
652
711
  `, "utf8");
653
712
  return true;
@@ -679,7 +738,7 @@ async function normalizeClaudeSessionFileForResume(sessionPath) {
679
738
  nextLines.push(line);
680
739
  }
681
740
  if (sawSdkCliEntrypoint && !hasPermissionMode) {
682
- const sessionId = basename(sessionPath, ".jsonl");
741
+ const sessionId = basename2(sessionPath, ".jsonl");
683
742
  nextLines.unshift(JSON.stringify({
684
743
  type: "permission-mode",
685
744
  permissionMode: "bypassPermissions",
@@ -698,16 +757,16 @@ import Database from "better-sqlite3";
698
757
  import { readdir as readdir2, readFile as readFile3, stat as stat2 } from "node:fs/promises";
699
758
  import { existsSync as existsSync4 } from "node:fs";
700
759
  import { homedir as homedir3 } from "node:os";
701
- import { basename as basename2, join as join3 } from "node:path";
760
+ import { basename as basename3, join as join4 } from "node:path";
702
761
  function resolveCodexSessionsRoot(env = process.env) {
703
- return join3(env.CODEX_HOME || join3(env.HOME || homedir3(), ".codex"), "sessions");
762
+ return join4(env.CODEX_HOME || join4(env.HOME || homedir3(), ".codex"), "sessions");
704
763
  }
705
764
  function isMatchingCodexRolloutFile(name) {
706
765
  return name.startsWith("rollout-") && name.endsWith(".jsonl");
707
766
  }
708
767
  function extractCodexSessionId(name) {
709
768
  if (!isMatchingCodexRolloutFile(name)) return null;
710
- const withoutExt = basename2(name, ".jsonl");
769
+ const withoutExt = basename3(name, ".jsonl");
711
770
  const match = withoutExt.match(/^rollout-\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}-(.+)$/);
712
771
  if (!match?.[1]) return null;
713
772
  return match[1];
@@ -723,7 +782,7 @@ async function listRecoverableCodexSessions(env = process.env) {
723
782
  for (const entry of entries) {
724
783
  if (entry.isSymbolicLink()) continue;
725
784
  const name = String(entry.name);
726
- const path = join3(currentDir, name);
785
+ const path = join4(currentDir, name);
727
786
  if (entry.isDirectory()) {
728
787
  await walk(path, depth + 1);
729
788
  continue;
@@ -759,7 +818,7 @@ async function findRecoverableCodexSessionPath(sessionId, env = process.env) {
759
818
  for (const entry of entries) {
760
819
  if (entry.isSymbolicLink()) continue;
761
820
  const name = String(entry.name);
762
- const path = join3(currentDir, name);
821
+ const path = join4(currentDir, name);
763
822
  if (entry.isDirectory()) {
764
823
  const found = await walk(path, depth + 1);
765
824
  if (found) return found;
@@ -773,7 +832,7 @@ async function findRecoverableCodexSessionPath(sessionId, env = process.env) {
773
832
  return await walk(root, 0);
774
833
  }
775
834
  async function readCodexSessionIndex(env) {
776
- const indexPath = join3(env.CODEX_HOME || join3(env.HOME || homedir3(), ".codex"), "session_index.jsonl");
835
+ const indexPath = join4(env.CODEX_HOME || join4(env.HOME || homedir3(), ".codex"), "session_index.jsonl");
777
836
  const index = /* @__PURE__ */ new Map();
778
837
  try {
779
838
  const content = await readFile3(indexPath, "utf8");
@@ -796,7 +855,7 @@ async function readCodexSessionIndex(env) {
796
855
  return index;
797
856
  }
798
857
  function readCodexThreadMetadata(env) {
799
- const dbPath = join3(env.CODEX_HOME || join3(env.HOME || homedir3(), ".codex"), "state_5.sqlite");
858
+ const dbPath = join4(env.CODEX_HOME || join4(env.HOME || homedir3(), ".codex"), "state_5.sqlite");
800
859
  const metadata = /* @__PURE__ */ new Map();
801
860
  if (!existsSync4(dbPath)) return metadata;
802
861
  let db = null;
@@ -857,9 +916,9 @@ import { nanoid } from "nanoid";
857
916
  // src/providers/codex/sessionIndex.ts
858
917
  import { mkdir as mkdir3, readFile as readFile4, writeFile as writeFile3 } from "node:fs/promises";
859
918
  import { homedir as homedir4 } from "node:os";
860
- import { dirname as dirname4, join as join4 } from "node:path";
919
+ import { dirname as dirname3, join as join5 } from "node:path";
861
920
  function resolveCodexIndexPath(env = process.env) {
862
- return join4(env.CODEX_HOME || join4(env.HOME || homedir4(), ".codex"), "session_index.jsonl");
921
+ return join5(env.CODEX_HOME || join5(env.HOME || homedir4(), ".codex"), "session_index.jsonl");
863
922
  }
864
923
  async function upsertCodexSessionIndexEntry(input, env = process.env) {
865
924
  const indexPath = resolveCodexIndexPath(env);
@@ -895,7 +954,7 @@ async function upsertCodexSessionIndexEntry(input, env = process.env) {
895
954
  updated_at: input.updatedAt ?? (/* @__PURE__ */ new Date()).toISOString()
896
955
  }));
897
956
  }
898
- await mkdir3(dirname4(indexPath), { recursive: true });
957
+ await mkdir3(dirname3(indexPath), { recursive: true });
899
958
  await writeFile3(indexPath, `${lines.join("\n")}
900
959
  `, "utf8");
901
960
  }
@@ -905,9 +964,9 @@ import Database2 from "better-sqlite3";
905
964
  import { existsSync as existsSync5 } from "node:fs";
906
965
  import { readFile as readFile5, writeFile as writeFile4 } from "node:fs/promises";
907
966
  import { homedir as homedir5 } from "node:os";
908
- import { join as join5 } from "node:path";
967
+ import { join as join6 } from "node:path";
909
968
  function resolveCodexStateDbPath(env = process.env) {
910
- return join5(env.CODEX_HOME || join5(env.HOME || homedir5(), ".codex"), "state_5.sqlite");
969
+ return join6(env.CODEX_HOME || join6(env.HOME || homedir5(), ".codex"), "state_5.sqlite");
911
970
  }
912
971
  async function syncCodexThreadForResume(input) {
913
972
  const env = input.env ?? process.env;
@@ -1570,7 +1629,7 @@ function normalizeSettings(input, defaultWorkspace) {
1570
1629
  // src/admin/fsBrowseRoutes.ts
1571
1630
  import { readdir as readdir3 } from "node:fs/promises";
1572
1631
  import { homedir as homedir7 } from "node:os";
1573
- import { dirname as dirname5, join as join6, resolve } from "node:path";
1632
+ import { dirname as dirname4, join as join7, resolve } from "node:path";
1574
1633
 
1575
1634
  // src/shared/expandTilde.ts
1576
1635
  import { homedir as homedir6 } from "node:os";
@@ -1596,8 +1655,8 @@ function registerFsBrowseRoutes(input) {
1596
1655
  reply.code(400);
1597
1656
  return { error: "cannot_read_directory", path: target };
1598
1657
  }
1599
- const entries = dirents.filter((dirent) => dirent.isDirectory()).map((dirent) => ({ name: dirent.name, path: join6(target, dirent.name), isDirectory: true })).filter((entry) => !entry.name.startsWith(".") || isOnKeepChain(entry.path)).sort((a, b) => a.name.localeCompare(b.name));
1600
- const parent = dirname5(target);
1658
+ const entries = dirents.filter((dirent) => dirent.isDirectory()).map((dirent) => ({ name: dirent.name, path: join7(target, dirent.name), isDirectory: true })).filter((entry) => !entry.name.startsWith(".") || isOnKeepChain(entry.path)).sort((a, b) => a.name.localeCompare(b.name));
1659
+ const parent = dirname4(target);
1601
1660
  const isRoot = parent === target;
1602
1661
  const listing = {
1603
1662
  path: target,
@@ -1778,7 +1837,7 @@ var WeixinDirectApiClient = class {
1778
1837
 
1779
1838
  // src/channels/weixin-direct/adapter.ts
1780
1839
  import { setTimeout as delay2 } from "node:timers/promises";
1781
- import { join as join7 } from "node:path";
1840
+ import { join as join8 } from "node:path";
1782
1841
 
1783
1842
  // src/channels/weixin-direct/typingController.ts
1784
1843
  var CONFIG_CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
@@ -2043,7 +2102,7 @@ var WeixinDirectAdapter = class _WeixinDirectAdapter {
2043
2102
  continue;
2044
2103
  }
2045
2104
  const { ext, mimeType } = mediaExtAndMime(meta);
2046
- const destPath = join7(mediaDir, `${idPrefix}_${i}${ext}`);
2105
+ const destPath = join8(mediaDir, `${idPrefix}_${i}${ext}`);
2047
2106
  const maxBytes = meta.kind === "video" ? VIDEO_MAX_BYTES : void 0;
2048
2107
  const result = await downloader.download(meta.media ?? {}, { destPath, aeskeyOverride: meta.aeskey, maxBytes });
2049
2108
  if (result.ok) {
@@ -2103,8 +2162,8 @@ function chunkText(text, limit) {
2103
2162
  }
2104
2163
 
2105
2164
  // src/channels/weixin-direct/mediaDownloader.ts
2106
- import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "node:fs";
2107
- import { dirname as dirname6 } from "node:path";
2165
+ import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "node:fs";
2166
+ import { dirname as dirname5 } from "node:path";
2108
2167
 
2109
2168
  // src/channels/weixin-direct/mediaCrypto.ts
2110
2169
  import { createDecipheriv } from "node:crypto";
@@ -2167,8 +2226,8 @@ var WeixinMediaDownloader = class {
2167
2226
  } catch {
2168
2227
  return { ok: false, reason: "decrypt_failed" };
2169
2228
  }
2170
- mkdirSync3(dirname6(input.destPath), { recursive: true });
2171
- writeFileSync3(input.destPath, plaintext);
2229
+ mkdirSync2(dirname5(input.destPath), { recursive: true });
2230
+ writeFileSync2(input.destPath, plaintext);
2172
2231
  return { ok: true, localPath: input.destPath, bytes: plaintext.length };
2173
2232
  }
2174
2233
  };
@@ -2354,7 +2413,7 @@ import { randomUUID as randomUUID2 } from "node:crypto";
2354
2413
 
2355
2414
  // src/shared/platform.ts
2356
2415
  import { access, constants } from "node:fs/promises";
2357
- import { delimiter, join as join8 } from "node:path";
2416
+ import { delimiter, join as join9 } from "node:path";
2358
2417
  import { tmpdir } from "node:os";
2359
2418
  import { spawn } from "node:child_process";
2360
2419
  var isWindows = process.platform === "win32";
@@ -2382,7 +2441,7 @@ async function findExecutable(command) {
2382
2441
  const dirs = (process.env.PATH ?? "").split(delimiter).filter(Boolean);
2383
2442
  for (const dir of dirs) {
2384
2443
  for (const ext of extensions) {
2385
- const candidate = join8(dir, command + ext);
2444
+ const candidate = join9(dir, command + ext);
2386
2445
  if (await isExecutableFile(candidate)) return candidate;
2387
2446
  }
2388
2447
  }
@@ -2405,13 +2464,13 @@ function terminateChild(child, signal = "SIGTERM") {
2405
2464
  }
2406
2465
  }
2407
2466
  function defaultWorkspaceDir() {
2408
- return join8(tmpdir(), "project");
2467
+ return join9(tmpdir(), "project");
2409
2468
  }
2410
2469
  function useShellForCli() {
2411
2470
  return isWindows;
2412
2471
  }
2413
2472
  function statePath(filename) {
2414
- return join8(tmpdir(), filename);
2473
+ return join9(tmpdir(), filename);
2415
2474
  }
2416
2475
 
2417
2476
  // src/providers/claude-code/assistantContent.ts
@@ -3399,8 +3458,7 @@ function buildBridgeCommandHelpMarkdown(input) {
3399
3458
  }
3400
3459
 
3401
3460
  // src/session/currentConversationStore.ts
3402
- import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "node:fs";
3403
- import { dirname as dirname7 } from "node:path";
3461
+ import { existsSync as existsSync6, readFileSync as readFileSync4 } from "node:fs";
3404
3462
  import { nanoid as nanoid2 } from "nanoid";
3405
3463
  var CurrentConversationStore = class {
3406
3464
  constructor(configPath, defaults) {
@@ -3482,9 +3540,8 @@ var CurrentConversationStore = class {
3482
3540
  this.writeState(state);
3483
3541
  }
3484
3542
  writeState(state) {
3485
- mkdirSync4(dirname7(this.configPath), { recursive: true });
3486
- writeFileSync4(this.configPath, `${JSON.stringify(state, null, 2)}
3487
- `, "utf8");
3543
+ writeFileAtomicSync(this.configPath, `${JSON.stringify(state, null, 2)}
3544
+ `);
3488
3545
  }
3489
3546
  };
3490
3547
 
@@ -4017,8 +4074,7 @@ function composeInboundText(content) {
4017
4074
  }
4018
4075
 
4019
4076
  // src/storage/lastProviderSessionStore.ts
4020
- import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "node:fs";
4021
- import { dirname as dirname8 } from "node:path";
4077
+ import { existsSync as existsSync7, readFileSync as readFileSync5 } from "node:fs";
4022
4078
  var LastProviderSessionStore = class {
4023
4079
  constructor(configPath) {
4024
4080
  this.configPath = configPath;
@@ -4049,15 +4105,13 @@ var LastProviderSessionStore = class {
4049
4105
  return raw && typeof raw === "object" ? raw : {};
4050
4106
  }
4051
4107
  writeState(state) {
4052
- mkdirSync5(dirname8(this.configPath), { recursive: true });
4053
- writeFileSync5(this.configPath, `${JSON.stringify(state, null, 2)}
4054
- `, "utf8");
4108
+ writeFileAtomicSync(this.configPath, `${JSON.stringify(state, null, 2)}
4109
+ `);
4055
4110
  }
4056
4111
  };
4057
4112
 
4058
4113
  // src/storage/runtimeUserStore.ts
4059
- import { mkdirSync as mkdirSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync6, existsSync as existsSync8 } from "node:fs";
4060
- import { dirname as dirname9 } from "node:path";
4114
+ import { readFileSync as readFileSync6, existsSync as existsSync8 } from "node:fs";
4061
4115
  import { nanoid as nanoid3 } from "nanoid";
4062
4116
  var RuntimeUserStore = class {
4063
4117
  constructor(configPath) {
@@ -4122,9 +4176,8 @@ var RuntimeUserStore = class {
4122
4176
  return raw && typeof raw === "object" ? raw : {};
4123
4177
  }
4124
4178
  writeState(state) {
4125
- mkdirSync6(dirname9(this.configPath), { recursive: true });
4126
- writeFileSync6(this.configPath, `${JSON.stringify(state, null, 2)}
4127
- `, "utf8");
4179
+ writeFileAtomicSync(this.configPath, `${JSON.stringify(state, null, 2)}
4180
+ `);
4128
4181
  }
4129
4182
  };
4130
4183
 
@@ -4452,12 +4505,12 @@ var RelayTunnelRouter = class {
4452
4505
 
4453
4506
  // src/shared/version.ts
4454
4507
  import { readFileSync as readFileSync7 } from "node:fs";
4455
- import { dirname as dirname10, join as join9 } from "node:path";
4508
+ import { dirname as dirname6, join as join10 } from "node:path";
4456
4509
  import { fileURLToPath } from "node:url";
4457
4510
  function readClientVersion() {
4458
4511
  try {
4459
- const here2 = dirname10(fileURLToPath(import.meta.url));
4460
- const pkg = JSON.parse(readFileSync7(join9(here2, "..", "..", "package.json"), "utf8"));
4512
+ const here2 = dirname6(fileURLToPath(import.meta.url));
4513
+ const pkg = JSON.parse(readFileSync7(join10(here2, "..", "..", "package.json"), "utf8"));
4461
4514
  return typeof pkg.version === "string" && pkg.version ? pkg.version : "0.0.0";
4462
4515
  } catch {
4463
4516
  return "0.0.0";
@@ -4476,7 +4529,7 @@ function readUpdateStatusBestEffort(configPath) {
4476
4529
  function createDaemonServer(options = {}) {
4477
4530
  const app = Fastify({ logger: true });
4478
4531
  const events = new BridgeEventHub();
4479
- const configPath = options.configPath ?? process.env.BRIDGE_CONFIG ?? join10(mkdtempSync(join10(tmpdir2(), "claude-codex-wechat-")), "config.json");
4532
+ const configPath = options.configPath ?? process.env.BRIDGE_CONFIG ?? join11(mkdtempSync(join11(tmpdir2(), "claude-codex-wechat-")), "config.json");
4480
4533
  const persistedConfig = loadBridgeConfig(configPath);
4481
4534
  const bridgeDefaults = {
4482
4535
  defaultProvider: options.bridgeDefaults?.defaultProvider ?? persistedConfig.bridge?.defaultProvider ?? "claude-code",
@@ -4533,7 +4586,7 @@ function createDaemonServer(options = {}) {
4533
4586
  });
4534
4587
  }
4535
4588
  const weixinStateStore = configPath ? new FileWeixinStateStore(configPath) : void 0;
4536
- const weixinMediaDir = configPath ? join10(dirname11(configPath), "media") : void 0;
4589
+ const weixinMediaDir = configPath ? join11(dirname7(configPath), "media") : void 0;
4537
4590
  const managedWechatChannel = options.channel ? null : new ManagedWeixinDirectAdapter(options.wechat, weixinStateStore, weixinMediaDir);
4538
4591
  const channel = options.channel ?? managedWechatChannel;
4539
4592
  const weixinOutboundGate = managedWechatChannel && weixinStateStore ? new WeixinOutboundGate({
@@ -4824,7 +4877,7 @@ async function resolveProviderCommands(providers) {
4824
4877
 
4825
4878
  // src/daemon/staticFrontend.ts
4826
4879
  import { readFileSync as readFileSync8 } from "node:fs";
4827
- import { join as join11 } from "node:path";
4880
+ import { join as join12 } from "node:path";
4828
4881
  import fastifyStatic from "@fastify/static";
4829
4882
  function attachStaticFrontend(webRoot2) {
4830
4883
  return async (app) => {
@@ -4832,7 +4885,7 @@ function attachStaticFrontend(webRoot2) {
4832
4885
  root: webRoot2,
4833
4886
  wildcard: false
4834
4887
  });
4835
- const indexHtml = readFileSync8(join11(webRoot2, "index.html"), "utf8");
4888
+ const indexHtml = readFileSync8(join12(webRoot2, "index.html"), "utf8");
4836
4889
  app.setNotFoundHandler((request, reply) => {
4837
4890
  if (request.url.startsWith("/api")) {
4838
4891
  reply.code(404).send({ error: "not_found" });
@@ -4847,7 +4900,7 @@ function attachStaticFrontend(webRoot2) {
4847
4900
  import { existsSync as existsSync9, statSync, openSync, closeSync, watchFile } from "node:fs";
4848
4901
  import { mkdir as mkdir4, readFile as readFile6, rm as rm2, writeFile as writeFile5 } from "node:fs/promises";
4849
4902
  import { homedir as homedir8 } from "node:os";
4850
- import { dirname as dirname12, join as join12 } from "node:path";
4903
+ import { dirname as dirname8, join as join13 } from "node:path";
4851
4904
  import { createReadStream } from "node:fs";
4852
4905
  import { execFile, spawn as spawn7 } from "node:child_process";
4853
4906
  import { createInterface } from "node:readline";
@@ -4856,8 +4909,8 @@ var execFileAsync = promisify(execFile);
4856
4909
  async function installService(context) {
4857
4910
  if (process.platform === "darwin") {
4858
4911
  const spec = buildLaunchdSpec(context);
4859
- await mkdir4(dirname12(spec.plistPath), { recursive: true });
4860
- await mkdir4(dirname12(spec.stdoutPath), { recursive: true });
4912
+ await mkdir4(dirname8(spec.plistPath), { recursive: true });
4913
+ await mkdir4(dirname8(spec.stdoutPath), { recursive: true });
4861
4914
  await writeFile5(spec.plistPath, renderLaunchdPlist(spec), "utf8");
4862
4915
  await runLaunchctl(["unload", spec.plistPath]).catch(() => void 0);
4863
4916
  await runLaunchctl(["load", spec.plistPath]);
@@ -4866,8 +4919,8 @@ async function installService(context) {
4866
4919
  }
4867
4920
  if (process.platform === "linux") {
4868
4921
  const spec = buildSystemdUserSpec(context);
4869
- await mkdir4(dirname12(spec.unitPath), { recursive: true });
4870
- await mkdir4(dirname12(spec.stdoutPath), { recursive: true });
4922
+ await mkdir4(dirname8(spec.unitPath), { recursive: true });
4923
+ await mkdir4(dirname8(spec.stdoutPath), { recursive: true });
4871
4924
  await writeFile5(spec.unitPath, renderSystemdUnit(spec), "utf8");
4872
4925
  await runSystemctl(["--user", "daemon-reload"]);
4873
4926
  await runSystemctl(["--user", "enable", "--now", spec.unitName]);
@@ -5034,37 +5087,37 @@ async function readServiceStatus(context) {
5034
5087
  throw new Error("service_status_not_supported_on_this_platform");
5035
5088
  }
5036
5089
  function buildLaunchdSpec(context) {
5037
- const stateDir = join12(homedir8(), ".claude-codex-wechat");
5090
+ const stateDir = join13(homedir8(), ".claude-codex-wechat");
5038
5091
  return {
5039
5092
  label: "com.claude-codex-wechat",
5040
- plistPath: join12(homedir8(), "Library", "LaunchAgents", "com.claude-codex-wechat.plist"),
5093
+ plistPath: join13(homedir8(), "Library", "LaunchAgents", "com.claude-codex-wechat.plist"),
5041
5094
  programArgs: [context.nodePath ?? process.execPath, context.cliEntrypointPath, "__daemon"],
5042
5095
  workingDirectory: homedir8(),
5043
- stdoutPath: join12(stateDir, "logs", "service.stdout.log"),
5044
- stderrPath: join12(stateDir, "logs", "service.stderr.log"),
5096
+ stdoutPath: join13(stateDir, "logs", "service.stdout.log"),
5097
+ stderrPath: join13(stateDir, "logs", "service.stderr.log"),
5045
5098
  environment: buildServiceEnvironment(context)
5046
5099
  };
5047
5100
  }
5048
5101
  function buildSystemdUserSpec(context) {
5049
- const stateDir = join12(homedir8(), ".claude-codex-wechat");
5102
+ const stateDir = join13(homedir8(), ".claude-codex-wechat");
5050
5103
  return {
5051
5104
  unitName: "claude-codex-wechat.service",
5052
- unitPath: join12(homedir8(), ".config", "systemd", "user", "claude-codex-wechat.service"),
5105
+ unitPath: join13(homedir8(), ".config", "systemd", "user", "claude-codex-wechat.service"),
5053
5106
  execStart: [context.nodePath ?? process.execPath, context.cliEntrypointPath, "__daemon"],
5054
5107
  workingDirectory: homedir8(),
5055
- stdoutPath: join12(stateDir, "logs", "service.stdout.log"),
5056
- stderrPath: join12(stateDir, "logs", "service.stderr.log"),
5108
+ stdoutPath: join13(stateDir, "logs", "service.stdout.log"),
5109
+ stderrPath: join13(stateDir, "logs", "service.stderr.log"),
5057
5110
  environment: buildServiceEnvironment(context)
5058
5111
  };
5059
5112
  }
5060
5113
  function buildProcessSpec(context) {
5061
- const stateDir = join12(homedir8(), ".claude-codex-wechat");
5114
+ const stateDir = join13(homedir8(), ".claude-codex-wechat");
5062
5115
  return {
5063
- pidPath: join12(stateDir, "service.pid"),
5116
+ pidPath: join13(stateDir, "service.pid"),
5064
5117
  command: [context.nodePath ?? process.execPath, context.cliEntrypointPath, "__daemon"],
5065
5118
  workingDirectory: homedir8(),
5066
- stdoutPath: join12(stateDir, "logs", "service.stdout.log"),
5067
- stderrPath: join12(stateDir, "logs", "service.stderr.log"),
5119
+ stdoutPath: join13(stateDir, "logs", "service.stdout.log"),
5120
+ stderrPath: join13(stateDir, "logs", "service.stderr.log"),
5068
5121
  environment: buildServiceEnvironment(context)
5069
5122
  };
5070
5123
  }
@@ -5185,7 +5238,7 @@ async function readPidFile(pidPath) {
5185
5238
  }
5186
5239
  }
5187
5240
  async function writePidFile(pidPath, pid) {
5188
- await mkdir4(dirname12(pidPath), { recursive: true });
5241
+ await mkdir4(dirname8(pidPath), { recursive: true });
5189
5242
  await writeFile5(pidPath, `${pid}
5190
5243
  `, "utf8");
5191
5244
  }
@@ -5200,7 +5253,7 @@ async function killPid(pid) {
5200
5253
  process.kill(pid, "SIGTERM");
5201
5254
  }
5202
5255
  async function spawnDetachedDaemon(spec) {
5203
- await mkdir4(dirname12(spec.stdoutPath), { recursive: true });
5256
+ await mkdir4(dirname8(spec.stdoutPath), { recursive: true });
5204
5257
  const outFd = openSync(spec.stdoutPath, "a");
5205
5258
  const errFd = openSync(spec.stderrPath, "a");
5206
5259
  try {
@@ -5286,8 +5339,8 @@ function fileSizeHint(path) {
5286
5339
  }
5287
5340
 
5288
5341
  // src/cli.ts
5289
- var here = dirname13(fileURLToPath2(import.meta.url));
5290
- var webRoot = join13(here, "..", "web");
5342
+ var here = dirname9(fileURLToPath2(import.meta.url));
5343
+ var webRoot = join14(here, "..", "web");
5291
5344
  async function main() {
5292
5345
  const command = process.argv[2];
5293
5346
  switch (command) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-codex-wechat",
3
- "version": "0.1.23",
3
+ "version": "0.1.24",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@11.5.0",