claude-codex-wechat 0.1.24 → 0.1.26

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 +77 -40
  2. package/package.json +3 -2
@@ -156,10 +156,11 @@ import { randomBytes as randomBytes2 } from "node:crypto";
156
156
  import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
157
157
 
158
158
  // src/shared/atomicFile.ts
159
- import { mkdirSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
160
- import { mkdir, rename, unlink, writeFile } from "node:fs/promises";
159
+ import { closeSync, fsyncSync, mkdirSync, openSync, renameSync, unlinkSync, writeSync } from "node:fs";
160
+ import { mkdir, open, readdir, rename, stat, unlink } from "node:fs/promises";
161
161
  import { randomBytes } from "node:crypto";
162
162
  import { basename, dirname, join as join2 } from "node:path";
163
+ var FILE_MODE = 384;
163
164
  function tempPathFor(path) {
164
165
  return join2(dirname(path), `.${basename(path)}.${process.pid}.${randomBytes(6).toString("hex")}.tmp`);
165
166
  }
@@ -182,7 +183,13 @@ function writeFileAtomicSync(path, data) {
182
183
  const tmp = tempPathFor(path);
183
184
  mkdirSync(dirname(path), { recursive: true });
184
185
  try {
185
- writeFileSync(tmp, data, "utf8");
186
+ const fd = openSync(tmp, "w", FILE_MODE);
187
+ try {
188
+ writeSync(fd, data, null, "utf8");
189
+ fsyncSync(fd);
190
+ } finally {
191
+ closeSync(fd);
192
+ }
186
193
  for (let attempt = 0; ; attempt++) {
187
194
  try {
188
195
  renameSync(tmp, path);
@@ -204,7 +211,13 @@ async function writeFileAtomic(path, data) {
204
211
  const tmp = tempPathFor(path);
205
212
  await mkdir(dirname(path), { recursive: true });
206
213
  try {
207
- await writeFile(tmp, data, "utf8");
214
+ const handle = await open(tmp, "w", FILE_MODE);
215
+ try {
216
+ await handle.writeFile(data, "utf8");
217
+ await handle.sync();
218
+ } finally {
219
+ await handle.close();
220
+ }
208
221
  for (let attempt = 0; ; attempt++) {
209
222
  try {
210
223
  await rename(tmp, path);
@@ -219,6 +232,26 @@ async function writeFileAtomic(path, data) {
219
232
  throw error;
220
233
  }
221
234
  }
235
+ async function cleanupStaleTempFiles(targetPath, maxAgeMs = 36e5) {
236
+ const dir = dirname(targetPath);
237
+ const prefix = `.${basename(targetPath)}.`;
238
+ let entries;
239
+ try {
240
+ entries = await readdir(dir);
241
+ } catch {
242
+ return;
243
+ }
244
+ const now = Date.now();
245
+ await Promise.all(entries.map(async (name) => {
246
+ if (!name.startsWith(prefix) || !name.endsWith(".tmp")) return;
247
+ const full = join2(dir, name);
248
+ try {
249
+ const info = await stat(full);
250
+ if (now - info.mtimeMs >= maxAgeMs) await unlink(full);
251
+ } catch {
252
+ }
253
+ }));
254
+ }
222
255
 
223
256
  // src/daemon/configPersistence.ts
224
257
  async function persistWechatCredentialsToConfigFile(input) {
@@ -463,7 +496,7 @@ var FileWeixinStateStore = class {
463
496
  };
464
497
 
465
498
  // src/providers/claude-code/nativeSessions.ts
466
- import { appendFile, mkdir as mkdir2, readdir, readFile as readFile2, stat, writeFile as writeFile2 } from "node:fs/promises";
499
+ import { appendFile, mkdir as mkdir2, readdir as readdir2, readFile as readFile2, stat as stat2, writeFile } from "node:fs/promises";
467
500
  import { homedir as homedir2 } from "node:os";
468
501
  import { basename as basename2, dirname as dirname2, join as join3 } from "node:path";
469
502
 
@@ -499,17 +532,17 @@ async function listRecoverableClaudeSessions(env = process.env) {
499
532
  const historyIndex = await readClaudeHistoryIndex(env);
500
533
  const candidates = [];
501
534
  try {
502
- const projectEntries = await readdir(projectsDir, { withFileTypes: true });
535
+ const projectEntries = await readdir2(projectsDir, { withFileTypes: true });
503
536
  for (const entry of projectEntries) {
504
537
  if (!entry.isDirectory() || entry.isSymbolicLink()) continue;
505
538
  const projectDir = join3(projectsDir, String(entry.name));
506
- const sessionEntries = await readdir(projectDir, { withFileTypes: true }).catch(() => []);
539
+ const sessionEntries = await readdir2(projectDir, { withFileTypes: true }).catch(() => []);
507
540
  for (const sessionEntry of sessionEntries) {
508
541
  if (!sessionEntry.isFile() || sessionEntry.isSymbolicLink()) continue;
509
542
  const fileName = String(sessionEntry.name);
510
543
  if (!fileName.endsWith(".jsonl")) continue;
511
544
  const filePath = join3(projectDir, fileName);
512
- const metadata = await stat(filePath).catch(() => null);
545
+ const metadata = await stat2(filePath).catch(() => null);
513
546
  const parsedMeta = await readClaudeSessionMetadata(filePath).catch(() => null);
514
547
  const historyMeta = historyIndex.get(basename2(fileName, ".jsonl"));
515
548
  const sessionId = basename2(fileName, ".jsonl");
@@ -536,12 +569,12 @@ async function findRecoverableClaudeSessionPath(sessionId, env = process.env) {
536
569
  if (!trimmed || trimmed.includes("/") || trimmed.includes("\\")) return null;
537
570
  const projectsDir = join3(resolveClaudeConfigDir(env), "projects");
538
571
  try {
539
- const projectEntries = await readdir(projectsDir, { withFileTypes: true });
572
+ const projectEntries = await readdir2(projectsDir, { withFileTypes: true });
540
573
  for (const entry of projectEntries) {
541
574
  if (!entry.isDirectory() || entry.isSymbolicLink()) continue;
542
575
  const sessionPath = join3(projectsDir, String(entry.name), `${trimmed}.jsonl`);
543
576
  try {
544
- const metadata = await stat(sessionPath);
577
+ const metadata = await stat2(sessionPath);
545
578
  if (metadata.isFile()) return sessionPath;
546
579
  } catch {
547
580
  }
@@ -707,7 +740,7 @@ async function upsertClaudeHistoryDisplay(input) {
707
740
  }
708
741
  if (!changed) return false;
709
742
  await mkdir2(dirname2(historyPath), { recursive: true });
710
- await writeFile2(historyPath, `${lines.join("\n")}
743
+ await writeFile(historyPath, `${lines.join("\n")}
711
744
  `, "utf8");
712
745
  return true;
713
746
  }
@@ -747,14 +780,14 @@ async function normalizeClaudeSessionFileForResume(sessionPath) {
747
780
  changed = true;
748
781
  }
749
782
  if (!changed) return false;
750
- await writeFile2(sessionPath, `${nextLines.join("\n")}
783
+ await writeFile(sessionPath, `${nextLines.join("\n")}
751
784
  `, "utf8");
752
785
  return true;
753
786
  }
754
787
 
755
788
  // src/providers/codex/nativeSessions.ts
756
789
  import Database from "better-sqlite3";
757
- import { readdir as readdir2, readFile as readFile3, stat as stat2 } from "node:fs/promises";
790
+ import { readdir as readdir3, readFile as readFile3, stat as stat3 } from "node:fs/promises";
758
791
  import { existsSync as existsSync4 } from "node:fs";
759
792
  import { homedir as homedir3 } from "node:os";
760
793
  import { basename as basename3, join as join4 } from "node:path";
@@ -778,7 +811,7 @@ async function listRecoverableCodexSessions(env = process.env) {
778
811
  const candidates = [];
779
812
  async function walk(currentDir, depth) {
780
813
  if (depth > 8) return;
781
- const entries = await readdir2(currentDir, { withFileTypes: true }).catch(() => []);
814
+ const entries = await readdir3(currentDir, { withFileTypes: true }).catch(() => []);
782
815
  for (const entry of entries) {
783
816
  if (entry.isSymbolicLink()) continue;
784
817
  const name = String(entry.name);
@@ -814,7 +847,7 @@ async function findRecoverableCodexSessionPath(sessionId, env = process.env) {
814
847
  if (!trimmed || trimmed.includes("/") || trimmed.includes("\\")) return null;
815
848
  async function walk(currentDir, depth) {
816
849
  if (depth > 8) return null;
817
- const entries = await readdir2(currentDir, { withFileTypes: true }).catch(() => []);
850
+ const entries = await readdir3(currentDir, { withFileTypes: true }).catch(() => []);
818
851
  for (const entry of entries) {
819
852
  if (entry.isSymbolicLink()) continue;
820
853
  const name = String(entry.name);
@@ -900,7 +933,7 @@ async function readCodexSessionMeta(filePath) {
900
933
  } catch {
901
934
  }
902
935
  }
903
- const metadata = await stat2(filePath).catch(() => null);
936
+ const metadata = await stat3(filePath).catch(() => null);
904
937
  return {
905
938
  cwd,
906
939
  lastActivityAt: metadata ? Math.trunc(metadata.mtimeMs) : void 0
@@ -914,7 +947,7 @@ async function readCodexSessionMeta(filePath) {
914
947
  import { nanoid } from "nanoid";
915
948
 
916
949
  // src/providers/codex/sessionIndex.ts
917
- import { mkdir as mkdir3, readFile as readFile4, writeFile as writeFile3 } from "node:fs/promises";
950
+ import { mkdir as mkdir3, readFile as readFile4, writeFile as writeFile2 } from "node:fs/promises";
918
951
  import { homedir as homedir4 } from "node:os";
919
952
  import { dirname as dirname3, join as join5 } from "node:path";
920
953
  function resolveCodexIndexPath(env = process.env) {
@@ -955,14 +988,14 @@ async function upsertCodexSessionIndexEntry(input, env = process.env) {
955
988
  }));
956
989
  }
957
990
  await mkdir3(dirname3(indexPath), { recursive: true });
958
- await writeFile3(indexPath, `${lines.join("\n")}
991
+ await writeFile2(indexPath, `${lines.join("\n")}
959
992
  `, "utf8");
960
993
  }
961
994
 
962
995
  // src/providers/codex/nativeThreads.ts
963
996
  import Database2 from "better-sqlite3";
964
997
  import { existsSync as existsSync5 } from "node:fs";
965
- import { readFile as readFile5, writeFile as writeFile4 } from "node:fs/promises";
998
+ import { readFile as readFile5, writeFile as writeFile3 } from "node:fs/promises";
966
999
  import { homedir as homedir5 } from "node:os";
967
1000
  import { join as join6 } from "node:path";
968
1001
  function resolveCodexStateDbPath(env = process.env) {
@@ -1022,7 +1055,7 @@ async function syncCodexRolloutSessionMeta(rolloutPath) {
1022
1055
  payload.originator = "codex-tui";
1023
1056
  first.payload = payload;
1024
1057
  lines[0] = JSON.stringify(first);
1025
- await writeFile4(rolloutPath, lines.join("\n"), "utf8");
1058
+ await writeFile3(rolloutPath, lines.join("\n"), "utf8");
1026
1059
  } catch {
1027
1060
  }
1028
1061
  }
@@ -1627,7 +1660,7 @@ function normalizeSettings(input, defaultWorkspace) {
1627
1660
  }
1628
1661
 
1629
1662
  // src/admin/fsBrowseRoutes.ts
1630
- import { readdir as readdir3 } from "node:fs/promises";
1663
+ import { readdir as readdir4 } from "node:fs/promises";
1631
1664
  import { homedir as homedir7 } from "node:os";
1632
1665
  import { dirname as dirname4, join as join7, resolve } from "node:path";
1633
1666
 
@@ -1650,7 +1683,7 @@ function registerFsBrowseRoutes(input) {
1650
1683
  const isOnKeepChain = (path) => keep !== null && (keep === path || keep.startsWith(path + "/"));
1651
1684
  let dirents;
1652
1685
  try {
1653
- dirents = await readdir3(target, { withFileTypes: true });
1686
+ dirents = await readdir4(target, { withFileTypes: true });
1654
1687
  } catch {
1655
1688
  reply.code(400);
1656
1689
  return { error: "cannot_read_directory", path: target };
@@ -2162,7 +2195,7 @@ function chunkText(text, limit) {
2162
2195
  }
2163
2196
 
2164
2197
  // src/channels/weixin-direct/mediaDownloader.ts
2165
- import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "node:fs";
2198
+ import { mkdirSync as mkdirSync2, writeFileSync } from "node:fs";
2166
2199
  import { dirname as dirname5 } from "node:path";
2167
2200
 
2168
2201
  // src/channels/weixin-direct/mediaCrypto.ts
@@ -2227,7 +2260,7 @@ var WeixinMediaDownloader = class {
2227
2260
  return { ok: false, reason: "decrypt_failed" };
2228
2261
  }
2229
2262
  mkdirSync2(dirname5(input.destPath), { recursive: true });
2230
- writeFileSync2(input.destPath, plaintext);
2263
+ writeFileSync(input.destPath, plaintext);
2231
2264
  return { ok: true, localPath: input.destPath, bytes: plaintext.length };
2232
2265
  }
2233
2266
  };
@@ -4823,6 +4856,7 @@ async function startDaemon(options) {
4823
4856
  const port = options.port ?? Number(process.env.BRIDGE_PORT ?? 8787);
4824
4857
  const host = options.host ?? "0.0.0.0";
4825
4858
  const configPath = options.configPath ?? process.env.BRIDGE_CONFIG ?? defaultConfigPath();
4859
+ await cleanupStaleTempFiles(configPath).catch(() => void 0);
4826
4860
  const config = loadBridgeConfig(configPath);
4827
4861
  const providerCommands = await resolveProviderCommands(config.providers);
4828
4862
  await persistProviderCommandsToConfigFile({
@@ -4897,8 +4931,8 @@ function attachStaticFrontend(webRoot2) {
4897
4931
  }
4898
4932
 
4899
4933
  // src/daemon/service.ts
4900
- import { existsSync as existsSync9, statSync, openSync, closeSync, watchFile } from "node:fs";
4901
- import { mkdir as mkdir4, readFile as readFile6, rm as rm2, writeFile as writeFile5 } from "node:fs/promises";
4934
+ import { existsSync as existsSync9, statSync, openSync as openSync2, closeSync as closeSync2, watchFile } from "node:fs";
4935
+ import { mkdir as mkdir4, readFile as readFile6, rm as rm2, writeFile as writeFile4 } from "node:fs/promises";
4902
4936
  import { homedir as homedir8 } from "node:os";
4903
4937
  import { dirname as dirname8, join as join13 } from "node:path";
4904
4938
  import { createReadStream } from "node:fs";
@@ -4911,17 +4945,18 @@ async function installService(context) {
4911
4945
  const spec = buildLaunchdSpec(context);
4912
4946
  await mkdir4(dirname8(spec.plistPath), { recursive: true });
4913
4947
  await mkdir4(dirname8(spec.stdoutPath), { recursive: true });
4914
- await writeFile5(spec.plistPath, renderLaunchdPlist(spec), "utf8");
4915
- await runLaunchctl(["unload", spec.plistPath]).catch(() => void 0);
4916
- await runLaunchctl(["load", spec.plistPath]);
4917
- await runLaunchctl(["kickstart", "-k", `gui/${process.getuid?.() ?? 0}/${spec.label}`]);
4948
+ await writeFile4(spec.plistPath, renderLaunchdPlist(spec), "utf8");
4949
+ const guiDomain = `gui/${process.getuid?.() ?? 0}`;
4950
+ await runLaunchctl(["bootout", guiDomain, spec.plistPath]).catch(() => void 0);
4951
+ await runLaunchctl(["bootstrap", guiDomain, spec.plistPath]);
4952
+ await runLaunchctl(["kickstart", "-k", `${guiDomain}/${spec.label}`]);
4918
4953
  return await readServiceStatus(context);
4919
4954
  }
4920
4955
  if (process.platform === "linux") {
4921
4956
  const spec = buildSystemdUserSpec(context);
4922
4957
  await mkdir4(dirname8(spec.unitPath), { recursive: true });
4923
4958
  await mkdir4(dirname8(spec.stdoutPath), { recursive: true });
4924
- await writeFile5(spec.unitPath, renderSystemdUnit(spec), "utf8");
4959
+ await writeFile4(spec.unitPath, renderSystemdUnit(spec), "utf8");
4925
4960
  await runSystemctl(["--user", "daemon-reload"]);
4926
4961
  await runSystemctl(["--user", "enable", "--now", spec.unitName]);
4927
4962
  return await readServiceStatus(context);
@@ -4935,8 +4970,9 @@ async function startService(context) {
4935
4970
  if (process.platform === "darwin") {
4936
4971
  const spec = buildLaunchdSpec(context);
4937
4972
  await ensureFileExists(spec.plistPath, "service_not_installed");
4938
- await runLaunchctl(["load", spec.plistPath]).catch(() => void 0);
4939
- await runLaunchctl(["kickstart", "-k", `gui/${process.getuid?.() ?? 0}/${spec.label}`]);
4973
+ const guiDomain = `gui/${process.getuid?.() ?? 0}`;
4974
+ await runLaunchctl(["bootstrap", guiDomain, spec.plistPath]).catch(() => void 0);
4975
+ await runLaunchctl(["kickstart", "-k", `${guiDomain}/${spec.label}`]);
4940
4976
  return await readServiceStatus(context);
4941
4977
  }
4942
4978
  if (process.platform === "linux") {
@@ -5007,8 +5043,9 @@ async function restartService(context) {
5007
5043
  if (process.platform === "darwin") {
5008
5044
  const spec = buildLaunchdSpec(context);
5009
5045
  await ensureFileExists(spec.plistPath, "service_not_installed");
5010
- await runLaunchctl(["load", spec.plistPath]).catch(() => void 0);
5011
- await runLaunchctl(["kickstart", "-k", `gui/${process.getuid?.() ?? 0}/${spec.label}`]);
5046
+ const guiDomain = `gui/${process.getuid?.() ?? 0}`;
5047
+ await runLaunchctl(["bootstrap", guiDomain, spec.plistPath]).catch(() => void 0);
5048
+ await runLaunchctl(["kickstart", "-k", `${guiDomain}/${spec.label}`]);
5012
5049
  return await readServiceStatus(context);
5013
5050
  }
5014
5051
  if (process.platform === "linux") {
@@ -5239,7 +5276,7 @@ async function readPidFile(pidPath) {
5239
5276
  }
5240
5277
  async function writePidFile(pidPath, pid) {
5241
5278
  await mkdir4(dirname8(pidPath), { recursive: true });
5242
- await writeFile5(pidPath, `${pid}
5279
+ await writeFile4(pidPath, `${pid}
5243
5280
  `, "utf8");
5244
5281
  }
5245
5282
  async function removePidFile(pidPath) {
@@ -5254,8 +5291,8 @@ async function killPid(pid) {
5254
5291
  }
5255
5292
  async function spawnDetachedDaemon(spec) {
5256
5293
  await mkdir4(dirname8(spec.stdoutPath), { recursive: true });
5257
- const outFd = openSync(spec.stdoutPath, "a");
5258
- const errFd = openSync(spec.stderrPath, "a");
5294
+ const outFd = openSync2(spec.stdoutPath, "a");
5295
+ const errFd = openSync2(spec.stderrPath, "a");
5259
5296
  try {
5260
5297
  const [command, ...args] = spec.command;
5261
5298
  const child = spawn7(command, args, {
@@ -5274,8 +5311,8 @@ async function spawnDetachedDaemon(spec) {
5274
5311
  child.unref();
5275
5312
  return child.pid;
5276
5313
  } finally {
5277
- closeSync(outFd);
5278
- closeSync(errFd);
5314
+ closeSync2(outFd);
5315
+ closeSync2(errFd);
5279
5316
  }
5280
5317
  }
5281
5318
  async function ensureFileExists(path, errorCode) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-codex-wechat",
3
- "version": "0.1.24",
3
+ "version": "0.1.26",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@11.5.0",
@@ -34,7 +34,8 @@
34
34
  "build": "vite build && node scripts/build-server.mjs",
35
35
  "relay:start": "pnpm --dir relay-server start",
36
36
  "relay:test": "pnpm --dir relay-server test",
37
- "prepublishOnly": "npm run build"
37
+ "prepublishOnly": "npm run build",
38
+ "postpublish": "node scripts/sync-mirror.mjs"
38
39
  },
39
40
  "dependencies": {
40
41
  "@fastify/static": "^9.1.3",