claude-codex-wechat 0.1.35 → 0.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/dist/mcp/scripts/douyin-download.mjs +140 -0
- package/dist/server/cli.js +98 -12
- package/package.json +1 -1
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 抖音无水印视频下载脚本(自包含,无外部依赖)。
|
|
5
|
+
*
|
|
6
|
+
* 用法:
|
|
7
|
+
* node douyin-download.mjs "<抖音链接>"
|
|
8
|
+
* node douyin-download.mjs "<抖音链接>" --output ./videos/
|
|
9
|
+
* node douyin-download.mjs "<分享文案含链接>"
|
|
10
|
+
*
|
|
11
|
+
* 要求:Node.js 18+(需要全局 fetch)
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { writeFileSync, mkdirSync, existsSync } from "node:fs";
|
|
15
|
+
import { resolve } from "node:path";
|
|
16
|
+
|
|
17
|
+
// ─── 常量 ───────────────────────────────────────────────────────────────────
|
|
18
|
+
|
|
19
|
+
const MOBILE_UA =
|
|
20
|
+
"Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1";
|
|
21
|
+
const DESKTOP_UA =
|
|
22
|
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36";
|
|
23
|
+
|
|
24
|
+
// ─── 解析逻辑 ────────────────────────────────────────────────────────────────
|
|
25
|
+
|
|
26
|
+
/** 从任意文本里提取第一个抖音 URL */
|
|
27
|
+
function extractDouyinUrl(text) {
|
|
28
|
+
const m = text.match(
|
|
29
|
+
/https?:\/\/(?:v\.douyin\.com|www\.douyin\.com|www\.iesdouyin\.com|iesdouyin\.com)\/[^\s"'))】]+/i
|
|
30
|
+
);
|
|
31
|
+
return m ? m[0] : null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** 把任意输入解析成 aweme_id */
|
|
35
|
+
async function resolveAwemeId(input) {
|
|
36
|
+
const trimmed = input.trim();
|
|
37
|
+
if (/^\d{15,}$/.test(trimmed)) return trimmed;
|
|
38
|
+
|
|
39
|
+
const url = extractDouyinUrl(trimmed) ?? trimmed;
|
|
40
|
+
|
|
41
|
+
const direct = url.match(/(?:share\/)?video\/(\d{15,})/);
|
|
42
|
+
if (direct) return direct[1];
|
|
43
|
+
|
|
44
|
+
if (!/^https?:\/\//i.test(url)) {
|
|
45
|
+
throw new Error(`无法识别的输入(既不是抖音链接,也不是 aweme_id):${trimmed}`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const resp = await fetch(url, {
|
|
49
|
+
method: "GET",
|
|
50
|
+
headers: { "User-Agent": MOBILE_UA },
|
|
51
|
+
redirect: "follow",
|
|
52
|
+
});
|
|
53
|
+
const m = resp.url.match(/(\d{15,})/);
|
|
54
|
+
if (!m) throw new Error(`短链重定向后未找到 aweme_id,最终地址:${resp.url}`);
|
|
55
|
+
return m[1];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** 从抖音分享页提取 video_id */
|
|
59
|
+
async function resolveVideoId(awemeId) {
|
|
60
|
+
const shareUrl = `https://www.iesdouyin.com/share/video/${awemeId}/`;
|
|
61
|
+
const resp = await fetch(shareUrl, { headers: { "User-Agent": MOBILE_UA } });
|
|
62
|
+
if (!resp.ok) throw new Error(`分享页请求失败:HTTP ${resp.status}(aweme_id=${awemeId})`);
|
|
63
|
+
const html = await resp.text();
|
|
64
|
+
const m =
|
|
65
|
+
html.match(/video_id=([a-z0-9]+)/i) ??
|
|
66
|
+
html.match(/"video_id"\s*:\s*"([a-z0-9]+)"/i) ??
|
|
67
|
+
html.match(/playwm\/\?[^"'\\]*?video_id=([a-z0-9]+)/i);
|
|
68
|
+
if (!m) throw new Error(`分享页里未找到 video_id(aweme_id=${awemeId})`);
|
|
69
|
+
return m[1];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** 拼无水印播放入口(play 而非 playwm) */
|
|
73
|
+
function buildPlayUrlNoWatermark(videoId, ratio = "720p") {
|
|
74
|
+
return `https://aweme.snssdk.com/aweme/v1/play/?ratio=${ratio}&video_id=${videoId}`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ─── 下载逻辑 ────────────────────────────────────────────────────────────────
|
|
78
|
+
|
|
79
|
+
async function download(input, outputDir) {
|
|
80
|
+
console.log("正在解析...");
|
|
81
|
+
|
|
82
|
+
const trimmed = input.trim();
|
|
83
|
+
let awemeId = "";
|
|
84
|
+
let videoId = "";
|
|
85
|
+
|
|
86
|
+
if (/^v0[a-z0-9]{10,}$/i.test(trimmed)) {
|
|
87
|
+
videoId = trimmed;
|
|
88
|
+
} else {
|
|
89
|
+
awemeId = await resolveAwemeId(trimmed);
|
|
90
|
+
videoId = await resolveVideoId(awemeId);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
console.log(`aweme_id: ${awemeId || "-"} video_id: ${videoId}`);
|
|
94
|
+
|
|
95
|
+
const playUrl = buildPlayUrlNoWatermark(videoId);
|
|
96
|
+
console.log("正在下载无水印视频...");
|
|
97
|
+
|
|
98
|
+
const resp = await fetch(playUrl, {
|
|
99
|
+
headers: { "User-Agent": DESKTOP_UA, Referer: "https://www.douyin.com/" },
|
|
100
|
+
redirect: "follow",
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
if (!resp.ok) {
|
|
104
|
+
throw new Error(`下载失败: HTTP ${resp.status}`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const buf = Buffer.from(await resp.arrayBuffer());
|
|
108
|
+
const filename = `douyin_${awemeId || videoId}.mp4`;
|
|
109
|
+
|
|
110
|
+
const dir = outputDir ? resolve(outputDir) : process.cwd();
|
|
111
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
112
|
+
|
|
113
|
+
const filepath = resolve(dir, filename);
|
|
114
|
+
writeFileSync(filepath, buf);
|
|
115
|
+
|
|
116
|
+
const sizeMB = (buf.length / 1024 / 1024).toFixed(2);
|
|
117
|
+
console.log(`✅ 已保存: ${filepath} (${sizeMB} MB)`);
|
|
118
|
+
return filepath;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// ─── CLI 入口 ─────────────────────────────────────────────────────────────────
|
|
122
|
+
|
|
123
|
+
const argv = process.argv.slice(2);
|
|
124
|
+
const outputIdx = argv.indexOf("--output");
|
|
125
|
+
let outputDir;
|
|
126
|
+
if (outputIdx !== -1) {
|
|
127
|
+
outputDir = argv[outputIdx + 1];
|
|
128
|
+
argv.splice(outputIdx, 2);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const input = argv.join(" ").trim();
|
|
132
|
+
if (!input) {
|
|
133
|
+
console.error('用法: node douyin-download.mjs "<抖音链接>" [--output <目录>]');
|
|
134
|
+
process.exit(2);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
download(input, outputDir).catch((error) => {
|
|
138
|
+
console.error("❌ 错误:", error.message || error);
|
|
139
|
+
process.exit(1);
|
|
140
|
+
});
|
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,
|