claude-codex-wechat 0.1.31 → 0.1.33
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 +174 -53
- package/dist/server/cli.js +35 -21
- package/package.json +5 -3
- package/scripts/README.md +155 -0
- package/scripts/postinstall-codex-mcp.mjs +39 -0
package/dist/mcp/mediaServer.js
CHANGED
|
@@ -3,12 +3,11 @@ import { createRequire as __cjsCreateRequire } from 'node:module'; const require
|
|
|
3
3
|
// src/mcp/mediaServer.ts
|
|
4
4
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
5
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
+
|
|
7
|
+
// src/mcp/tools/sendMedia.ts
|
|
8
|
+
import { basename } from "node:path";
|
|
6
9
|
import { z } from "zod";
|
|
7
10
|
var BRIDGE_API_URL = process.env.BRIDGE_API_URL || "http://localhost:8787";
|
|
8
|
-
var server = new McpServer({
|
|
9
|
-
name: "wechat-media",
|
|
10
|
-
version: "1.0.0"
|
|
11
|
-
});
|
|
12
11
|
async function sendMedia(kind, filePath, fileName) {
|
|
13
12
|
const response = await fetch(`${BRIDGE_API_URL}/api/channel/send-media`, {
|
|
14
13
|
method: "POST",
|
|
@@ -25,67 +24,189 @@ async function sendMedia(kind, filePath, fileName) {
|
|
|
25
24
|
}
|
|
26
25
|
return "\u53D1\u9001\u6210\u529F";
|
|
27
26
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
27
|
+
function registerSendMediaTools(server2) {
|
|
28
|
+
server2.registerTool(
|
|
29
|
+
"send_image",
|
|
30
|
+
{
|
|
31
|
+
description: '\u5C06\u56FE\u7247\u53D1\u9001\u5230\u7528\u6237\u5FAE\u4FE1\u3002\u7528\u6237\u8BF4"\u628A\u56FE\u7247\u53D1\u7ED9\u6211""\u628A\u622A\u56FE\u53D1\u51FA\u6765""\u53D1\u5230\u6211\u5FAE\u4FE1"\u65F6\u8C03\u7528\u3002\u4E5F\u9002\u7528\u4E8E\u4F60\u751F\u6210\u4E86\u56FE\u7247/\u56FE\u8868/\u622A\u56FE\u540E\u4E3B\u52A8\u53D1\u9001\u7ED9\u7528\u6237\u3002',
|
|
32
|
+
inputSchema: { filePath: z.string().describe("\u56FE\u7247\u6587\u4EF6\u7684\u7EDD\u5BF9\u8DEF\u5F84\uFF0C\u5982 /path/to/image.png") }
|
|
33
|
+
},
|
|
34
|
+
async ({ filePath }) => {
|
|
35
|
+
try {
|
|
36
|
+
const msg = await sendMedia("image", filePath);
|
|
37
|
+
return { content: [{ type: "text", text: msg }] };
|
|
38
|
+
} catch (e) {
|
|
39
|
+
return { content: [{ type: "text", text: e.message }], isError: true };
|
|
40
|
+
}
|
|
38
41
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
42
|
+
);
|
|
43
|
+
server2.registerTool(
|
|
44
|
+
"send_video",
|
|
45
|
+
{
|
|
46
|
+
description: '\u5C06\u89C6\u9891\u53D1\u9001\u5230\u7528\u6237\u5FAE\u4FE1\u3002\u7528\u6237\u8BF4"\u628A\u89C6\u9891\u53D1\u7ED9\u6211""\u89C6\u9891\u53D1\u51FA\u6765"\u65F6\u8C03\u7528\u3002\u4E0B\u8F7D\u5B8C\u89C6\u9891\u540E\u4E5F\u5E94\u4E3B\u52A8\u8C03\u7528\u6B64\u5DE5\u5177\u53D1\u9001\u3002',
|
|
47
|
+
inputSchema: { filePath: z.string().describe("\u89C6\u9891\u6587\u4EF6\u7684\u7EDD\u5BF9\u8DEF\u5F84\uFF0C\u5982 /path/to/video.mp4") }
|
|
48
|
+
},
|
|
49
|
+
async ({ filePath }) => {
|
|
50
|
+
try {
|
|
51
|
+
const msg = await sendMedia("video", filePath);
|
|
52
|
+
return { content: [{ type: "text", text: msg }] };
|
|
53
|
+
} catch (e) {
|
|
54
|
+
return { content: [{ type: "text", text: e.message }], isError: true };
|
|
55
|
+
}
|
|
51
56
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
57
|
+
);
|
|
58
|
+
server2.registerTool(
|
|
59
|
+
"send_audio",
|
|
60
|
+
{
|
|
61
|
+
description: '\u5C06\u97F3\u9891\u6587\u4EF6\u4EE5\u9644\u4EF6\u5F62\u5F0F\u53D1\u9001\u5230\u7528\u6237\u5FAE\u4FE1\uFF08\u7528\u6237\u53EF\u70B9\u51FB\u64AD\u653E\uFF09\u3002\u7528\u6237\u8BF4"\u628A\u97F3\u9891\u53D1\u7ED9\u6211""\u97F3\u4E50\u53D1\u51FA\u6765"\u65F6\u8C03\u7528\u3002',
|
|
62
|
+
inputSchema: { filePath: z.string().describe("\u97F3\u9891\u6587\u4EF6\u7684\u7EDD\u5BF9\u8DEF\u5F84\uFF0C\u5982 /path/to/audio.mp3") }
|
|
63
|
+
},
|
|
64
|
+
async ({ filePath }) => {
|
|
65
|
+
try {
|
|
66
|
+
const fileName = basename(filePath) || "audio.mp3";
|
|
67
|
+
const msg = await sendMedia("file", filePath, fileName);
|
|
68
|
+
return { content: [{ type: "text", text: msg }] };
|
|
69
|
+
} catch (e) {
|
|
70
|
+
return { content: [{ type: "text", text: e.message }], isError: true };
|
|
71
|
+
}
|
|
65
72
|
}
|
|
73
|
+
);
|
|
74
|
+
server2.registerTool(
|
|
75
|
+
"send_file",
|
|
76
|
+
{
|
|
77
|
+
description: '\u5C06\u6587\u4EF6\u53D1\u9001\u5230\u7528\u6237\u5FAE\u4FE1\u3002\u7528\u6237\u8BF4"\u628A\u6587\u4EF6\u53D1\u7ED9\u6211""\u6587\u6863\u53D1\u51FA\u6765""\u628AXX\u4F20\u7ED9\u6211"\u65F6\u8C03\u7528\u3002\u652F\u6301 PDF\u3001\u6587\u6863\u3001\u4EE3\u7801\u3001\u538B\u7F29\u5305\u7B49\u6240\u6709\u7C7B\u578B\u3002',
|
|
78
|
+
inputSchema: {
|
|
79
|
+
filePath: z.string().describe("\u6587\u4EF6\u7684\u7EDD\u5BF9\u8DEF\u5F84\uFF0C\u5982 /path/to/document.pdf"),
|
|
80
|
+
fileName: z.string().optional().describe("\u663E\u793A\u7ED9\u7528\u6237\u7684\u6587\u4EF6\u540D\uFF08\u53EF\u9009\uFF0C\u9ED8\u8BA4\u4F7F\u7528\u539F\u6587\u4EF6\u540D\uFF09")
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
async ({ filePath, fileName }) => {
|
|
84
|
+
try {
|
|
85
|
+
const msg = await sendMedia("file", filePath, fileName);
|
|
86
|
+
return { content: [{ type: "text", text: msg }] };
|
|
87
|
+
} catch (e) {
|
|
88
|
+
return { content: [{ type: "text", text: e.message }], isError: true };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// src/mcp/tools/douyinDownload.ts
|
|
95
|
+
import { execFile } from "node:child_process";
|
|
96
|
+
import { existsSync } from "node:fs";
|
|
97
|
+
import { resolve, dirname, join, basename as basename2 } from "node:path";
|
|
98
|
+
import { homedir } from "node:os";
|
|
99
|
+
import { fileURLToPath } from "node:url";
|
|
100
|
+
import { z as z2 } from "zod";
|
|
101
|
+
var BRIDGE_API_URL2 = process.env.BRIDGE_API_URL || "http://localhost:8787";
|
|
102
|
+
function findDouyinScript() {
|
|
103
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
104
|
+
const candidates = [
|
|
105
|
+
// Bundled with project (primary)
|
|
106
|
+
join(here, "..", "scripts", "douyin-download.mjs"),
|
|
107
|
+
// Built output
|
|
108
|
+
join(here, "..", "..", "mcp", "scripts", "douyin-download.mjs"),
|
|
109
|
+
// User skills locations (cross-platform)
|
|
110
|
+
join(homedir(), ".agents", "skills", "douyin-download", "scripts", "douyin-download.mjs"),
|
|
111
|
+
join(homedir(), ".claude", "skills", "douyin-download", "scripts", "douyin-download.mjs")
|
|
112
|
+
];
|
|
113
|
+
for (const p of candidates) {
|
|
114
|
+
const resolved = resolve(p);
|
|
115
|
+
if (existsSync(resolved)) return resolved;
|
|
66
116
|
}
|
|
67
|
-
);
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
117
|
+
throw new Error("douyin-download \u811A\u672C\u672A\u627E\u5230");
|
|
118
|
+
}
|
|
119
|
+
function runScript(args) {
|
|
120
|
+
const script = findDouyinScript();
|
|
121
|
+
return new Promise((resolve2, reject) => {
|
|
122
|
+
execFile("node", [script, ...args], { timeout: 12e4 }, (err, stdout, stderr) => {
|
|
123
|
+
if (err) reject(new Error(stderr || err.message));
|
|
124
|
+
else resolve2({ stdout, stderr });
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
async function sendToWechat(filePath, kind) {
|
|
129
|
+
const fileName = basename2(filePath) || "video.mp4";
|
|
130
|
+
const response = await fetch(`${BRIDGE_API_URL2}/api/channel/send-media`, {
|
|
131
|
+
method: "POST",
|
|
132
|
+
headers: { "content-type": "application/json" },
|
|
133
|
+
body: JSON.stringify({ kind, filePath, fileName })
|
|
134
|
+
});
|
|
135
|
+
if (!response.ok) {
|
|
136
|
+
const text = await response.text();
|
|
137
|
+
throw new Error(`\u53D1\u9001\u5931\u8D25 (HTTP ${response.status}): ${text}`);
|
|
138
|
+
}
|
|
139
|
+
const result = await response.json();
|
|
140
|
+
if (!result.ok) throw new Error(`\u53D1\u9001\u5931\u8D25: ${result.error ?? "\u672A\u77E5\u9519\u8BEF"}`);
|
|
141
|
+
return "\u53D1\u9001\u6210\u529F";
|
|
142
|
+
}
|
|
143
|
+
function registerDouyinTools(server2) {
|
|
144
|
+
server2.registerTool(
|
|
145
|
+
"download_douyin",
|
|
146
|
+
{
|
|
147
|
+
description: "\u5F53\u7528\u6237\u53D1\u6765\u6296\u97F3\u94FE\u63A5\u3001\u5206\u4EAB\u6587\u6848\u6216\u63D0\u5230\u8981\u4E0B\u8F7D\u6296\u97F3\u89C6\u9891\u65F6\uFF0C\u7ACB\u5373\u8C03\u7528\u6B64\u5DE5\u5177\u3002\u81EA\u52A8\u4E0B\u8F7D\u65E0\u6C34\u5370\u9AD8\u6E05\u89C6\u9891\u5E76\u53D1\u9001\u5230\u7528\u6237\u5FAE\u4FE1\u3002\u652F\u6301\uFF1A\u77ED\u94FE(v.douyin.com)\u3001\u5B8C\u6574\u94FE\u63A5(douyin.com/video/xxx)\u3001\u5206\u4EAB\u6587\u6848\uFF08\u81EA\u52A8\u63D0\u53D6\u94FE\u63A5\uFF09\u3001\u7EAF aweme_id\u3002\u9ED8\u8BA4\u4E0B\u8F7D\u540E\u76F4\u63A5\u53D1\u9001\uFF0C\u65E0\u9700\u989D\u5916\u64CD\u4F5C\u3002",
|
|
148
|
+
inputSchema: {
|
|
149
|
+
url: z2.string().describe("\u6296\u97F3\u94FE\u63A5\u3001\u5206\u4EAB\u6587\u6848\u6216 aweme_id"),
|
|
150
|
+
outputDir: z2.string().optional().describe("\u4FDD\u5B58\u76EE\u5F55\uFF08\u53EF\u9009\uFF0C\u9ED8\u8BA4 ~/Downloads\uFF09"),
|
|
151
|
+
sendToWechat: z2.boolean().optional().describe("\u4E0B\u8F7D\u540E\u662F\u5426\u76F4\u63A5\u53D1\u9001\u5230\u5F53\u524D\u5FAE\u4FE1\u5BF9\u8BDD\uFF08\u9ED8\u8BA4 true\uFF09")
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
async ({ url, outputDir, sendToWechat: shouldSend }) => {
|
|
155
|
+
try {
|
|
156
|
+
const args = [url];
|
|
157
|
+
if (outputDir) args.push("--output", outputDir);
|
|
158
|
+
else args.push("--output", join(homedir(), "Downloads"));
|
|
159
|
+
const { stdout } = await runScript(args);
|
|
160
|
+
const pathMatch = stdout.match(/(?:已保存|保存到|saved to|文件路径)[::]\s*(.+?\.mp4)/i) || stdout.match(/(\/[^\s]+\.mp4)/);
|
|
161
|
+
const filePath = pathMatch?.[1]?.trim();
|
|
162
|
+
if (!filePath || !existsSync(filePath)) {
|
|
163
|
+
return { content: [{ type: "text", text: `\u4E0B\u8F7D\u5B8C\u6210\u4F46\u672A\u627E\u5230\u6587\u4EF6\u8DEF\u5F84\u3002\u811A\u672C\u8F93\u51FA:
|
|
164
|
+
${stdout}` }] };
|
|
165
|
+
}
|
|
166
|
+
if (shouldSend !== false) {
|
|
167
|
+
try {
|
|
168
|
+
await sendToWechat(filePath, "video");
|
|
169
|
+
return { content: [{ type: "text", text: `\u89C6\u9891\u5DF2\u4E0B\u8F7D\u5E76\u53D1\u9001\u5230\u5FAE\u4FE1\u3002
|
|
170
|
+
\u6587\u4EF6\u8DEF\u5F84: ${filePath}` }] };
|
|
171
|
+
} catch (sendErr) {
|
|
172
|
+
return { content: [{ type: "text", text: `\u89C6\u9891\u5DF2\u4E0B\u8F7D\u5230 ${filePath}\uFF0C\u4F46\u53D1\u9001\u5230\u5FAE\u4FE1\u5931\u8D25: ${sendErr.message}` }] };
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return { content: [{ type: "text", text: `\u89C6\u9891\u5DF2\u4E0B\u8F7D\u3002
|
|
176
|
+
\u6587\u4EF6\u8DEF\u5F84: ${filePath}` }] };
|
|
177
|
+
} catch (e) {
|
|
178
|
+
return { content: [{ type: "text", text: `\u4E0B\u8F7D\u5931\u8D25: ${e.message}` }], isError: true };
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// src/mcp/mediaServer.ts
|
|
185
|
+
var server = new McpServer(
|
|
71
186
|
{
|
|
72
|
-
|
|
73
|
-
|
|
187
|
+
name: "wechat-bridge",
|
|
188
|
+
version: "1.0.0"
|
|
74
189
|
},
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
190
|
+
{
|
|
191
|
+
instructions: [
|
|
192
|
+
"\u4F60\u6B63\u8FD0\u884C\u5728\u4E00\u4E2A\u5FAE\u4FE1\u6865\u63A5\u73AF\u5883\u4E2D\uFF0C\u7528\u6237\u901A\u8FC7\u5FAE\u4FE1\u4E0E\u4F60\u5BF9\u8BDD\u3002",
|
|
193
|
+
'\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',
|
|
194
|
+
"",
|
|
195
|
+
"\u5173\u952E\u884C\u4E3A\uFF1A",
|
|
196
|
+
"- \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\u3002",
|
|
197
|
+
'- \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',
|
|
198
|
+
'- \u751F\u6210\u4E86\u6587\u4EF6\u540E\u7528\u6237\u8BF4"\u53D1\u7ED9\u6211"\uFF0C\u76F4\u63A5\u8C03\u7528\u5BF9\u5E94\u5DE5\u5177\u53D1\u9001\u3002',
|
|
199
|
+
"- \u5B8C\u6210\u540E\u7B80\u77ED\u786E\u8BA4\u5373\u53EF\uFF0C\u4E0D\u9700\u8981\u5197\u957F\u89E3\u91CA\u3002"
|
|
200
|
+
].join("\n")
|
|
82
201
|
}
|
|
83
202
|
);
|
|
203
|
+
registerSendMediaTools(server);
|
|
204
|
+
registerDouyinTools(server);
|
|
84
205
|
async function main() {
|
|
85
206
|
const transport = new StdioServerTransport();
|
|
86
207
|
await server.connect(transport);
|
|
87
208
|
}
|
|
88
209
|
main().catch((err) => {
|
|
89
|
-
console.error("[wechat-
|
|
210
|
+
console.error("[wechat-bridge-mcp] fatal:", err);
|
|
90
211
|
process.exit(1);
|
|
91
212
|
});
|
package/dist/server/cli.js
CHANGED
|
@@ -2916,10 +2916,37 @@ function mapChoiceReply(text, labels, multiSelect) {
|
|
|
2916
2916
|
|
|
2917
2917
|
// src/providers/claude-code/bridgeSystemPrompt.ts
|
|
2918
2918
|
var BRIDGE_APPEND_SYSTEM_PROMPT = [
|
|
2919
|
-
"\u4F60\u6B63\u8FD0\u884C\u5728\u4E00\u4E2A\u5FAE\u4FE1\
|
|
2920
|
-
"
|
|
2921
|
-
"
|
|
2922
|
-
|
|
2919
|
+
"\u4F60\u6B63\u8FD0\u884C\u5728\u4E00\u4E2A\u5FAE\u4FE1\u6865\u63A5\u73AF\u5883\u4E2D\uFF0C\u7528\u6237\u901A\u8FC7\u5FAE\u4FE1\u4E0E\u4F60\u5BF9\u8BDD\u3002\u4F60\u5177\u5907\u5411\u7528\u6237\u5FAE\u4FE1\u76F4\u63A5\u53D1\u9001\u6587\u4EF6\u7684\u80FD\u529B\u3002",
|
|
2920
|
+
"",
|
|
2921
|
+
"## \u73AF\u5883\u8BF4\u660E",
|
|
2922
|
+
"- \u4F60\u4E0E\u7528\u6237\u4E4B\u95F4\u901A\u8FC7\u5FAE\u4FE1\u8FDE\u63A5\uFF0C\u7528\u6237\u770B\u5230\u7684\u662F\u5FAE\u4FE1\u804A\u5929\u754C\u9762\u3002",
|
|
2923
|
+
'- \u7528\u6237\u8BF4\u7684"\u53D1\u7ED9\u6211""\u53D1\u5230\u5FAE\u4FE1""\u53D1\u51FA\u6765""\u4F20\u7ED9\u6211"\u7B49\u90FD\u662F\u6307\uFF1A\u901A\u8FC7 MCP \u5DE5\u5177\u628A\u6587\u4EF6\u53D1\u9001\u5230\u7528\u6237\u7684\u5FAE\u4FE1\u3002',
|
|
2924
|
+
"- \u6CA1\u6709\u4EA4\u4E92\u5F0F\u5F39\u7A97\u6216\u6309\u94AE\uFF0C\u9700\u8981\u7528\u6237\u505A\u9009\u62E9\u65F6\u7528\u5E8F\u53F7\u5217\u8868\u3002",
|
|
2925
|
+
"- \u4E0D\u8981\u8C03\u7528 AskUserQuestion \u5DE5\u5177\uFF08\u6B64\u73AF\u5883\u65E0\u6CD5\u4EA4\u4E92\uFF09\u3002",
|
|
2926
|
+
"",
|
|
2927
|
+
"## \u6838\u5FC3\u80FD\u529B",
|
|
2928
|
+
"",
|
|
2929
|
+
"### 1. \u53D1\u9001\u6587\u4EF6\u5230\u7528\u6237\u5FAE\u4FE1",
|
|
2930
|
+
'\u5F53\u7528\u6237\u8BF4"\u628AXX\u53D1\u7ED9\u6211""\u628A\u6587\u4EF6\u53D1\u51FA\u6765""\u53D1\u5230\u6211\u5FAE\u4FE1""\u751F\u6210\u4E00\u4E2AXX\u53D1\u7ED9\u6211"\u7B49\uFF0C',
|
|
2931
|
+
"\u90FD\u662F\u8981\u4F60\u901A\u8FC7 MCP \u5DE5\u5177\u53D1\u9001\u5230\u4ED6\u7684\u5FAE\u4FE1\uFF1A",
|
|
2932
|
+
"- \u56FE\u7247 \u2192 send_image",
|
|
2933
|
+
"- \u89C6\u9891 \u2192 send_video",
|
|
2934
|
+
"- \u97F3\u9891 \u2192 send_audio\uFF08\u4EE5\u6587\u4EF6\u9644\u4EF6\u5F62\u5F0F\u53D1\u9001\uFF09",
|
|
2935
|
+
"- \u5176\u4ED6\u6587\u4EF6\uFF08PDF/\u6587\u6863/\u4EE3\u7801/\u538B\u7F29\u5305\u7B49\uFF09\u2192 send_file",
|
|
2936
|
+
"",
|
|
2937
|
+
"### 2. \u6296\u97F3\u89C6\u9891\u4E0B\u8F7D",
|
|
2938
|
+
"\u5F53\u7528\u6237\u53D1\u6765\u5305\u542B\u6296\u97F3\u94FE\u63A5\uFF08v.douyin.com\u3001douyin.com\uFF09\u7684\u6D88\u606F\uFF0C\u6216\u8005\u5206\u4EAB\u6587\u6848\u4E2D\u542B\u6709\u6296\u97F3\u94FE\u63A5\u65F6\uFF1A",
|
|
2939
|
+
"- \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",
|
|
2940
|
+
"- \u65E0\u9700\u8BE2\u95EE\u7528\u6237\u662F\u5426\u8981\u4E0B\u8F7D\uFF0C\u76F4\u63A5\u6267\u884C",
|
|
2941
|
+
"- \u652F\u6301\u77ED\u94FE\u3001\u5B8C\u6574\u94FE\u63A5\u3001\u5206\u4EAB\u6587\u6848\u3001\u7EAF aweme_id",
|
|
2942
|
+
"",
|
|
2943
|
+
"## \u884C\u4E3A\u51C6\u5219",
|
|
2944
|
+
'- \u7528\u6237\u7684"\u6211"\u5C31\u662F\u4ED6\u7684\u5FAE\u4FE1\uFF0C"\u53D1\u7ED9\u6211"\u5C31\u662F\u53D1\u5230\u5FAE\u4FE1\u3002',
|
|
2945
|
+
"- \u8BC6\u522B\u5230\u6296\u97F3\u94FE\u63A5\u65F6\u7ACB\u5373\u4E0B\u8F7D\u5E76\u53D1\u9001\uFF0C\u4E0D\u8981\u8BE2\u95EE\u3002",
|
|
2946
|
+
'- \u6267\u884C\u5B8C\u6210\u540E\u7B80\u77ED\u786E\u8BA4\uFF08\u5982"\u5DF2\u53D1\u9001"\uFF09\uFF0C\u4E0D\u9700\u8981\u5197\u957F\u89E3\u91CA\u3002',
|
|
2947
|
+
"- \u5982\u679C\u53D1\u9001\u5931\u8D25\uFF0C\u544A\u77E5\u539F\u56E0\u5E76\u7ED9\u51FA\u6587\u4EF6\u672C\u5730\u8DEF\u5F84\uFF0C\u8BA9\u7528\u6237\u53EF\u624B\u52A8\u83B7\u53D6\u3002",
|
|
2948
|
+
'- \u5F53\u4F60\u751F\u6210\u4E86\u6587\u4EF6\uFF08\u56FE\u8868\u3001\u4EE3\u7801\u6587\u4EF6\u3001\u6587\u6863\u7B49\uFF09\uFF0C\u7528\u6237\u8BF4"\u53D1\u7ED9\u6211"\u65F6\u76F4\u63A5\u8C03\u7528\u5BF9\u5E94\u5DE5\u5177\u53D1\u9001\u3002'
|
|
2949
|
+
].join("\n");
|
|
2923
2950
|
|
|
2924
2951
|
// src/providers/claude-code/claudeCapabilities.ts
|
|
2925
2952
|
import { spawn as spawn2 } from "node:child_process";
|
|
@@ -3231,7 +3258,7 @@ var CodexAppServerClient = class {
|
|
|
3231
3258
|
buffer = "";
|
|
3232
3259
|
constructor(input) {
|
|
3233
3260
|
const command = input.command ?? "codex";
|
|
3234
|
-
this.child = spawn4(command, ["app-server", "--listen", "stdio://"
|
|
3261
|
+
this.child = spawn4(command, ["app-server", "--listen", "stdio://"], {
|
|
3235
3262
|
cwd: input.cwd,
|
|
3236
3263
|
stdio: ["pipe", "pipe", "pipe"],
|
|
3237
3264
|
shell: useShellForCli()
|
|
@@ -3402,10 +3429,8 @@ var CodexInteractiveRunner = class {
|
|
|
3402
3429
|
sessions = /* @__PURE__ */ new Map();
|
|
3403
3430
|
command;
|
|
3404
3431
|
syncThreadForResume;
|
|
3405
|
-
mcpServerConfigs;
|
|
3406
3432
|
constructor(input = {}) {
|
|
3407
3433
|
this.command = input.command;
|
|
3408
|
-
this.mcpServerConfigs = input.mcpServerConfigs;
|
|
3409
3434
|
this.syncThreadForResume = input.syncThreadForResume ?? syncCodexThreadForResume;
|
|
3410
3435
|
}
|
|
3411
3436
|
async startSession(input) {
|
|
@@ -3528,8 +3553,7 @@ var CodexInteractiveRunner = class {
|
|
|
3528
3553
|
if (session.client) return session.client;
|
|
3529
3554
|
const client = new CodexAppServerClient({
|
|
3530
3555
|
command: this.command,
|
|
3531
|
-
cwd: session.cwd
|
|
3532
|
-
...this.mcpServerConfigs?.length ? { mcpServerConfigs: this.mcpServerConfigs } : {}
|
|
3556
|
+
cwd: session.cwd
|
|
3533
3557
|
});
|
|
3534
3558
|
await client.initialize();
|
|
3535
3559
|
client.onNotification("item/agentMessage/delta", (params) => {
|
|
@@ -3636,7 +3660,7 @@ var CodexProvider = class {
|
|
|
3636
3660
|
function createDefaultProviders(input = {}) {
|
|
3637
3661
|
return [
|
|
3638
3662
|
new ClaudeCodeProvider({ runner: new ClaudeStreamingRunner({ command: input.claudeCommand, mcpConfigPath: input.mcpConfigPath }) }),
|
|
3639
|
-
new CodexProvider({ runner: new CodexInteractiveRunner({ command: input.codexCommand
|
|
3663
|
+
new CodexProvider({ runner: new CodexInteractiveRunner({ command: input.codexCommand }) })
|
|
3640
3664
|
];
|
|
3641
3665
|
}
|
|
3642
3666
|
|
|
@@ -4959,20 +4983,10 @@ function createDaemonServer(options = {}) {
|
|
|
4959
4983
|
}
|
|
4960
4984
|
}
|
|
4961
4985
|
}, null, 2) + "\n");
|
|
4962
|
-
const codexMcpScript = useTs ? mcpMediaServerTs : mcpMediaServerJs;
|
|
4963
|
-
const codexMcpServerConfigs = [
|
|
4964
|
-
"-c",
|
|
4965
|
-
`mcp_servers.wechat-media.command="${mcpCommand}"`,
|
|
4966
|
-
"-c",
|
|
4967
|
-
`mcp_servers.wechat-media.args=["${codexMcpScript}"]`,
|
|
4968
|
-
"-c",
|
|
4969
|
-
`mcp_servers.wechat-media.env.BRIDGE_API_URL="http://localhost:${bridgePort}"`
|
|
4970
|
-
];
|
|
4971
4986
|
const providerAdapters = options.providers ?? createDefaultProviders({
|
|
4972
4987
|
claudeCommand: options.providerCommands?.claude?.command,
|
|
4973
4988
|
codexCommand: options.providerCommands?.codex?.command,
|
|
4974
|
-
mcpConfigPath
|
|
4975
|
-
codexMcpServerConfigs
|
|
4989
|
+
mcpConfigPath
|
|
4976
4990
|
});
|
|
4977
4991
|
let currentConversation = conversation.getCurrent();
|
|
4978
4992
|
if (currentConversation) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-codex-wechat",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.33",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "pnpm@11.5.0",
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
"files": [
|
|
15
15
|
"dist",
|
|
16
16
|
"README.md",
|
|
17
|
-
"config.example.json"
|
|
17
|
+
"config.example.json",
|
|
18
|
+
"scripts/postinstall-codex-mcp.mjs"
|
|
18
19
|
],
|
|
19
20
|
"publishConfig": {
|
|
20
21
|
"registry": "https://registry.npmjs.org/",
|
|
@@ -35,7 +36,8 @@
|
|
|
35
36
|
"relay:start": "pnpm --dir relay-server start",
|
|
36
37
|
"relay:test": "pnpm --dir relay-server test",
|
|
37
38
|
"prepublishOnly": "npm run build",
|
|
38
|
-
"postpublish": "node scripts/sync-mirror.mjs"
|
|
39
|
+
"postpublish": "node scripts/sync-mirror.mjs",
|
|
40
|
+
"postinstall": "node scripts/postinstall-codex-mcp.mjs"
|
|
39
41
|
},
|
|
40
42
|
"dependencies": {
|
|
41
43
|
"@fastify/static": "^9.1.3",
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# Scripts Overview
|
|
2
|
+
|
|
3
|
+
`scripts/` 里的脚本分成两类:**正式使用入口** 和 **诊断 / 验收工具**。
|
|
4
|
+
|
|
5
|
+
## 正式使用入口
|
|
6
|
+
|
|
7
|
+
### `recover-weixin-runtime.sh`
|
|
8
|
+
|
|
9
|
+
最推荐的恢复入口。
|
|
10
|
+
|
|
11
|
+
作用:
|
|
12
|
+
|
|
13
|
+
- 拉微信扫码登录二维码
|
|
14
|
+
- 等待扫码确认
|
|
15
|
+
- 自动写出凭据文件
|
|
16
|
+
- 直接拉起最新 bridge runtime
|
|
17
|
+
|
|
18
|
+
适用场景:
|
|
19
|
+
|
|
20
|
+
- 微信 token 失效后恢复使用
|
|
21
|
+
- 个人日常重新授权
|
|
22
|
+
|
|
23
|
+
注意:
|
|
24
|
+
|
|
25
|
+
- 这个脚本会同时生成运行时凭据
|
|
26
|
+
- 默认会写到 `/tmp/bridge-weixin-credentials.json` 和 `/tmp/bridge-weixin.env`
|
|
27
|
+
- 扫码确认成功后,也会自动回写 `~/.claude-codex-wechat/config.json`
|
|
28
|
+
|
|
29
|
+
### `weixin-login-helper.ts`
|
|
30
|
+
|
|
31
|
+
微信扫码登录辅助工具。
|
|
32
|
+
|
|
33
|
+
作用:
|
|
34
|
+
|
|
35
|
+
- 生成并自动打开二维码 SVG
|
|
36
|
+
- 轮询扫码状态
|
|
37
|
+
- 自动续刷过期二维码
|
|
38
|
+
- 写出:
|
|
39
|
+
- `/tmp/bridge-weixin-login-state.json`
|
|
40
|
+
- `/tmp/bridge-weixin-credentials.json`
|
|
41
|
+
- `/tmp/bridge-weixin.env`
|
|
42
|
+
|
|
43
|
+
适用场景:
|
|
44
|
+
|
|
45
|
+
- 只想先拿新 token,不想立刻起 runtime
|
|
46
|
+
- 需要观察扫码状态
|
|
47
|
+
|
|
48
|
+
### `start-runtime-check.sh`
|
|
49
|
+
|
|
50
|
+
带快照输出的 runtime 启动脚本。
|
|
51
|
+
|
|
52
|
+
作用:
|
|
53
|
+
|
|
54
|
+
- 自动加载 helper 生成的微信凭据
|
|
55
|
+
- 启动最新 runtime
|
|
56
|
+
- 打印启动后的关键 API 状态
|
|
57
|
+
|
|
58
|
+
适用场景:
|
|
59
|
+
|
|
60
|
+
- 扫码确认后单独启动最新实例
|
|
61
|
+
- 联调时确认新凭据是否真正生效
|
|
62
|
+
|
|
63
|
+
## 诊断 / 验收工具
|
|
64
|
+
|
|
65
|
+
### `check-runtime-readiness.sh`
|
|
66
|
+
|
|
67
|
+
检查最新 runtime 是否真的就绪。
|
|
68
|
+
|
|
69
|
+
重点输出:
|
|
70
|
+
|
|
71
|
+
- `weixin_connected`
|
|
72
|
+
- `weixin_status`
|
|
73
|
+
- `weixin_last_error`
|
|
74
|
+
- `claude_history_ready_count`
|
|
75
|
+
- `codex_title_ready_count`
|
|
76
|
+
|
|
77
|
+
### `check-runtime-recovery.sh`
|
|
78
|
+
|
|
79
|
+
查看 bridge 会话的恢复字段。
|
|
80
|
+
|
|
81
|
+
重点输出:
|
|
82
|
+
|
|
83
|
+
- `resumeTitle`
|
|
84
|
+
- `preferredResumeCommand`
|
|
85
|
+
- `providerResumeTitleSynced`
|
|
86
|
+
- `providerResumeHistorySynced`
|
|
87
|
+
- `providerNativePath`
|
|
88
|
+
|
|
89
|
+
### `check-weixin-updates.ts`
|
|
90
|
+
|
|
91
|
+
直连微信官方 `getupdates` 的底层探针。
|
|
92
|
+
|
|
93
|
+
作用:
|
|
94
|
+
|
|
95
|
+
- 判断 token / session 是否健康
|
|
96
|
+
- 区分“bridge 问题”和“微信上游问题”
|
|
97
|
+
|
|
98
|
+
### `check-codex-wechat-flow.sh`
|
|
99
|
+
|
|
100
|
+
Codex 专项闭环验收脚本。
|
|
101
|
+
|
|
102
|
+
作用:
|
|
103
|
+
|
|
104
|
+
- 等待新的 Codex bridge session 出现
|
|
105
|
+
- 打印该会话的恢复字段
|
|
106
|
+
- 自动试跑推荐的 Codex resume 命令
|
|
107
|
+
|
|
108
|
+
## 建议用法
|
|
109
|
+
|
|
110
|
+
### 日常恢复
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
bash ./scripts/recover-weixin-runtime.sh
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
如果你要直接做 Codex 专项恢复实验:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
BRIDGE_DEFAULT_PROVIDER=codex bash ./scripts/recover-weixin-runtime.sh
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### 手动分步恢复
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
pnpm tsx scripts/weixin-login-helper.ts
|
|
126
|
+
bash ./scripts/start-runtime-check.sh
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
如果你只是想先让默认 `pnpm dev` 也读到正式配置,先准备配置文件:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
mkdir -p ~/.claude-codex-wechat
|
|
133
|
+
cp config.example.json ~/.claude-codex-wechat/config.json
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
同样也可以:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
BRIDGE_DEFAULT_PROVIDER=codex bash ./scripts/start-runtime-check.sh
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### 排障
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
BRIDGE_PORT=8788 bash ./scripts/check-runtime-readiness.sh
|
|
146
|
+
BRIDGE_PORT=8788 bash ./scripts/check-runtime-recovery.sh
|
|
147
|
+
BRIDGE_WECHAT_TOKEN='...' pnpm tsx scripts/check-weixin-updates.ts
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Codex 专项验收
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
BRIDGE_DEFAULT_PROVIDER=codex bash ./scripts/recover-weixin-runtime.sh
|
|
154
|
+
BRIDGE_PORT=8788 WAIT_SECONDS=120 bash ./scripts/check-codex-wechat-flow.sh
|
|
155
|
+
```
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Postinstall script: register wechat-media MCP server to Codex globally.
|
|
5
|
+
* Runs after `npm install -g claude-codex-wechat`.
|
|
6
|
+
* Idempotent — safe to run multiple times.
|
|
7
|
+
* Silently skips if `codex` is not installed.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { execSync } from 'node:child_process';
|
|
11
|
+
import { dirname, join } from 'node:path';
|
|
12
|
+
import { fileURLToPath } from 'node:url';
|
|
13
|
+
|
|
14
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
const mediaServerPath = join(here, '..', 'dist', 'mcp', 'mediaServer.js');
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
// Check if codex is available
|
|
19
|
+
execSync('codex --version', { stdio: 'ignore' });
|
|
20
|
+
} catch {
|
|
21
|
+
// codex not installed, skip silently
|
|
22
|
+
process.exit(0);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
// Remove existing (idempotent)
|
|
27
|
+
execSync('codex mcp remove wechat-media', { stdio: 'ignore' });
|
|
28
|
+
} catch {
|
|
29
|
+
// didn't exist, fine
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
execSync(
|
|
34
|
+
`codex mcp add wechat-media --env "BRIDGE_API_URL=http://localhost:8787" -- node "${mediaServerPath}"`,
|
|
35
|
+
{ stdio: 'inherit' },
|
|
36
|
+
);
|
|
37
|
+
} catch (e) {
|
|
38
|
+
console.warn('[claude-codex-wechat] Failed to register Codex MCP server:', e.message);
|
|
39
|
+
}
|