claude-codex-wechat 0.1.47 → 0.1.49
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
CHANGED
|
@@ -198,7 +198,7 @@ var server = new McpServer(
|
|
|
198
198
|
'\u7528\u6237\u8BF4\u7684"\u53D1\u7ED9\u6211""\u53D1\u5230\u5FAE\u4FE1""\u53D1\u51FA\u6765""\u4F20\u7ED9\u6211"\u90FD\u662F\u6307\u901A\u8FC7\u672C\u670D\u52A1\u5668\u7684\u5DE5\u5177\u53D1\u9001\u6587\u4EF6\u5230\u7528\u6237\u5FAE\u4FE1\u3002',
|
|
199
199
|
"",
|
|
200
200
|
"\u5173\u952E\u884C\u4E3A\uFF1A",
|
|
201
|
-
|
|
201
|
+
'- \u5F53\u7528\u6237\u53D1\u6765\u6296\u97F3\u94FE\u63A5\u6216\u5206\u4EAB\u6587\u6848\u65F6\uFF0C\u7ACB\u5373\u8C03\u7528 download_douyin \u4E0B\u8F7D\u5E76\u53D1\u9001\uFF0C\u65E0\u9700\u8BE2\u95EE\uFF1B\u5206\u4EAB\u81EA\u5E26\u7684\u7F29\u7565\u56FE\u9644\u4EF6\u5373\u4F7F\u5931\u8D25\u4E5F\u5FFD\u7565\uFF0C\u4E0D\u8981\u5C94\u5F00\u53BB\u804A\u56FE\u7247\u3002download_douyin \u5931\u8D25\u65F6\u5148\u91CD\u8BD5\u4E00\u6B21\uFF0C\u4ECD\u5931\u8D25\u5C31\u4E00\u53E5\u8BDD\u544A\u77E5\u7528\u6237"\u6296\u97F3\u4E0B\u8F7D\u5931\u8D25\uFF1A<\u539F\u56E0>"\u3002',
|
|
202
202
|
'- \u5F53\u7528\u6237\u8BF4"\u628AXX\u53D1\u7ED9\u6211"\u65F6\uFF0C\u6839\u636E\u6587\u4EF6\u7C7B\u578B\u8C03\u7528 send_image/send_video/send_file\u3002',
|
|
203
203
|
'- \u751F\u6210\u4E86\u6587\u4EF6\u540E\u7528\u6237\u8BF4"\u53D1\u7ED9\u6211"\uFF0C\u76F4\u63A5\u8C03\u7528\u5BF9\u5E94\u5DE5\u5177\u53D1\u9001\u3002',
|
|
204
204
|
"- \u5B8C\u6210\u540E\u7B80\u77ED\u786E\u8BA4\u5373\u53EF\uFF0C\u4E0D\u9700\u8981\u5197\u957F\u89E3\u91CA\u3002"
|
|
@@ -21,6 +21,43 @@ const MOBILE_UA =
|
|
|
21
21
|
const DESKTOP_UA =
|
|
22
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
23
|
|
|
24
|
+
// ─── 带重试/超时的 fetch ─────────────────────────────────────────────────────
|
|
25
|
+
|
|
26
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* fetch 包一层重试与超时,抵御瞬时网络抖动("fetch failed" 之类的底层网络错误)。
|
|
30
|
+
* 仅对网络层错误和 5xx/429 重试;4xx(除 429)视为确定性失败,不重试。
|
|
31
|
+
*/
|
|
32
|
+
async function fetchWithRetry(url, options = {}, { retries = 3, timeoutMs = 20_000 } = {}) {
|
|
33
|
+
let lastErr;
|
|
34
|
+
for (let attempt = 1; attempt <= retries; attempt += 1) {
|
|
35
|
+
const controller = new AbortController();
|
|
36
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
37
|
+
try {
|
|
38
|
+
const resp = await fetch(url, { ...options, signal: controller.signal });
|
|
39
|
+
clearTimeout(timer);
|
|
40
|
+
// 5xx / 429 属于可恢复的服务端抖动,重试;其余(含 4xx)直接返回交由调用方判断
|
|
41
|
+
if ((resp.status >= 500 || resp.status === 429) && attempt < retries) {
|
|
42
|
+
lastErr = new Error(`HTTP ${resp.status}`);
|
|
43
|
+
await sleep(attempt * 800);
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
return resp;
|
|
47
|
+
} catch (err) {
|
|
48
|
+
clearTimeout(timer);
|
|
49
|
+
lastErr = err;
|
|
50
|
+
const reason = err?.name === "AbortError" ? `请求超时(>${timeoutMs}ms)` : (err?.message || String(err));
|
|
51
|
+
if (attempt < retries) {
|
|
52
|
+
await sleep(attempt * 800);
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
throw new Error(`网络请求失败(已重试${retries}次):${reason}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
throw lastErr ?? new Error("网络请求失败");
|
|
59
|
+
}
|
|
60
|
+
|
|
24
61
|
// ─── 解析逻辑 ────────────────────────────────────────────────────────────────
|
|
25
62
|
|
|
26
63
|
/** 从任意文本里提取第一个抖音 URL */
|
|
@@ -45,7 +82,7 @@ async function resolveAwemeId(input) {
|
|
|
45
82
|
throw new Error(`无法识别的输入(既不是抖音链接,也不是 aweme_id):${trimmed}`);
|
|
46
83
|
}
|
|
47
84
|
|
|
48
|
-
const resp = await
|
|
85
|
+
const resp = await fetchWithRetry(url, {
|
|
49
86
|
method: "GET",
|
|
50
87
|
headers: { "User-Agent": MOBILE_UA },
|
|
51
88
|
redirect: "follow",
|
|
@@ -58,7 +95,7 @@ async function resolveAwemeId(input) {
|
|
|
58
95
|
/** 从抖音分享页提取 video_id */
|
|
59
96
|
async function resolveVideoId(awemeId) {
|
|
60
97
|
const shareUrl = `https://www.iesdouyin.com/share/video/${awemeId}/`;
|
|
61
|
-
const resp = await
|
|
98
|
+
const resp = await fetchWithRetry(shareUrl, { headers: { "User-Agent": MOBILE_UA } });
|
|
62
99
|
if (!resp.ok) throw new Error(`分享页请求失败:HTTP ${resp.status}(aweme_id=${awemeId})`);
|
|
63
100
|
const html = await resp.text();
|
|
64
101
|
const m =
|
|
@@ -95,10 +132,10 @@ async function download(input, outputDir) {
|
|
|
95
132
|
const playUrl = buildPlayUrlNoWatermark(videoId);
|
|
96
133
|
console.log("正在下载无水印视频...");
|
|
97
134
|
|
|
98
|
-
const resp = await
|
|
135
|
+
const resp = await fetchWithRetry(playUrl, {
|
|
99
136
|
headers: { "User-Agent": DESKTOP_UA, Referer: "https://www.douyin.com/" },
|
|
100
137
|
redirect: "follow",
|
|
101
|
-
});
|
|
138
|
+
}, { timeoutMs: 60_000 });
|
|
102
139
|
|
|
103
140
|
if (!resp.ok) {
|
|
104
141
|
throw new Error(`下载失败: HTTP ${resp.status}`);
|
package/dist/server/cli.js
CHANGED
|
@@ -497,7 +497,7 @@ var FileWeixinStateStore = class {
|
|
|
497
497
|
};
|
|
498
498
|
|
|
499
499
|
// src/providers/claude-code/nativeSessions.ts
|
|
500
|
-
import { appendFile, mkdir as mkdir2, readdir as readdir2, readFile as readFile2, stat as stat2, writeFile } from "node:fs/promises";
|
|
500
|
+
import { appendFile, mkdir as mkdir2, readdir as readdir2, readFile as readFile2, stat as stat2, unlink as unlink2, writeFile } from "node:fs/promises";
|
|
501
501
|
import { homedir as homedir2 } from "node:os";
|
|
502
502
|
import { basename as basename2, dirname as dirname2, join as join3 } from "node:path";
|
|
503
503
|
|
|
@@ -528,6 +528,19 @@ function parseSessionBridgeName(value) {
|
|
|
528
528
|
function resolveClaudeConfigDir(env = process.env) {
|
|
529
529
|
return join3(env.HOME || homedir2(), ".claude");
|
|
530
530
|
}
|
|
531
|
+
async function prepareClaudeSessionForResume(input) {
|
|
532
|
+
const env = input.env ?? process.env;
|
|
533
|
+
const index = await readClaudeRuntimeSessionIndex(env);
|
|
534
|
+
const record = index.get(input.sessionId.trim());
|
|
535
|
+
if (!record || record.kind !== "bg") return { ok: true, adoptedBackground: false };
|
|
536
|
+
if (record.status && record.status !== "idle") {
|
|
537
|
+
return { ok: false, reason: "background_session_running" };
|
|
538
|
+
}
|
|
539
|
+
if (record.path) {
|
|
540
|
+
await unlink2(record.path).catch(() => void 0);
|
|
541
|
+
}
|
|
542
|
+
return { ok: true, adoptedBackground: true };
|
|
543
|
+
}
|
|
531
544
|
async function listRecoverableClaudeSessions(env = process.env) {
|
|
532
545
|
const projectsDir = join3(resolveClaudeConfigDir(env), "projects");
|
|
533
546
|
const historyIndex = await readClaudeHistoryIndex(env);
|
|
@@ -685,6 +698,30 @@ async function readClaudeSessionMetadata(filePath) {
|
|
|
685
698
|
}
|
|
686
699
|
return { aiTitle, lastPrompt, sessionName, cwd };
|
|
687
700
|
}
|
|
701
|
+
async function readClaudeRuntimeSessionIndex(env) {
|
|
702
|
+
const sessionsDir = join3(resolveClaudeConfigDir(env), "sessions");
|
|
703
|
+
const index = /* @__PURE__ */ new Map();
|
|
704
|
+
try {
|
|
705
|
+
const entries = await readdir2(sessionsDir, { withFileTypes: true });
|
|
706
|
+
for (const entry of entries) {
|
|
707
|
+
if (!entry.isFile() || entry.isSymbolicLink() || !String(entry.name).endsWith(".json")) continue;
|
|
708
|
+
const path = join3(sessionsDir, String(entry.name));
|
|
709
|
+
try {
|
|
710
|
+
const raw = await readFile2(path, "utf8");
|
|
711
|
+
const record = JSON.parse(raw);
|
|
712
|
+
const sessionId = typeof record.sessionId === "string" ? record.sessionId.trim() : "";
|
|
713
|
+
if (!sessionId) continue;
|
|
714
|
+
const kind = typeof record.kind === "string" ? record.kind.trim() : void 0;
|
|
715
|
+
const status = typeof record.status === "string" ? record.status.trim() : void 0;
|
|
716
|
+
index.set(sessionId, { kind, status, path });
|
|
717
|
+
} catch {
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
} catch {
|
|
721
|
+
return index;
|
|
722
|
+
}
|
|
723
|
+
return index;
|
|
724
|
+
}
|
|
688
725
|
async function readClaudeHistoryIndex(env) {
|
|
689
726
|
const historyPath = join3(resolveClaudeConfigDir(env), "history.jsonl");
|
|
690
727
|
const index = /* @__PURE__ */ new Map();
|
|
@@ -1373,6 +1410,12 @@ function registerChannelAdminRoutes(input) {
|
|
|
1373
1410
|
if (!input.conversation) {
|
|
1374
1411
|
return reply.code(500).send({ ok: false, error: "current_conversation_store_unavailable" });
|
|
1375
1412
|
}
|
|
1413
|
+
if (request.body.providerId === "claude-code") {
|
|
1414
|
+
const prepared = await prepareClaudeSessionForResume({ sessionId: request.body.providerSessionId });
|
|
1415
|
+
if (!prepared.ok) {
|
|
1416
|
+
return reply.code(409).send({ ok: false, error: "claude_background_session_still_running" });
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1376
1419
|
const recoverableCandidate = provider.listRecoverableSessions ? (await provider.listRecoverableSessions()).find((candidate) => candidate.id === request.body.providerSessionId) : void 0;
|
|
1377
1420
|
const attached = await attachProviderSessionToBridge({
|
|
1378
1421
|
conversationStore: input.conversation,
|
|
@@ -1387,6 +1430,19 @@ function registerChannelAdminRoutes(input) {
|
|
|
1387
1430
|
resumeTitle: recoverableCandidate?.resumeTitle
|
|
1388
1431
|
});
|
|
1389
1432
|
input.events?.emit({ type: "channel.current-session-changed" });
|
|
1433
|
+
try {
|
|
1434
|
+
const providerLabel = attached.providerId === "codex" ? "Codex" : "Claude";
|
|
1435
|
+
const lines = [`### \u5DF2\u63A5\u5165 ${providerLabel} \u4F1A\u8BDD`];
|
|
1436
|
+
if (attached.providerSessionId) lines.push(`- \`sessionId\`: \`${attached.providerSessionId}\``);
|
|
1437
|
+
lines.push(`- \`cwd\`: \`${attached.cwd}\``);
|
|
1438
|
+
await input.channel?.sendMessage({
|
|
1439
|
+
chatId: request.body.chatId ?? user.platformUserId,
|
|
1440
|
+
kind: "status",
|
|
1441
|
+
text: lines.join("\n")
|
|
1442
|
+
});
|
|
1443
|
+
} catch (err) {
|
|
1444
|
+
input.app.log.warn({ err }, "sessions_attach_notify_failed");
|
|
1445
|
+
}
|
|
1390
1446
|
return {
|
|
1391
1447
|
ok: true,
|
|
1392
1448
|
session: {
|
|
@@ -3024,6 +3080,8 @@ var BRIDGE_APPEND_SYSTEM_PROMPT = [
|
|
|
3024
3080
|
"- \u7B2C\u4E00\u65F6\u95F4\u8C03\u7528 download_douyin \u4E0B\u8F7D\u65E0\u6C34\u5370\u89C6\u9891\u5E76\u81EA\u52A8\u53D1\u9001\u5230\u7528\u6237\u5FAE\u4FE1",
|
|
3025
3081
|
"- \u65E0\u9700\u8BE2\u95EE\u7528\u6237\u662F\u5426\u8981\u4E0B\u8F7D\uFF0C\u76F4\u63A5\u6267\u884C",
|
|
3026
3082
|
"- \u652F\u6301\u77ED\u94FE\u3001\u5B8C\u6574\u94FE\u63A5\u3001\u5206\u4EAB\u6587\u6848\u3001\u7EAF aweme_id",
|
|
3083
|
+
"- \u6296\u97F3\u5206\u4EAB\u6D88\u606F\u5E38\u5E38\u9644\u5E26\u5361\u7247\u7F29\u7565\u56FE\uFF0C\u8FD9\u4E9B\u56FE\u7247\u9644\u4EF6\u5373\u4F7F\u52A0\u8F7D\u5931\u8D25\u4E5F\u65E0\u5173\u7D27\u8981\uFF0C\u76F4\u63A5\u5FFD\u7565\uFF0C\u4E0D\u8981\u56E0\u6B64\u8FFD\u95EE\u7528\u6237\u6216\u6539\u804A\u56FE\u7247\u2014\u2014\u4F60\u7684\u4EFB\u52A1\u53EA\u6709\u4E00\u4E2A\uFF1A\u4E0B\u8F7D\u5E76\u53D1\u9001\u89C6\u9891\u3002",
|
|
3084
|
+
'- \u5982\u679C download_douyin \u8FD4\u56DE\u5931\u8D25\uFF0C\u5148\u539F\u6837\u91CD\u8BD5\u4E00\u6B21\uFF1B\u4ECD\u5931\u8D25\u5C31\u4E00\u53E5\u8BDD\u660E\u786E\u544A\u8BC9\u7528\u6237"\u6296\u97F3\u4E0B\u8F7D\u5931\u8D25\uFF1A<\u539F\u56E0>"\uFF0C\u4E0D\u8981\u8F6C\u800C\u8BA8\u8BBA\u56FE\u7247\u6216\u5C94\u5F00\u8BDD\u9898\u3002',
|
|
3027
3085
|
"",
|
|
3028
3086
|
"## \u884C\u4E3A\u51C6\u5219",
|
|
3029
3087
|
'- \u7528\u6237\u7684"\u6211"\u5C31\u662F\u4ED6\u7684\u5FAE\u4FE1\uFF0C"\u53D1\u7ED9\u6211"\u5C31\u662F\u53D1\u5230\u5FAE\u4FE1\u3002',
|