claude-codex-wechat 0.1.24 → 0.1.25
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/dist/server/cli.js +77 -40
- package/package.json +1 -1
package/dist/server/cli.js
CHANGED
|
@@ -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,
|
|
160
|
-
import { mkdir, rename,
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
4915
|
-
|
|
4916
|
-
await runLaunchctl(["
|
|
4917
|
-
await runLaunchctl(["
|
|
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
|
|
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
|
-
|
|
4939
|
-
await runLaunchctl(["
|
|
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
|
-
|
|
5011
|
-
await runLaunchctl(["
|
|
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
|
|
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 =
|
|
5258
|
-
const errFd =
|
|
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
|
-
|
|
5278
|
-
|
|
5314
|
+
closeSync2(outFd);
|
|
5315
|
+
closeSync2(errFd);
|
|
5279
5316
|
}
|
|
5280
5317
|
}
|
|
5281
5318
|
async function ensureFileExists(path, errorCode) {
|