claude-codex-wechat 0.1.36 → 0.1.38
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/mcp/mediaServer.js +12 -7
- package/dist/server/cli.js +98 -12
- package/package.json +1 -1
package/dist/mcp/mediaServer.js
CHANGED
|
@@ -99,17 +99,22 @@ import { homedir } from "node:os";
|
|
|
99
99
|
import { fileURLToPath } from "node:url";
|
|
100
100
|
import { z as z2 } from "zod";
|
|
101
101
|
var BRIDGE_API_URL2 = process.env.BRIDGE_API_URL || "http://localhost:8787";
|
|
102
|
-
function
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
102
|
+
function douyinScriptCandidates(baseDir) {
|
|
103
|
+
return [
|
|
104
|
+
// Source layout: src/mcp/tools -> src/mcp/scripts
|
|
105
|
+
join(baseDir, "..", "scripts", "douyin-download.mjs"),
|
|
106
|
+
// Bundled layout: dist/mcp/mediaServer.js -> dist/mcp/scripts
|
|
107
|
+
join(baseDir, "scripts", "douyin-download.mjs"),
|
|
108
|
+
// Older/alternate dist layout fallback
|
|
109
|
+
join(baseDir, "..", "..", "mcp", "scripts", "douyin-download.mjs"),
|
|
109
110
|
// User skills locations (cross-platform)
|
|
110
111
|
join(homedir(), ".agents", "skills", "douyin-download", "scripts", "douyin-download.mjs"),
|
|
111
112
|
join(homedir(), ".claude", "skills", "douyin-download", "scripts", "douyin-download.mjs")
|
|
112
113
|
];
|
|
114
|
+
}
|
|
115
|
+
function findDouyinScript() {
|
|
116
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
117
|
+
const candidates = douyinScriptCandidates(here);
|
|
113
118
|
for (const p of candidates) {
|
|
114
119
|
const resolved = resolve(p);
|
|
115
120
|
if (existsSync(resolved)) return resolved;
|
package/dist/server/cli.js
CHANGED
|
@@ -2977,17 +2977,52 @@ function runHelpProbe(command) {
|
|
|
2977
2977
|
}
|
|
2978
2978
|
|
|
2979
2979
|
// src/providers/claude-code/claudeStreamingRunner.ts
|
|
2980
|
+
var IDLE_SENTINEL = /* @__PURE__ */ Symbol("idle_timeout");
|
|
2981
|
+
function capTail(text, capBytes) {
|
|
2982
|
+
if (capBytes <= 0 || text.length <= capBytes) return text;
|
|
2983
|
+
return text.slice(text.length - capBytes);
|
|
2984
|
+
}
|
|
2985
|
+
function readWithIdleTimeout(handle, ms) {
|
|
2986
|
+
if (!ms || ms <= 0) return handle.read();
|
|
2987
|
+
return new Promise((resolve2) => {
|
|
2988
|
+
let settled = false;
|
|
2989
|
+
const timer = setTimeout(() => {
|
|
2990
|
+
if (!settled) {
|
|
2991
|
+
settled = true;
|
|
2992
|
+
resolve2(IDLE_SENTINEL);
|
|
2993
|
+
}
|
|
2994
|
+
}, ms);
|
|
2995
|
+
timer.unref?.();
|
|
2996
|
+
void handle.read().then((chunk) => {
|
|
2997
|
+
if (!settled) {
|
|
2998
|
+
settled = true;
|
|
2999
|
+
clearTimeout(timer);
|
|
3000
|
+
resolve2(chunk);
|
|
3001
|
+
}
|
|
3002
|
+
});
|
|
3003
|
+
});
|
|
3004
|
+
}
|
|
2980
3005
|
var ClaudeStreamingRunner = class {
|
|
2981
3006
|
sessions = /* @__PURE__ */ new Map();
|
|
2982
3007
|
command;
|
|
2983
3008
|
spawner;
|
|
2984
3009
|
capabilityProbe;
|
|
2985
3010
|
mcpConfigPath;
|
|
3011
|
+
idleTimeoutMs;
|
|
3012
|
+
maxProcessAgeMs;
|
|
3013
|
+
maxTurns;
|
|
3014
|
+
stderrCapBytes;
|
|
3015
|
+
maxLineBytes;
|
|
2986
3016
|
constructor(input = {}) {
|
|
2987
3017
|
this.command = input.command ?? "claude";
|
|
2988
|
-
this.spawner = input.spawner ?? defaultClaudeStreamSpawner;
|
|
2989
3018
|
this.capabilityProbe = input.capabilityProbe ?? probeAppendSystemPromptSupport;
|
|
2990
3019
|
this.mcpConfigPath = input.mcpConfigPath;
|
|
3020
|
+
this.idleTimeoutMs = input.idleTimeoutMs ?? 18e4;
|
|
3021
|
+
this.maxProcessAgeMs = input.maxProcessAgeMs ?? 2 * 60 * 60 * 1e3;
|
|
3022
|
+
this.maxTurns = input.maxTurns ?? 50;
|
|
3023
|
+
this.stderrCapBytes = input.stderrCapBytes ?? 64 * 1024;
|
|
3024
|
+
this.maxLineBytes = input.maxLineBytes ?? 10 * 1024 * 1024;
|
|
3025
|
+
this.spawner = input.spawner ?? ((call) => defaultClaudeStreamSpawner(call, { stderrCapBytes: this.stderrCapBytes, maxLineBytes: this.maxLineBytes }));
|
|
2991
3026
|
}
|
|
2992
3027
|
async startSession(input) {
|
|
2993
3028
|
const resumeId = input.options?.providerSessionId;
|
|
@@ -3000,7 +3035,8 @@ var ClaudeStreamingRunner = class {
|
|
|
3000
3035
|
cwd: input.cwd,
|
|
3001
3036
|
status: "idle",
|
|
3002
3037
|
resumeId,
|
|
3003
|
-
pendingFollowUps: 0
|
|
3038
|
+
pendingFollowUps: 0,
|
|
3039
|
+
turnCount: 0
|
|
3004
3040
|
};
|
|
3005
3041
|
this.sessions.set(input.bridgeSessionId, session);
|
|
3006
3042
|
return session;
|
|
@@ -3015,7 +3051,13 @@ var ClaudeStreamingRunner = class {
|
|
|
3015
3051
|
session.pendingFollowUps = 0;
|
|
3016
3052
|
handle.write(userEnvelope(input.text));
|
|
3017
3053
|
while (true) {
|
|
3018
|
-
const chunk = await handle.
|
|
3054
|
+
const chunk = await readWithIdleTimeout(handle, this.idleTimeoutMs);
|
|
3055
|
+
if (chunk === IDLE_SENTINEL) {
|
|
3056
|
+
handle.close();
|
|
3057
|
+
session.handle = void 0;
|
|
3058
|
+
yield { type: "error", error: "idle_timeout", code: "idle_timeout" };
|
|
3059
|
+
return;
|
|
3060
|
+
}
|
|
3019
3061
|
if (chunk.type === "exit") {
|
|
3020
3062
|
session.handle = void 0;
|
|
3021
3063
|
yield { type: "error", error: chunk.stderr || `claude stream exited with ${chunk.code}` };
|
|
@@ -3064,6 +3106,7 @@ var ClaudeStreamingRunner = class {
|
|
|
3064
3106
|
session.claudeSessionId = sessionId;
|
|
3065
3107
|
session.resumeId = sessionId;
|
|
3066
3108
|
}
|
|
3109
|
+
session.turnCount += 1;
|
|
3067
3110
|
yield { type: "message_done" };
|
|
3068
3111
|
if (session.pendingFollowUps > 0) {
|
|
3069
3112
|
session.pendingFollowUps -= 1;
|
|
@@ -3096,11 +3139,22 @@ var ClaudeStreamingRunner = class {
|
|
|
3096
3139
|
this.sessions.delete(bridgeSessionId);
|
|
3097
3140
|
}
|
|
3098
3141
|
ensureHandle(session) {
|
|
3099
|
-
if (session.handle) return session.handle;
|
|
3142
|
+
if (session.handle && !this.shouldRetire(session)) return session.handle;
|
|
3143
|
+
if (session.handle) {
|
|
3144
|
+
session.handle.close();
|
|
3145
|
+
session.handle = void 0;
|
|
3146
|
+
}
|
|
3100
3147
|
const args = buildStreamingArgs(session, this.mcpConfigPath);
|
|
3101
3148
|
session.handle = this.spawner({ command: this.command, args, cwd: session.cwd });
|
|
3149
|
+
session.spawnedAt = Date.now();
|
|
3150
|
+
session.turnCount = 0;
|
|
3102
3151
|
return session.handle;
|
|
3103
3152
|
}
|
|
3153
|
+
shouldRetire(session) {
|
|
3154
|
+
if (this.maxTurns > 0 && session.turnCount >= this.maxTurns) return true;
|
|
3155
|
+
if (this.maxProcessAgeMs > 0 && session.spawnedAt && Date.now() - session.spawnedAt >= this.maxProcessAgeMs) return true;
|
|
3156
|
+
return false;
|
|
3157
|
+
}
|
|
3104
3158
|
};
|
|
3105
3159
|
function buildStreamingArgs(session, mcpConfigPath) {
|
|
3106
3160
|
const args = [
|
|
@@ -3147,7 +3201,9 @@ function parseJsonLine(line) {
|
|
|
3147
3201
|
return null;
|
|
3148
3202
|
}
|
|
3149
3203
|
}
|
|
3150
|
-
function defaultClaudeStreamSpawner(call) {
|
|
3204
|
+
function defaultClaudeStreamSpawner(call, opts = {}) {
|
|
3205
|
+
const stderrCapBytes = opts.stderrCapBytes ?? 64 * 1024;
|
|
3206
|
+
const maxLineBytes = opts.maxLineBytes ?? 10 * 1024 * 1024;
|
|
3151
3207
|
const child = spawn3(call.command, call.args, { cwd: expandTilde(call.cwd) ?? call.cwd, stdio: ["pipe", "pipe", "pipe"], shell: useShellForCli() });
|
|
3152
3208
|
let stderr = "";
|
|
3153
3209
|
let buffer = "";
|
|
@@ -3169,9 +3225,15 @@ function defaultClaudeStreamSpawner(call) {
|
|
|
3169
3225
|
for (const line of parts) {
|
|
3170
3226
|
if (line.trim()) push({ type: "line", line });
|
|
3171
3227
|
}
|
|
3228
|
+
if (buffer.length > maxLineBytes) {
|
|
3229
|
+
stderr = capTail(stderr + `
|
|
3230
|
+
[bridge] dropped oversized line buffer (${buffer.length} bytes)
|
|
3231
|
+
`, stderrCapBytes);
|
|
3232
|
+
buffer = "";
|
|
3233
|
+
}
|
|
3172
3234
|
});
|
|
3173
3235
|
child.stderr.on("data", (data) => {
|
|
3174
|
-
stderr
|
|
3236
|
+
stderr = capTail(stderr + String(data), stderrCapBytes);
|
|
3175
3237
|
});
|
|
3176
3238
|
child.on("error", (error) => {
|
|
3177
3239
|
push({ type: "exit", code: error.code ?? "ERROR", stderr: stderr || error.message });
|
|
@@ -3657,9 +3719,22 @@ var CodexProvider = class {
|
|
|
3657
3719
|
};
|
|
3658
3720
|
|
|
3659
3721
|
// src/providers/defaultProviders.ts
|
|
3722
|
+
function readEnvInt(raw, fallback) {
|
|
3723
|
+
if (!raw) return fallback;
|
|
3724
|
+
const n = Number.parseInt(raw, 10);
|
|
3725
|
+
if (!Number.isFinite(n) || n <= 0) return fallback;
|
|
3726
|
+
return n;
|
|
3727
|
+
}
|
|
3660
3728
|
function createDefaultProviders(input = {}) {
|
|
3729
|
+
const claudeRunner = new ClaudeStreamingRunner({
|
|
3730
|
+
command: input.claudeCommand,
|
|
3731
|
+
mcpConfigPath: input.mcpConfigPath,
|
|
3732
|
+
idleTimeoutMs: readEnvInt(process.env.BRIDGE_CLAUDE_IDLE_TIMEOUT_MS, 18e4),
|
|
3733
|
+
maxProcessAgeMs: readEnvInt(process.env.BRIDGE_CLAUDE_MAX_PROCESS_AGE_MS, 2 * 60 * 60 * 1e3),
|
|
3734
|
+
maxTurns: readEnvInt(process.env.BRIDGE_CLAUDE_MAX_TURNS, 50)
|
|
3735
|
+
});
|
|
3661
3736
|
return [
|
|
3662
|
-
new ClaudeCodeProvider({ runner:
|
|
3737
|
+
new ClaudeCodeProvider({ runner: claudeRunner }),
|
|
3663
3738
|
new CodexProvider({ runner: new CodexInteractiveRunner({ command: input.codexCommand }) })
|
|
3664
3739
|
];
|
|
3665
3740
|
}
|
|
@@ -4108,6 +4183,13 @@ var MessageRouter = class _MessageRouter {
|
|
|
4108
4183
|
active.abort();
|
|
4109
4184
|
void Promise.resolve(this.providers.get(active.providerId)?.interruptSession?.(active.bridgeSessionId)).catch(() => void 0);
|
|
4110
4185
|
}
|
|
4186
|
+
// 切换/清除当前会话前,回收旧 provider 的持久子进程,防止孤儿堆积。
|
|
4187
|
+
// stopSession 内部是同步 close/kill,不会挂起;fire-and-forget,不阻塞命令链。
|
|
4188
|
+
recycleProviderProcess(session) {
|
|
4189
|
+
if (!session) return;
|
|
4190
|
+
const provider = this.providers.get(session.providerId);
|
|
4191
|
+
void Promise.resolve(provider?.stopSession(session.id)).catch(() => void 0);
|
|
4192
|
+
}
|
|
4111
4193
|
async runChatGeneration(message, user, text, seq) {
|
|
4112
4194
|
const superseded = () => (this.latestMutatingSeq.get(message.chatId) ?? 0) > seq;
|
|
4113
4195
|
if (superseded()) return;
|
|
@@ -4219,7 +4301,7 @@ var MessageRouter = class _MessageRouter {
|
|
|
4219
4301
|
await this.sendToChat({ chatId: message.chatId, kind: "text", text: bufferedText });
|
|
4220
4302
|
bufferedText = "";
|
|
4221
4303
|
}
|
|
4222
|
-
const errorText = `Provider error: ${event.error}`;
|
|
4304
|
+
const errorText = event.code === "idle_timeout" ? "\u26A0\uFE0F \u4E0A\u4E00\u8F6E\u957F\u65F6\u95F4\u65E0\u54CD\u5E94(\u7591\u4F3C\u5DE5\u5177\u5361\u6B7B),\u5DF2\u81EA\u52A8\u7EC8\u6B62\u5E76\u4FDD\u7559\u4F1A\u8BDD\u3002\u76F4\u63A5\u91CD\u53D1\u6D88\u606F\u5373\u53EF\u7EE7\u7EED\u3002" : `Provider error: ${event.error}`;
|
|
4223
4305
|
await this.sendToChat({
|
|
4224
4306
|
chatId: message.chatId,
|
|
4225
4307
|
kind: "status",
|
|
@@ -4272,9 +4354,11 @@ var MessageRouter = class _MessageRouter {
|
|
|
4272
4354
|
}
|
|
4273
4355
|
if (command.kind === "new_session") {
|
|
4274
4356
|
this.pendingChoices.delete(chatId);
|
|
4357
|
+
const previousSession = this.conversation.getCurrent();
|
|
4275
4358
|
await this.preemptActiveGeneration(chatId);
|
|
4276
|
-
|
|
4277
|
-
const
|
|
4359
|
+
this.recycleProviderProcess(previousSession);
|
|
4360
|
+
const providerId = command.providerId ?? previousSession?.providerId ?? this.options.defaults?.defaultProvider ?? "claude-code";
|
|
4361
|
+
const cwd = command.cwd ?? previousSession?.cwd ?? this.options.defaults?.defaultWorkspace ?? defaultWorkspaceDir();
|
|
4278
4362
|
const session = this.conversation.create({
|
|
4279
4363
|
chatId,
|
|
4280
4364
|
ownerUserId: user.id,
|
|
@@ -4346,8 +4430,9 @@ var MessageRouter = class _MessageRouter {
|
|
|
4346
4430
|
await this.sendToChat({ chatId, kind: "status", text: "\u7528\u6CD5\uFF1A/resume <\u7F16\u53F7>" });
|
|
4347
4431
|
return;
|
|
4348
4432
|
}
|
|
4433
|
+
const previousSession = this.conversation.getCurrent();
|
|
4349
4434
|
await this.preemptActiveGeneration(chatId);
|
|
4350
|
-
let providerId =
|
|
4435
|
+
let providerId = previousSession?.providerId ?? this.options.defaults?.defaultProvider ?? "claude-code";
|
|
4351
4436
|
let resolvedId = ref;
|
|
4352
4437
|
if (/^\d+$/.test(ref)) {
|
|
4353
4438
|
const cached = this.sessionListCache.get(chatId);
|
|
@@ -4363,7 +4448,7 @@ var MessageRouter = class _MessageRouter {
|
|
|
4363
4448
|
resolvedId = target;
|
|
4364
4449
|
providerId = cached.providerId;
|
|
4365
4450
|
}
|
|
4366
|
-
const previousCwd =
|
|
4451
|
+
const previousCwd = previousSession?.cwd;
|
|
4367
4452
|
const provider = this.providers.get(providerId);
|
|
4368
4453
|
if (!provider?.attachSession || !provider.listRecoverableSessions) {
|
|
4369
4454
|
await this.sendToChat({ chatId, kind: "status", text: `\u5F53\u524D provider\uFF08${providerId}\uFF09\u4E0D\u652F\u6301\u4F1A\u8BDD\u6062\u590D` });
|
|
@@ -4374,6 +4459,7 @@ var MessageRouter = class _MessageRouter {
|
|
|
4374
4459
|
await this.sendToChat({ chatId, kind: "status", text: `\u627E\u4E0D\u5230\u4F1A\u8BDD ${ref}` });
|
|
4375
4460
|
return;
|
|
4376
4461
|
}
|
|
4462
|
+
this.recycleProviderProcess(previousSession);
|
|
4377
4463
|
const cwdUnresolved = !candidate.cwd;
|
|
4378
4464
|
const attached = await attachProviderSessionToBridge({
|
|
4379
4465
|
conversationStore: this.conversation,
|