dataclaw-agent-kit 0.0.0-stage → 0.1.0

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.
@@ -0,0 +1,1212 @@
1
+ // src/mcp/client.ts
2
+ import { setTimeout as sleep } from "timers/promises";
3
+ import fs from "fs";
4
+ import path from "path";
5
+ import crypto from "crypto";
6
+ import { Readable } from "stream";
7
+ function getErrorMessage(error) {
8
+ if (error instanceof Error) return error.message;
9
+ if (typeof error === "string") return error;
10
+ return "\u672A\u77E5\u9519\u8BEF";
11
+ }
12
+ function formatBytes(bytes) {
13
+ if (!bytes || bytes <= 0) return "";
14
+ if (bytes < 1024) return `${bytes} B`;
15
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
16
+ if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
17
+ return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
18
+ }
19
+ function formatSegmentsToSrt(segments) {
20
+ const pad = (n, width = 2) => String(n).padStart(width, "0");
21
+ return segments.filter((s) => Boolean(s.text && s.text.trim())).map((s, index) => {
22
+ const startMs = Math.round(Number(s.start || 0) * 1e3);
23
+ const endMs = Math.round(Number(s.end ?? Number(s.start || 0) + 2) * 1e3);
24
+ const sH = Math.floor(startMs / 36e5);
25
+ const sM = Math.floor(startMs % 36e5 / 6e4);
26
+ const sS = Math.floor(startMs % 6e4 / 1e3);
27
+ const sMs = startMs % 1e3;
28
+ const eH = Math.floor(endMs / 36e5);
29
+ const eM = Math.floor(endMs % 36e5 / 6e4);
30
+ const eS = Math.floor(endMs % 6e4 / 1e3);
31
+ const eMs = endMs % 1e3;
32
+ const startTime = `${pad(sH)}:${pad(sM)}:${pad(sS)},${pad(sMs, 3)}`;
33
+ const endTime = `${pad(eH)}:${pad(eM)}:${pad(eS)},${pad(eMs, 3)}`;
34
+ return `${index + 1}
35
+ ${startTime} --> ${endTime}
36
+ ${s.text?.trim() || ""}
37
+ `;
38
+ }).join("\n");
39
+ }
40
+ var DY_SEARCH_SORT = {
41
+ general: "0",
42
+ most_liked: "1",
43
+ most_recent: "2"
44
+ };
45
+ var DY_AWEME_RE = /(?:modal_id|vid|item_id|aweme_id)=([0-9]{6,})|(?:video|slides|note|photo|article|shipin)\/([0-9]{6,})/;
46
+ function douyinAwemeId(raw) {
47
+ const text = raw?.trim();
48
+ if (!text) return void 0;
49
+ if (/^\d+$/.test(text)) return text;
50
+ const match = text.match(DY_AWEME_RE);
51
+ return match?.[1] || match?.[2];
52
+ }
53
+ function douyinVideoUrl(input) {
54
+ const direct = input.url?.trim() || input.share_url?.trim();
55
+ if (direct) return direct;
56
+ const id = douyinAwemeId(input.aweme_id);
57
+ if (id) return `https://www.douyin.com/video/${id}`;
58
+ throw new Error("\u8BF7\u63D0\u4F9B url \u6216 aweme_id");
59
+ }
60
+ function douyinCommentTarget(input) {
61
+ const direct = input.url?.trim() || input.share_url?.trim();
62
+ const awemeId = douyinAwemeId(input.aweme_id) || douyinAwemeId(direct);
63
+ if (awemeId) {
64
+ const url = direct && /^https?:\/\//i.test(direct) ? direct : `https://www.douyin.com/video/${awemeId}`;
65
+ return { url, video_id: awemeId };
66
+ }
67
+ if (direct) return { url: direct };
68
+ throw new Error("\u8BF7\u63D0\u4F9B url \u6216 aweme_id");
69
+ }
70
+ var DataClawClient = class {
71
+ apiKey;
72
+ baseUrl;
73
+ ossPublicBase;
74
+ constructor(config = {}) {
75
+ this.apiKey = config.apiKey || process.env.DATACLAW_API_KEY || "";
76
+ this.baseUrl = (config.baseUrl || process.env.DATACLAW_BASE_URL || "https://www.geodataclaw.com").replace(/\/+$/, "");
77
+ this.ossPublicBase = (process.env.DATACLAW_OSS_BASE_URL || "https://oss.geodataclaw.com").replace(/\/+$/, "");
78
+ }
79
+ get isConfigured() {
80
+ return Boolean(this.apiKey);
81
+ }
82
+ async request(endpoint, options = {}) {
83
+ if (!this.apiKey) {
84
+ throw new Error(
85
+ "\u3010\u672A\u914D\u7F6E API-Key\u3011\u8BF7\u8BBE\u7F6E\u73AF\u5883\u53D8\u91CF DATACLAW_API_KEY\uFF0C\u6216\u8FD0\u884C `npx dataclaw-agent-kit init` \u91CD\u65B0\u914D\u7F6E\u3002"
86
+ );
87
+ }
88
+ const url = `${this.baseUrl}${endpoint.startsWith("/") ? endpoint : `/${endpoint}`}`;
89
+ const headers = new Headers(options.headers || {});
90
+ headers.set("Authorization", `Bearer ${this.apiKey}`);
91
+ headers.set("X-API-Key", this.apiKey);
92
+ headers.set("X-Client-Website", "dataclaw");
93
+ if (!headers.has("Content-Type") && !(options.body instanceof FormData)) {
94
+ headers.set("Content-Type", "application/json");
95
+ }
96
+ let response;
97
+ try {
98
+ response = await fetch(url, {
99
+ ...options,
100
+ headers
101
+ });
102
+ } catch (err) {
103
+ throw new Error(`\u8BF7\u6C42 DataClaw \u670D\u52A1\u5931\u8D25 (${url}): ${getErrorMessage(err)}`);
104
+ }
105
+ if (response.status === 401) {
106
+ throw new Error("\u3010\u9274\u6743\u5931\u8D25\u3011\u60A8\u7684 DataClaw API-Key \u65E0\u6548\u6216\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u524D\u5F80\u5B98\u7F51\u4E2A\u4EBA\u4E2D\u5FC3\u91CD\u65B0\u751F\u6210\u3002");
107
+ }
108
+ if (response.status === 402) {
109
+ throw new Error("\u3010\u79EF\u5206\u4E0D\u8DB3\u3011\u60A8\u7684 DataClaw \u8D26\u6237\u79EF\u5206\u4E0D\u8DB3\u4EE5\u652F\u4ED8\u672C\u6B21\u64CD\u4F5C\uFF0C\u8BF7\u524D\u5F80\u4E2A\u4EBA\u4E2D\u5FC3\u5145\u503C\u3002");
110
+ }
111
+ const resJson = await response.json().catch(() => null);
112
+ if (!response.ok) {
113
+ let errMsg = `HTTP \u9519\u8BEF: ${response.status} ${response.statusText}`;
114
+ if (resJson && typeof resJson === "object") {
115
+ if ("message" in resJson && typeof resJson.message === "string") {
116
+ errMsg = resJson.message;
117
+ } else if ("error" in resJson && typeof resJson.error === "string") {
118
+ errMsg = resJson.error;
119
+ } else if ("detail" in resJson && typeof resJson.detail === "string") {
120
+ errMsg = resJson.detail;
121
+ }
122
+ }
123
+ if (/credit|积分|balance/i.test(errMsg)) {
124
+ throw new Error(`\u3010\u79EF\u5206\u4E0D\u8DB3\u3011${errMsg}\u3002\u8BF7\u524D\u5F80 DataClaw \u4E2A\u4EBA\u4E2D\u5FC3\u5145\u503C\u3002`);
125
+ }
126
+ throw new Error(`DataClaw API \u9519\u8BEF: ${errMsg}`);
127
+ }
128
+ if (resJson && typeof resJson === "object" && "success" in resJson && resJson.success === false) {
129
+ const msg = "message" in resJson && typeof resJson.message === "string" ? resJson.message : "\u672A\u77E5\u4E1A\u52A1\u9519\u8BEF";
130
+ if (/credit|积分|balance/i.test(msg)) {
131
+ throw new Error(`\u3010\u79EF\u5206\u4E0D\u8DB3\u3011${msg}\u3002\u8BF7\u524D\u5F80 DataClaw \u4E2A\u4EBA\u4E2D\u5FC3\u5145\u503C\u3002`);
132
+ }
133
+ throw new Error(`\u4E1A\u52A1\u9519\u8BEF: ${msg}`);
134
+ }
135
+ if (resJson && typeof resJson === "object" && "data" in resJson) {
136
+ return resJson.data;
137
+ }
138
+ return resJson;
139
+ }
140
+ // ==========================================
141
+ // 用户鉴权与信息校验
142
+ // ==========================================
143
+ async getMe() {
144
+ const res = await this.request("/api/auth/me", { method: "GET" });
145
+ const user = res && typeof res === "object" && "user" in res ? res.user : res;
146
+ return {
147
+ id: typeof user?.id === "number" ? user.id : 0,
148
+ username: typeof user?.username === "string" ? user.username : "",
149
+ credit: typeof user?.credit === "number" ? user.credit : void 0,
150
+ role: typeof user?.role === "string" ? user.role : void 0
151
+ };
152
+ }
153
+ // ==========================================
154
+ // 本地文件预签名直传 R2 / OSS (temp/mcp/ 规范)
155
+ // ==========================================
156
+ async uploadLocalFileToR2(localFilePath) {
157
+ if (!fs.existsSync(localFilePath)) {
158
+ throw new Error(`\u672C\u5730\u6587\u4EF6\u4E0D\u5B58\u5728: ${localFilePath}`);
159
+ }
160
+ const stat = fs.statSync(localFilePath);
161
+ if (!stat.isFile()) {
162
+ throw new Error(`\u6307\u5B9A\u7684\u8DEF\u5F84\u4E0D\u662F\u666E\u901A\u6587\u4EF6: ${localFilePath}`);
163
+ }
164
+ const dateStr = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10).replace(/-/g, "");
165
+ const ext = path.extname(localFilePath) || ".mp4";
166
+ const uniqueId = crypto.randomUUID();
167
+ const objectKey = `temp/mcp/${dateStr}/${uniqueId}${ext}`;
168
+ const uploadInfo = await this.request("/api/upload/url", {
169
+ method: "POST",
170
+ body: JSON.stringify({
171
+ key: objectKey,
172
+ contentType: "application/octet-stream",
173
+ expireSeconds: 3600
174
+ })
175
+ });
176
+ if (!uploadInfo?.url) {
177
+ throw new Error("\u83B7\u53D6\u5BF9\u8C61\u5B58\u50A8\u9884\u7B7E\u540D\u4E0A\u4F20 URL \u5931\u8D25");
178
+ }
179
+ const fileStream = Readable.toWeb(fs.createReadStream(localFilePath));
180
+ const putRes = await fetch(uploadInfo.url, {
181
+ method: uploadInfo.method || "PUT",
182
+ headers: {
183
+ "Content-Type": "application/octet-stream",
184
+ "Content-Length": stat.size.toString()
185
+ },
186
+ body: fileStream,
187
+ duplex: "half"
188
+ });
189
+ if (!putRes.ok) {
190
+ throw new Error(`\u672C\u5730\u6587\u4EF6\u76F4\u4F20\u5B58\u50A8\u5931\u8D25: ${putRes.status} ${putRes.statusText}`);
191
+ }
192
+ return `${this.ossPublicBase}/${objectKey}`;
193
+ }
194
+ // ==========================================
195
+ // 多媒体域 Tools
196
+ // ==========================================
197
+ async resolveVideo(params) {
198
+ const rawUrl = params.url.trim();
199
+ if (!rawUrl) {
200
+ throw new Error("\u8BF7\u63D0\u4F9B\u9700\u8981\u89E3\u6790\u7684\u89C6\u9891\u5206\u4EAB\u94FE\u63A5\u6216\u5305\u542B\u94FE\u63A5\u7684\u6587\u6848");
201
+ }
202
+ const batchRes = await this.request("/api/task/remove-watermark", {
203
+ method: "POST",
204
+ body: JSON.stringify({
205
+ title: "MCP\u89C6\u9891\u53BB\u6C34\u5370\u89E3\u6790",
206
+ links: [rawUrl],
207
+ enqueue: true
208
+ })
209
+ });
210
+ const task = batchRes?.tasks?.[0];
211
+ if (!task?.task_id) {
212
+ throw new Error("\u521B\u5EFA\u53BB\u6C34\u5370\u89E3\u6790\u4EFB\u52A1\u5931\u8D25\uFF0C\u672A\u8FD4\u56DE task_id");
213
+ }
214
+ const maxAttempts = 40;
215
+ const intervalMs = 1e3;
216
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
217
+ await sleep(intervalMs);
218
+ const taskInfo = await this.request(`/api/v2/task/info?task_id=${encodeURIComponent(task.task_id)}`, {
219
+ method: "GET"
220
+ });
221
+ console.error(
222
+ `[DataClaw] \u4EFB\u52A1 ${task.task_id} \u8F6E\u8BE2\u72B6\u6001: ${taskInfo.status} (${attempt + 1}/${maxAttempts})`
223
+ );
224
+ const isCompleted = taskInfo.status === "completed" || taskInfo.status === "success" || taskInfo.status === "partial_completed";
225
+ if (isCompleted) {
226
+ const tr = taskInfo.task_result || {};
227
+ const meta = tr.metadata || tr;
228
+ const video = tr.video || meta.video || {};
229
+ const rawStreams = video.streams || [];
230
+ const streams = rawStreams.map((s) => {
231
+ const sizeNum = typeof s.size === "number" ? s.size : void 0;
232
+ return {
233
+ quality: s.quality || "default",
234
+ type: s.type || "video",
235
+ size: sizeNum,
236
+ size_formatted: sizeNum ? formatBytes(sizeNum) : void 0,
237
+ url: s.url || "",
238
+ proxy_url: s.proxy_url || ""
239
+ };
240
+ });
241
+ const availableResolutions = streams.filter((s) => s.type === "video" || !s.type).map((s) => `${s.quality}${s.size_formatted ? ` (${s.size_formatted})` : ""}`);
242
+ const playUrl = video.play_url || streams.find((s) => s.type === "video" && s.url)?.url || video.play_proxy_url || "";
243
+ const proxyUrl = video.play_proxy_url || streams.find((s) => s.type === "video" && s.proxy_url)?.proxy_url || "";
244
+ const durationSeconds = video.duration || ("duration" in meta && typeof meta.duration === "number" ? meta.duration : 0);
245
+ const durationFormatted = durationSeconds > 0 ? `${Math.floor(durationSeconds / 60)}\u5206${durationSeconds % 60}\u79D2` : void 0;
246
+ const coverUrl = tr.cover_url || meta.cover_url || "";
247
+ const images = tr.images || tr.proxy_images || meta.images || [];
248
+ return {
249
+ platform: taskInfo.task_params?.platform || "auto",
250
+ title: tr.title || meta.title || taskInfo.title || "",
251
+ author: tr.author || meta.author || "",
252
+ cover_url: coverUrl,
253
+ duration: durationSeconds,
254
+ duration_formatted: durationFormatted,
255
+ video_play_url: playUrl,
256
+ video_download_proxy_url: proxyUrl,
257
+ available_resolutions: availableResolutions,
258
+ streams,
259
+ images,
260
+ content: tr.content || "",
261
+ statistics: tr.statistics || meta.statistics || {},
262
+ task_id: task.task_id
263
+ };
264
+ }
265
+ if (taskInfo.status === "failed") {
266
+ const reason = taskInfo.error_message || taskInfo.error || "\u672A\u77E5\u89E3\u6790\u9519\u8BEF";
267
+ throw new Error(`\u89C6\u9891\u89E3\u6790\u4EFB\u52A1\u5931\u8D25: ${reason}`);
268
+ }
269
+ }
270
+ throw new Error("\u89C6\u9891\u53BB\u6C34\u5370\u89E3\u6790\u8D85\u65F6\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5");
271
+ }
272
+ async fetchSubtitleFromOss(r2Key) {
273
+ const key = r2Key.replace(/^\//, "");
274
+ const url = /^https?:\/\//i.test(key) ? key : `${this.ossPublicBase}/${key}`;
275
+ try {
276
+ const res = await fetch(url, { cache: "no-store" });
277
+ if (!res.ok) return null;
278
+ const data = await res.json();
279
+ return data;
280
+ } catch {
281
+ return null;
282
+ }
283
+ }
284
+ async transcribeAudio(params) {
285
+ const rawUrl = params.local_file_path ? await this.uploadLocalFileToR2(params.local_file_path) : (params.media_url || "").trim();
286
+ if (!rawUrl) {
287
+ throw new Error("\u5FC5\u987B\u63D0\u4F9B media_url \u6216 local_file_path \u4E4B\u4E00");
288
+ }
289
+ const isFinder = /channels\.weixin\.qq\.com|weixin\.qq\.com\/sph\/|finder\.video\.qq\.com/i.test(rawUrl);
290
+ let taskId = "";
291
+ if (params.local_file_path) {
292
+ const fileName = path.basename(params.local_file_path);
293
+ const batchRes = await this.request("/api/task/create/batch", {
294
+ method: "POST",
295
+ body: JSON.stringify({
296
+ title: `\u672C\u5730\u6587\u4EF6\u8F6C\u6587\u672C-${fileName}`,
297
+ task_type: "local_media_transcribe",
298
+ links: [rawUrl],
299
+ task_params: {
300
+ platform: "local",
301
+ options: {
302
+ original_name: fileName
303
+ }
304
+ },
305
+ enqueue: true
306
+ })
307
+ });
308
+ taskId = batchRes?.tasks?.[0]?.task_id || "";
309
+ } else if (isFinder) {
310
+ const batchRes = await this.request("/api/task/create/batch", {
311
+ method: "POST",
312
+ body: JSON.stringify({
313
+ title: "MCP\u89C6\u9891\u53F7\u8F6C\u6587\u672C",
314
+ task_type: "finder_link_to_text",
315
+ links: [rawUrl],
316
+ task_params: { platform: "finder" },
317
+ enqueue: true
318
+ })
319
+ });
320
+ taskId = batchRes?.tasks?.[0]?.task_id || "";
321
+ } else {
322
+ const batchRes = await this.request("/api/task/link-to-text", {
323
+ method: "POST",
324
+ body: JSON.stringify({
325
+ title: "MCP\u94FE\u63A5\u8F6C\u6587\u672C",
326
+ links: [rawUrl],
327
+ enqueue: true
328
+ })
329
+ });
330
+ taskId = batchRes?.tasks?.[0]?.task_id || "";
331
+ }
332
+ if (!taskId) {
333
+ throw new Error("\u521B\u5EFA\u8F6C\u6587\u6848\u4EFB\u52A1\u5931\u8D25\uFF0C\u672A\u8FD4\u56DE task_id");
334
+ }
335
+ const maxAttempts = params.local_file_path ? 1800 : 60;
336
+ const intervalMs = params.local_file_path ? 2e3 : 1e3;
337
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
338
+ await sleep(intervalMs);
339
+ const taskInfo = await this.request(`/api/v2/task/info?task_id=${encodeURIComponent(taskId)}`, {
340
+ method: "GET"
341
+ });
342
+ console.error(
343
+ `[DataClaw] \u8F6C\u6587\u6848\u4EFB\u52A1 ${taskId} \u8F6E\u8BE2\u72B6\u6001: ${taskInfo.status} (${attempt + 1}/${maxAttempts})`
344
+ );
345
+ const isCompleted = taskInfo.status === "completed" || taskInfo.status === "success" || taskInfo.status === "partial_completed";
346
+ if (isCompleted) {
347
+ const tr = taskInfo.task_result || {};
348
+ const meta = tr.metadata || {};
349
+ const video = tr.video || meta.video || {};
350
+ const subtitle = video.subtitle || {};
351
+ let text = subtitle.text || "";
352
+ let segments = subtitle.segments || [];
353
+ if ((!text || segments.length === 0) && subtitle.r2_key) {
354
+ const ossData = await this.fetchSubtitleFromOss(subtitle.r2_key);
355
+ if (ossData) {
356
+ text = text || ossData.text || "";
357
+ segments = segments.length > 0 ? segments : ossData.segments || [];
358
+ }
359
+ }
360
+ if (!text) {
361
+ text = tr.content || tr.desc || "";
362
+ }
363
+ const rawStreams = video.streams || [];
364
+ const streams = rawStreams.map((s) => {
365
+ const sizeNum = typeof s.size === "number" ? s.size : void 0;
366
+ return {
367
+ quality: s.quality || "default",
368
+ type: s.type || "video",
369
+ size: sizeNum,
370
+ size_formatted: sizeNum ? formatBytes(sizeNum) : void 0,
371
+ url: s.url || "",
372
+ proxy_url: s.proxy_url || ""
373
+ };
374
+ });
375
+ const availableResolutions = streams.filter((s) => s.type === "video" || !s.type).map((s) => `${s.quality}${s.size_formatted ? ` (${s.size_formatted})` : ""}`);
376
+ const playUrl = video.play_url || streams.find((s) => s.type === "video" && s.url)?.url || video.play_proxy_url || "";
377
+ const proxyUrl = video.play_proxy_url || streams.find((s) => s.type === "video" && s.proxy_url)?.proxy_url || "";
378
+ const durationSeconds = video.duration || meta.duration || 0;
379
+ const durationFormatted = durationSeconds > 0 ? `${Math.floor(durationSeconds / 60)}\u5206${durationSeconds % 60}\u79D2` : void 0;
380
+ const coverUrl = tr.cover_url || meta.cover_url || "";
381
+ const title = tr.title || meta.title || taskInfo.title || "";
382
+ const author = tr.author || meta.author || "";
383
+ const srt = formatSegmentsToSrt(segments);
384
+ if (params.output_format === "srt") {
385
+ return {
386
+ title,
387
+ author,
388
+ cover_url: coverUrl,
389
+ duration: durationSeconds,
390
+ duration_formatted: durationFormatted,
391
+ srt: srt || text,
392
+ video_play_url: playUrl,
393
+ video_download_proxy_url: proxyUrl,
394
+ available_resolutions: availableResolutions,
395
+ streams,
396
+ task_id: taskId
397
+ };
398
+ }
399
+ if (params.output_format === "verbose_json") {
400
+ return {
401
+ title,
402
+ author,
403
+ cover_url: coverUrl,
404
+ duration: durationSeconds,
405
+ duration_formatted: durationFormatted,
406
+ text,
407
+ srt,
408
+ segments,
409
+ video_play_url: playUrl,
410
+ video_download_proxy_url: proxyUrl,
411
+ available_resolutions: availableResolutions,
412
+ streams,
413
+ statistics: tr.statistics || meta.statistics || {},
414
+ task_id: taskId
415
+ };
416
+ }
417
+ return {
418
+ title,
419
+ author,
420
+ cover_url: coverUrl,
421
+ duration: durationSeconds,
422
+ duration_formatted: durationFormatted,
423
+ text: text || "\uFF08\u672A\u68C0\u6D4B\u5230\u53E3\u64AD\u8BED\u97F3\uFF09",
424
+ video_play_url: playUrl,
425
+ video_download_proxy_url: proxyUrl,
426
+ available_resolutions: availableResolutions,
427
+ streams,
428
+ statistics: tr.statistics || meta.statistics || {},
429
+ task_id: taskId
430
+ };
431
+ }
432
+ if (taskInfo.status === "failed") {
433
+ const reason = taskInfo.error_message || taskInfo.error || "\u672A\u77E5\u8F6C\u5199\u9519\u8BEF";
434
+ throw new Error(`\u89C6\u9891\u8F6C\u6587\u6848\u4EFB\u52A1\u5931\u8D25: ${reason}`);
435
+ }
436
+ }
437
+ throw new Error("\u8F6C\u6587\u6848\u4EFB\u52A1\u5904\u7406\u8D85\u65F6\uFF0C\u8BF7\u7A0D\u540E\u524D\u5F80 DataClaw \u7F51\u9875\u7AEF\u67E5\u770B\u8BE6\u60C5");
438
+ }
439
+ // ==========================================
440
+ // 抖音域 Tools
441
+ // ==========================================
442
+ async searchDyVideos(params) {
443
+ const body = {
444
+ keyword: params.keyword,
445
+ sort_type: DY_SEARCH_SORT[params.sort_type] ?? "0",
446
+ publish_time: params.publish_time ?? "0",
447
+ cursor: params.cursor ?? 0
448
+ };
449
+ if (params.search_id) body.search_id = params.search_id;
450
+ if (params.backtrace) body.backtrace = params.backtrace;
451
+ return this.request("/api/v2/dy/search/video", {
452
+ method: "POST",
453
+ body: JSON.stringify(body)
454
+ });
455
+ }
456
+ async getDyVideoDetail(params) {
457
+ return this.request("/api/v2/dy/detail", {
458
+ method: "POST",
459
+ body: JSON.stringify({ url: douyinVideoUrl(params) })
460
+ });
461
+ }
462
+ async getDyPlayStats(params) {
463
+ return this.request("/api/dy/video/play-stats", {
464
+ method: "POST",
465
+ body: JSON.stringify({ url: douyinVideoUrl(params) })
466
+ });
467
+ }
468
+ async getDyComments(params) {
469
+ const target = douyinCommentTarget(params);
470
+ return this.request("/api/v2/dy/video/comments", {
471
+ method: "POST",
472
+ body: JSON.stringify({
473
+ url: target.url,
474
+ ...target.video_id ? { video_id: target.video_id } : {},
475
+ cursor: params.cursor ?? 0,
476
+ count: params.count ?? 50
477
+ })
478
+ });
479
+ }
480
+ async getDyUserProfile(params) {
481
+ return this.request("/api/v2/dy/user/detail", {
482
+ method: "POST",
483
+ body: JSON.stringify({
484
+ sec_user_id: params.sec_user_id,
485
+ url: params.url || params.profile_url
486
+ })
487
+ });
488
+ }
489
+ // ==========================================
490
+ // 视频号域 Tools
491
+ // ==========================================
492
+ async searchChannelsAuthors(params) {
493
+ const query = params.keyword.trim();
494
+ if (!query) throw new Error("\u8BF7\u63D0\u4F9B\u641C\u7D22\u5173\u952E\u8BCD");
495
+ return this.request("/api/v2/mp/finder/search-authors", {
496
+ method: "POST",
497
+ body: JSON.stringify({
498
+ query,
499
+ count: params.count ?? 20,
500
+ buffer: params.buffer ?? "",
501
+ scene: params.scene ?? 1
502
+ })
503
+ });
504
+ }
505
+ async getChannelsAuthorFeeds(params) {
506
+ const username = params.finder_username.trim();
507
+ if (!username) throw new Error("\u8BF7\u63D0\u4F9B finder_username");
508
+ return this.request("/api/v2/mp/finder/feeds", {
509
+ method: "POST",
510
+ body: JSON.stringify({
511
+ username,
512
+ buffer: params.last_buffer?.trim() ?? "",
513
+ count: 15,
514
+ scene: 0
515
+ })
516
+ });
517
+ }
518
+ async resolveChannelsShareUrl(params) {
519
+ return this.request("/api/v2/mp/finder/video-by-share-url", {
520
+ method: "POST",
521
+ body: JSON.stringify(params)
522
+ });
523
+ }
524
+ };
525
+
526
+ // src/types.ts
527
+ import { z } from "zod";
528
+ var VideoResolveSchema = z.object({
529
+ url: z.string().describe("\u6296\u97F3\u3001\u89C6\u9891\u53F7\u3001\u5C0F\u7EA2\u4E66\u3001\u5FEB\u624B\u3001B\u7AD9\u7B49\u5404\u5927\u5E73\u53F0\u7684\u5206\u4EAB\u6587\u6848\u6216\u94FE\u63A5\uFF08\u652F\u6301\u5B8C\u6574\u94FE\u63A5\u3001\u77ED\u94FE\u6216\u5305\u542B\u5206\u4EAB\u6587\u672C\u7684\u6574\u6BB5\u6587\u5B57\uFF09")
530
+ });
531
+ var AudioTranscribeSchema = z.object({
532
+ media_url: z.string().optional().describe("\u7F51\u7EDC\u97F3\u89C6\u9891\u76F4\u94FE URL \u6216\u77ED\u89C6\u9891\u5206\u4EAB\u6587\u6848/\u94FE\u63A5\uFF08\u652F\u6301\u666E\u901A\u77ED\u89C6\u9891\u4E0E\u5FAE\u4FE1\u89C6\u9891\u53F7\uFF0C\u4E0E local_file_path \u4E8C\u9009\u4E00\uFF09"),
533
+ local_file_path: z.string().optional().describe("\u672C\u5730\u97F3\u89C6\u9891\u6587\u4EF6\u7EDD\u5BF9\u8DEF\u5F84\uFF08\u4E0E media_url \u4E8C\u9009\u4E00\uFF1B\u4F1A\u5148\u4E0A\u4F20\u5230\u5BF9\u8C61\u5B58\u50A8\uFF0C\u518D\u521B\u5EFA\u5F02\u6B65\u8F6C\u5199\u4EFB\u52A1\uFF09"),
534
+ output_format: z.enum(["text", "srt", "verbose_json"]).default("text").describe("\u8FD4\u56DE\u683C\u5F0F\uFF1A\u7EAF\u6587\u672C\u3001SRT\u5B57\u5E55\u3001\u6216\u5E26\u65F6\u95F4\u6233\u7684\u6BB5\u843D\u8BE6\u60C5")
535
+ });
536
+ var DySearchVideosSchema = z.object({
537
+ keyword: z.string().describe("\u641C\u7D22\u5173\u952E\u8BCD"),
538
+ sort_type: z.enum(["general", "most_recent", "most_liked"]).default("general").describe("\u6392\u5E8F\uFF1A\u7EFC\u5408(general\u21920)\u3001\u6700\u591A\u70B9\u8D5E(most_liked\u21921)\u3001\u6700\u65B0(most_recent\u21922)"),
539
+ publish_time: z.enum(["0", "1", "7", "180"]).default("0").describe("\u53D1\u5E03\u65F6\u95F4\uFF1A0 \u4E0D\u9650\uFF0C1 \u6700\u8FD1\u4E00\u5929\uFF0C7 \u6700\u8FD1\u4E00\u5468\uFF0C180 \u6700\u8FD1\u534A\u5E74"),
540
+ count: z.number().min(1).max(20).default(10).describe("\u517C\u5BB9\u5B57\u6BB5\u3002\u4E0A\u6E38\u9875\u5927\u5C0F\u56FA\u5B9A\uFF0C\u8C03\u7528\u65F6\u5FFD\u7565"),
541
+ cursor: z.number().optional().describe("\u5206\u9875\u6E38\u6807\uFF0C\u9996\u9875\u4E0D\u4F20\u6216\u4F20 0\uFF0C\u4E0B\u4E00\u9875\u4F7F\u7528\u4E0A\u6B21\u8FD4\u56DE\u7684 cursor"),
542
+ search_id: z.string().optional().describe("\u7FFB\u9875\u65F6\u5E26\u56DE\u4E0A\u6B21\u8FD4\u56DE\u7684 search_id"),
543
+ backtrace: z.string().optional().describe("\u7FFB\u9875\u65F6\u5E26\u56DE\u4E0A\u6B21\u8FD4\u56DE\u7684 backtrace")
544
+ });
545
+ var DyGetVideoDetailSchema = z.object({
546
+ url: z.string().optional().describe("\u6296\u97F3\u89C6\u9891\u94FE\u63A5\u6216\u5206\u4EAB\u6587\u6848\u3002\u8BF7\u6C42\u4F53\u5B57\u6BB5\u5C31\u662F url\uFF0C\u4F8B\u5982 https://www.douyin.com/video/ID \u6216 https://www.douyin.com/jingxuan?modal_id=ID"),
547
+ aweme_id: z.string().optional().describe("\u6296\u97F3\u89C6\u9891 ID\u3002\u672A\u4F20 url \u65F6\u8F6C\u6210 https://www.douyin.com/video/{aweme_id}")
548
+ });
549
+ var DyGetPlayStatsSchema = z.object({
550
+ url: z.string().optional().describe("\u6296\u97F3\u89C6\u9891\u94FE\u63A5\u6216\u5206\u4EAB\u6587\u6848\u3002\u8BF7\u6C42\u4F53\u5B57\u6BB5\u5C31\u662F url"),
551
+ aweme_id: z.string().optional().describe("\u6296\u97F3\u89C6\u9891 ID\u3002\u672A\u4F20 url \u65F6\u8F6C\u6210\u89C6\u9891\u957F\u94FE")
552
+ });
553
+ var DyGetCommentsSchema = z.object({
554
+ url: z.string().optional().describe("\u6296\u97F3\u89C6\u9891\u94FE\u63A5\u6216\u5206\u4EAB\u6587\u6848\u3002\u4F8B\u5982 https://www.douyin.com/video/ID \u6216 https://www.douyin.com/jingxuan?modal_id=ID"),
555
+ aweme_id: z.string().optional().describe("\u6296\u97F3\u89C6\u9891 ID\u3002\u672A\u4F20 url \u65F6\u4F7F\u7528\uFF0C\u4E0D\u8981\u628A\u6574\u6BB5\u94FE\u63A5\u586B\u5728\u8FD9\u91CC"),
556
+ cursor: z.number().default(0).describe("\u5206\u9875\u6E38\u6807\uFF0C\u7B2C\u4E00\u9875\u4F20 0"),
557
+ count: z.number().min(1).max(50).default(50).describe("\u6BCF\u9875\u8BC4\u8BBA\u6570\uFF0C\u9ED8\u8BA4 50\uFF0C\u6700\u5927 50")
558
+ });
559
+ var DyGetUserProfileSchema = z.object({
560
+ sec_user_id: z.string().optional().describe("\u535A\u4E3B sec_user_id\uFF08\u4E0E url \u4E8C\u9009\u4E00\uFF09"),
561
+ url: z.string().optional().describe("\u535A\u4E3B\u4E3B\u9875\u94FE\u63A5\u3001\u5206\u4EAB\u77ED\u94FE\u6216\u5206\u4EAB\u6587\u6848\uFF08\u4E0E sec_user_id \u4E8C\u9009\u4E00\uFF09"),
562
+ profile_url: z.string().optional().describe("\uFF08\u522B\u540D\u517C\u5BB9\uFF09\u535A\u4E3B\u4E3B\u9875\u94FE\u63A5")
563
+ });
564
+ var ChannelsSearchAuthorsSchema = z.object({
565
+ keyword: z.string().describe("\u521B\u4F5C\u8005\u5173\u952E\u8BCD\u6216\u535A\u4E3B\u6635\u79F0"),
566
+ count: z.number().int().min(1).max(50).default(20).describe("\u6BCF\u9875\u4F5C\u8005\u6570\u91CF\uFF0C\u9ED8\u8BA4 20"),
567
+ buffer: z.string().default("").describe("\u5206\u9875\u6E38\u6807\uFF0C\u9996\u9875\u4E3A\u7A7A\uFF0C\u4E0B\u4E00\u9875\u4F7F\u7528\u4E0A\u6B21\u8FD4\u56DE\u7684 buffer"),
568
+ scene: z.number().int().default(1).describe("\u641C\u7D22\u573A\u666F\uFF0C\u9ED8\u8BA4 1")
569
+ });
570
+ var ChannelsGetAuthorFeedsSchema = z.object({
571
+ finder_username: z.string().describe("\u89C6\u9891\u53F7\u4F5C\u8005\u552F\u4E00\u6807\u8BC6 finder_username"),
572
+ last_buffer: z.string().optional().describe("\u7FFB\u9875 buffer\uFF08\u9996\u9875\u65E0\u9700\u4F20\uFF09")
573
+ });
574
+ var ChannelsResolveShareUrlSchema = z.object({
575
+ share_url: z.string().describe("\u5FAE\u4FE1\u89C6\u9891\u53F7\u5206\u4EAB\u94FE\u63A5\uFF0C\u4F8B\u5982\u5305\u542B finder.video.qq.com \u6216\u77ED\u94FE\u63A5")
576
+ });
577
+
578
+ // src/mcp/server.ts
579
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
580
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
581
+
582
+ // src/mcp/tools/media.ts
583
+ function registerMediaTools(server, client) {
584
+ server.registerTool(
585
+ "dataclaw_video_resolve",
586
+ {
587
+ description: "\u4F20\u5165\u6296\u97F3\u3001\u89C6\u9891\u53F7\u3001\u5C0F\u7EA2\u4E66\u3001\u5FEB\u624B\u3001B\u7AD9\u7B49\u5404\u5927\u5E73\u53F0\u7684\u5206\u4EAB\u6587\u6848\u6216\u94FE\u63A5\uFF0C\u81EA\u52A8\u8BC6\u522B\u5E73\u53F0\u53BB\u6C34\u5370\u89E3\u6790\uFF0C\u63D0\u53D6\u65E0\u6C34\u5370\u9AD8\u6E05\u89C6\u9891\u64AD\u653E\u76F4\u94FE\u3001\u5C01\u9762\u56FE\u3001\u6807\u9898\u3001\u4F5C\u8005\u53CA\u65F6\u957F\u6570\u636E\uFF08\u5BF9\u5E94 /tools/material/remove-watermark \u9875\u9762\uFF09",
588
+ inputSchema: VideoResolveSchema.shape
589
+ },
590
+ async (args) => {
591
+ try {
592
+ const result = await client.resolveVideo(args);
593
+ return {
594
+ content: [
595
+ {
596
+ type: "text",
597
+ text: JSON.stringify(result, null, 2)
598
+ }
599
+ ]
600
+ };
601
+ } catch (err) {
602
+ const message = err instanceof Error ? err.message : String(err);
603
+ return {
604
+ isError: true,
605
+ content: [{ type: "text", text: `\u89C6\u9891\u89E3\u6790\u5931\u8D25: ${message}` }]
606
+ };
607
+ }
608
+ }
609
+ );
610
+ server.registerTool(
611
+ "dataclaw_audio_transcribe",
612
+ {
613
+ description: "\u4F20\u5165\u77ED\u89C6\u9891\u5206\u4EAB\u6587\u6848/\u94FE\u63A5\uFF08\u81EA\u52A8\u8BC6\u522B\u6296\u97F3\u3001\u5C0F\u7EA2\u4E66\u3001\u5FEB\u624B\u7B49\u666E\u901A\u89C6\u9891\u4E0E\u5FAE\u4FE1\u89C6\u9891\u53F7\u5E76\u81EA\u52A8\u89E3\u5BC6\u8F6C\u5199\uFF0C\u5BF9\u5E94 /tools/copy/extract \u548C /tools/copy/extract-finder\uFF09\uFF0C\u6216\u672C\u5730\u97F3\u89C6\u9891\u6587\u4EF6\uFF08\u5148\u76F4\u4F20\u5BF9\u8C61\u5B58\u50A8\uFF0C\u518D\u521B\u5EFA\u5F02\u6B65\u8F6C\u5199\u4EFB\u52A1\uFF09\uFF0C\u8F6C\u5199\u4E3A\u7EAF\u6587\u672C\u3001SRT\u5B57\u5E55\u6216\u5E26\u65F6\u95F4\u6233\u6BB5\u843D\u8BE6\u60C5",
614
+ inputSchema: AudioTranscribeSchema.shape
615
+ },
616
+ async (args) => {
617
+ try {
618
+ const result = await client.transcribeAudio(args);
619
+ return {
620
+ content: [
621
+ {
622
+ type: "text",
623
+ text: typeof result === "string" ? result : JSON.stringify(result, null, 2)
624
+ }
625
+ ]
626
+ };
627
+ } catch (err) {
628
+ const message = err instanceof Error ? err.message : String(err);
629
+ return {
630
+ isError: true,
631
+ content: [{ type: "text", text: `\u97F3\u9891\u8F6C\u5199\u5931\u8D25: ${message}` }]
632
+ };
633
+ }
634
+ }
635
+ );
636
+ }
637
+
638
+ // src/mcp/tools/douyin.ts
639
+ function registerDouyinTools(server, client) {
640
+ server.registerTool(
641
+ "dataclaw_dy_search_videos",
642
+ {
643
+ description: "\u5173\u952E\u8BCD\u641C\u7D22\u6296\u97F3\u70ED\u95E8\u89C6\u9891\uFF0C\u652F\u6301\u6309\u7EFC\u5408\u3001\u6700\u65B0\u3001\u6700\u591A\u70B9\u8D5E\u6392\u5E8F\u53CA\u53D1\u5E03\u65F6\u95F4\u7B5B\u9009\uFF0C\u8FD4\u56DE\u5305\u542B\u70B9\u8D5E\u3001\u64AD\u653E\u3001\u6807\u9898\u548C\u4F5C\u8005\u7684\u4F5C\u54C1\u5217\u8868",
644
+ inputSchema: DySearchVideosSchema.shape
645
+ },
646
+ async (args) => {
647
+ try {
648
+ const result = await client.searchDyVideos(args);
649
+ return {
650
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
651
+ };
652
+ } catch (err) {
653
+ const message = err instanceof Error ? err.message : String(err);
654
+ return {
655
+ isError: true,
656
+ content: [{ type: "text", text: `\u6296\u97F3\u89C6\u9891\u641C\u7D22\u5931\u8D25: ${message}` }]
657
+ };
658
+ }
659
+ }
660
+ );
661
+ server.registerTool(
662
+ "dataclaw_dy_get_video_detail",
663
+ {
664
+ description: "\u83B7\u53D6\u6307\u5B9A\u6296\u97F3\u89C6\u9891\u8BE6\u60C5\uFF1A\u6807\u9898\u3001\u4F5C\u8005\u3001\u5C01\u9762\u3001\u64AD\u653E\u5730\u5740\uFF0C\u4EE5\u53CA\u70B9\u8D5E\u3001\u8BC4\u8BBA\u3001\u5206\u4EAB\u3001\u6536\u85CF\u6570\u3002\u4E0D\u542B\u64AD\u653E\u91CF\uFF0C\u4E5F\u4E0D\u542B\u79D2\u7EA7\u4E92\u52A8\u8D8B\u52BF\u3002\u53EA\u4F20 url",
665
+ inputSchema: DyGetVideoDetailSchema.shape
666
+ },
667
+ async (args) => {
668
+ try {
669
+ const result = await client.getDyVideoDetail(args);
670
+ return {
671
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
672
+ };
673
+ } catch (err) {
674
+ const message = err instanceof Error ? err.message : String(err);
675
+ return {
676
+ isError: true,
677
+ content: [{ type: "text", text: `\u83B7\u53D6\u6296\u97F3\u89C6\u9891\u8BE6\u60C5\u5931\u8D25: ${message}` }]
678
+ };
679
+ }
680
+ }
681
+ );
682
+ server.registerTool(
683
+ "dataclaw_dy_get_play_stats",
684
+ {
685
+ description: "\u67E5\u8BE2\u6296\u97F3\u89C6\u9891\u64AD\u653E\u91CF\u3001\u5B8C\u64AD\u7387\u7B49\u661F\u56FE\u7EDF\u8BA1\u3002\u8BE6\u60C5\u63A5\u53E3\u4E0D\u542B\u64AD\u653E\u91CF\uFF0C\u5FC5\u987B\u5355\u72EC\u8C03\u7528\u672C\u5DE5\u5177\u3002\u53EA\u4F20 url\uFF0C\u6216 aweme_id\uFF08\u4F1A\u8F6C\u6210 url\uFF09",
686
+ inputSchema: DyGetPlayStatsSchema.shape
687
+ },
688
+ async (args) => {
689
+ try {
690
+ const result = await client.getDyPlayStats(args);
691
+ return {
692
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
693
+ };
694
+ } catch (err) {
695
+ const message = err instanceof Error ? err.message : String(err);
696
+ return {
697
+ isError: true,
698
+ content: [{ type: "text", text: `\u83B7\u53D6\u6296\u97F3\u64AD\u653E\u91CF\u5931\u8D25: ${message}` }]
699
+ };
700
+ }
701
+ }
702
+ );
703
+ server.registerTool(
704
+ "dataclaw_dy_get_comments",
705
+ {
706
+ description: "\u83B7\u53D6\u6307\u5B9A\u6296\u97F3\u89C6\u9891\u7684\u4E00\u7EA7\u8BC4\u8BBA\uFF08\u542B\u7F6E\u9876\u3001\u70B9\u8D5E\u6570\u3001\u56DE\u590D\u6570\uFF09\u3002\u4F20 url \u6216 aweme_id\uFF0C\u4E0D\u8981\u628A\u6574\u6BB5\u94FE\u63A5\u586B\u8FDB aweme_id",
707
+ inputSchema: DyGetCommentsSchema.shape
708
+ },
709
+ async (args) => {
710
+ try {
711
+ const result = await client.getDyComments(args);
712
+ return {
713
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
714
+ };
715
+ } catch (err) {
716
+ const message = err instanceof Error ? err.message : String(err);
717
+ return {
718
+ isError: true,
719
+ content: [{ type: "text", text: `\u83B7\u53D6\u6296\u97F3\u8BC4\u8BBA\u5931\u8D25: ${message}` }]
720
+ };
721
+ }
722
+ }
723
+ );
724
+ server.registerTool(
725
+ "dataclaw_dy_get_user_profile",
726
+ {
727
+ description: "\u83B7\u53D6\u6296\u97F3\u535A\u4E3B\u4E3B\u9875\u8D44\u6599\uFF1A\u6635\u79F0\u3001\u7C89\u4E1D\u6570\u3001\u4F5C\u54C1\u6570\u3001\u7B80\u4ECB\u3002\u4E0D\u542B\u7C89\u4E1D\u753B\u50CF\u548C\u5386\u53F2\u4F5C\u54C1\u5217\u8868",
728
+ inputSchema: DyGetUserProfileSchema.shape
729
+ },
730
+ async (args) => {
731
+ try {
732
+ const result = await client.getDyUserProfile(args);
733
+ return {
734
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
735
+ };
736
+ } catch (err) {
737
+ const message = err instanceof Error ? err.message : String(err);
738
+ return {
739
+ isError: true,
740
+ content: [{ type: "text", text: `\u83B7\u53D6\u6296\u97F3\u535A\u4E3B\u4FE1\u606F\u5931\u8D25: ${message}` }]
741
+ };
742
+ }
743
+ }
744
+ );
745
+ }
746
+
747
+ // src/mcp/tools/channels.ts
748
+ function registerChannelsTools(server, client) {
749
+ server.registerTool(
750
+ "dataclaw_channels_search_authors",
751
+ {
752
+ description: "\u5173\u952E\u8BCD\u641C\u7D22\u5FAE\u4FE1\u89C6\u9891\u53F7\u521B\u4F5C\u8005/\u535A\u4E3B\uFF0C\u83B7\u53D6\u535A\u4E3B finder_username\u3001\u5934\u50CF\u53CA\u8BA4\u8BC1\u7B80\u4ECB",
753
+ inputSchema: ChannelsSearchAuthorsSchema.shape
754
+ },
755
+ async (args) => {
756
+ try {
757
+ const result = await client.searchChannelsAuthors(args);
758
+ return {
759
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
760
+ };
761
+ } catch (err) {
762
+ const message = err instanceof Error ? err.message : String(err);
763
+ return {
764
+ isError: true,
765
+ content: [{ type: "text", text: `\u641C\u7D22\u89C6\u9891\u53F7\u521B\u4F5C\u8005\u5931\u8D25: ${message}` }]
766
+ };
767
+ }
768
+ }
769
+ );
770
+ server.registerTool(
771
+ "dataclaw_channels_get_author_feeds",
772
+ {
773
+ description: "\u6839\u636E\u4F5C\u8005 finder_username \u83B7\u53D6\u89C6\u9891\u53F7\u4F5C\u54C1 Feed\uFF0C\u6BCF\u9875 15 \u6761\u3002\u9996\u9875\u4E0D\u4F20 last_buffer\uFF0C\u4E0B\u4E00\u9875\u4F20\u4E0A\u6B21\u8FD4\u56DE\u7684 buffer",
774
+ inputSchema: ChannelsGetAuthorFeedsSchema.shape
775
+ },
776
+ async (args) => {
777
+ try {
778
+ const result = await client.getChannelsAuthorFeeds(args);
779
+ return {
780
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
781
+ };
782
+ } catch (err) {
783
+ const message = err instanceof Error ? err.message : String(err);
784
+ return {
785
+ isError: true,
786
+ content: [{ type: "text", text: `\u83B7\u53D6\u89C6\u9891\u53F7\u4F5C\u54C1\u5217\u8868\u5931\u8D25: ${message}` }]
787
+ };
788
+ }
789
+ }
790
+ );
791
+ server.registerTool(
792
+ "dataclaw_channels_resolve_share_url",
793
+ {
794
+ description: "\u6839\u636E\u5FAE\u4FE1\u89C6\u9891\u53F7\u5206\u4EAB\u6587\u672C\u6216\u77ED\u94FE\u89E3\u6790\u4F5C\u54C1\u5168\u91CF\u6570\u636E\u3001\u4E92\u52A8\u6307\u6807\u53CA\u65E0\u6C34\u5370\u9AD8\u6E05\u89C6\u9891\u6E90",
795
+ inputSchema: ChannelsResolveShareUrlSchema.shape
796
+ },
797
+ async (args) => {
798
+ try {
799
+ const result = await client.resolveChannelsShareUrl(args);
800
+ return {
801
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
802
+ };
803
+ } catch (err) {
804
+ const message = err instanceof Error ? err.message : String(err);
805
+ return {
806
+ isError: true,
807
+ content: [{ type: "text", text: `\u89E3\u6790\u89C6\u9891\u53F7\u5206\u4EAB\u94FE\u63A5\u5931\u8D25: ${message}` }]
808
+ };
809
+ }
810
+ }
811
+ );
812
+ }
813
+
814
+ // src/mcp/server.ts
815
+ function createMcpServer(client = new DataClawClient()) {
816
+ const server = new McpServer({
817
+ name: "dataclaw-agent-kit",
818
+ version: "0.1.0"
819
+ });
820
+ registerMediaTools(server, client);
821
+ registerDouyinTools(server, client);
822
+ registerChannelsTools(server, client);
823
+ return server;
824
+ }
825
+ async function startMcpServer(client = new DataClawClient()) {
826
+ const server = createMcpServer(client);
827
+ const transport = new StdioServerTransport();
828
+ console.error("\u{1F99E} [DataClaw MCP Server] \u6B63\u5728\u542F\u52A8 (stdio \u4F20\u8F93\u534F\u8BAE)...");
829
+ if (!client.isConfigured) {
830
+ console.error(
831
+ "\u26A0\uFE0F [DataClaw] \u672A\u68C0\u6D4B\u5230 DATACLAW_API_KEY \u73AF\u5883\u53D8\u91CF\u3002\u8BF7\u786E\u4FDD\u5728 MCP Client \u914D\u7F6E\u4E2D\u6B63\u786E\u4F20\u9012\u4E86 env.DATACLAW_API_KEY\u3002"
832
+ );
833
+ }
834
+ await server.connect(transport);
835
+ console.error("\u{1F680} [DataClaw MCP Server] \u8FD0\u884C\u5C31\u7EEA\uFF0C\u5DF2\u6CE8\u518C 10 \u4E2A\u6807\u51C6\u5316 Tools\u3002");
836
+ }
837
+
838
+ // src/cli/detect.ts
839
+ import fs2 from "fs";
840
+ import path2 from "path";
841
+ import os from "os";
842
+ function detectIdeEnvironments(targetDir = process.cwd()) {
843
+ const home = os.homedir();
844
+ const platform = process.platform;
845
+ const cursorLocal = path2.join(targetDir, ".cursor");
846
+ const cursorGlobal = path2.join(home, ".cursor");
847
+ const cursorDetected = fs2.existsSync(cursorLocal) || fs2.existsSync(cursorGlobal);
848
+ const cursorInfo = {
849
+ id: "cursor",
850
+ name: "Cursor (.cursor/rules + mcp.json)",
851
+ detected: cursorDetected,
852
+ configPath: path2.join(targetDir, ".cursor", "mcp.json"),
853
+ skillsDir: path2.join(targetDir, ".cursor", "rules")
854
+ };
855
+ let claudeDesktopConfigPath = "";
856
+ if (platform === "darwin") {
857
+ claudeDesktopConfigPath = path2.join(home, "Library", "Application Support", "Claude", "claude_desktop_config.json");
858
+ } else if (platform === "win32") {
859
+ const appData = process.env.APPDATA || path2.join(home, "AppData", "Roaming");
860
+ claudeDesktopConfigPath = path2.join(appData, "Claude", "claude_desktop_config.json");
861
+ } else {
862
+ claudeDesktopConfigPath = path2.join(home, ".config", "Claude", "claude_desktop_config.json");
863
+ }
864
+ const claudeDesktopDetected = fs2.existsSync(path2.dirname(claudeDesktopConfigPath));
865
+ const claudeDesktopInfo = {
866
+ id: "claude-desktop",
867
+ name: "Claude Desktop (claude_desktop_config.json)",
868
+ detected: claudeDesktopDetected,
869
+ configPath: claudeDesktopConfigPath
870
+ };
871
+ const claudeCodeLocal = path2.join(targetDir, ".claude");
872
+ const claudeCodeGlobal = path2.join(home, ".claude");
873
+ const claudeCodeDetected = fs2.existsSync(claudeCodeLocal) || fs2.existsSync(claudeCodeGlobal);
874
+ const claudeCodeInfo = {
875
+ id: "claude-code",
876
+ name: "Claude Code (.claude/skills)",
877
+ detected: claudeCodeDetected,
878
+ configPath: path2.join(targetDir, ".claude", "mcp.json"),
879
+ skillsDir: path2.join(targetDir, ".claude", "skills")
880
+ };
881
+ const windsurfGlobalConfig = path2.join(home, ".codeium", "windsurf", "mcp_config.json");
882
+ const windsurfLocal = path2.join(targetDir, ".windsurf");
883
+ const windsurfDetected = fs2.existsSync(windsurfLocal) || fs2.existsSync(path2.dirname(windsurfGlobalConfig));
884
+ const windsurfInfo = {
885
+ id: "windsurf",
886
+ name: "Windsurf (mcp_config.json)",
887
+ detected: windsurfDetected,
888
+ configPath: windsurfGlobalConfig,
889
+ skillsDir: path2.join(targetDir, ".windsurf", "rules")
890
+ };
891
+ return [cursorInfo, claudeDesktopInfo, claudeCodeInfo, windsurfInfo];
892
+ }
893
+
894
+ // src/cli/generator.ts
895
+ import fs3 from "fs";
896
+ import path3 from "path";
897
+ var MEDIA_SKILL_TEMPLATE = `---
898
+ description: \u89C6\u9891\u53BB\u6C34\u5370\u4E0B\u8F7D\u3001\u5168\u5E73\u53F0\u56FE\u96C6\u63D0\u53D6\u4E0E\u97F3\u89C6\u9891 Whisper \u667A\u80FD\u8F6C\u6587\u6848 SOP
899
+ globs:
900
+ ---
901
+
902
+ # \u89C6\u9891\u53BB\u6C34\u5370\u4E0B\u8F7D\u4E0E\u6587\u6848\u63D0\u53D6 SOP (Video Download & Transcribe)
903
+
904
+ \u5F53\u7528\u6237\u610F\u56FE\u6D89\u53CA\u201C\u4E0B\u8F7D\u89C6\u9891/\u65E0\u6C34\u5370\u63D0\u53D6\u201D\u3001\u201C\u63D0\u53D6\u6296\u97F3/\u89C6\u9891\u53F7/\u5C0F\u7EA2\u4E66\u7B49\u5E73\u53F0\u6587\u6848\u201D\u3001\u201C\u628A\u89C6\u9891\u8F6C\u6210\u6587\u5B57/\u63D0\u53D6\u5B57\u5E55/\u603B\u7ED3\u5927\u610F\u201D\u65F6\uFF0C\u8BF7\u4E25\u683C\u6309\u4EE5\u4E0B\u5206\u652F\u7B56\u7565\u8C03\u7528 DataClaw MCP \u5DE5\u5177\uFF1A
905
+
906
+ ## \u5206\u652F\u7B56\u7565\u4E0E\u6267\u884C\u6D41\u7A0B
907
+
908
+ ### \u5206\u652F A\uFF1A\u7528\u6237\u9700\u8981\u63D0\u53D6\u6587\u6848 / \u5B57\u5E55 / \u603B\u7ED3\u89C6\u9891\u5927\u610F\uFF08\u4E00\u6B65\u76F4\u8FBE\uFF09
909
+ **\u6838\u5FC3\u539F\u5219**\uFF1A\u8F6C\u6587\u6848**\u65E0\u9700**\u5148\u8C03\u7528\u89E3\u6790\u5DE5\u5177\u83B7\u53D6\u76F4\u94FE\uFF0C\u76F4\u63A5\u8C03\u7528 \\\`dataclaw_audio_transcribe\\\` \u4E00\u6B65\u76F4\u8FBE\uFF0C\u8017\u65F6\u66F4\u77ED\u4E14\u652F\u6301\u5B98\u65B9\u5B57\u5E55\u7F13\u5B58\u79D2\u56DE\u3002
910
+
911
+ 1. **\u76F4\u63A5\u8C03\u7528\u8F6C\u6587\u6848\u5DE5\u5177** \\\`dataclaw_audio_transcribe\\\`\uFF1A
912
+ - **\u7F51\u7EDC\u77ED\u89C6\u9891**\uFF1A\u4F20\u53C2 \\\`media_url = <\u7528\u6237\u63D0\u4F9B\u7684\u5206\u4EAB\u94FE\u63A5\u6216\u6587\u6848>\\\`\u3002
913
+ - \u652F\u6301\u6296\u97F3\u3001\u5C0F\u7EA2\u4E66\u3001\u5FEB\u624B\u3001B\u7AD9\u7B49\u666E\u901A\u77ED\u89C6\u9891\uFF08\u7CFB\u7EDF\u81EA\u52A8\u8D70 \\\`/tools/copy/extract\\\` \u94FE\u8DEF\uFF09\u3002
914
+ - \u652F\u6301\u5FAE\u4FE1\u89C6\u9891\u53F7\u77ED\u94FE\uFF08\u7CFB\u7EDF\u81EA\u52A8\u8D70 \\\`/tools/copy/extract-finder\\\` \u94FE\u8DEF\uFF0C\u81EA\u52A8\u5B8C\u6210 WASM \u89E3\u5BC6\uFF09\u3002
915
+ - **\u672C\u5730\u97F3\u89C6\u9891\u6587\u4EF6**\uFF1A\u4F20\u53C2 local_file_path = <\u672C\u5730\u7EDD\u5BF9\u8DEF\u5F84>\uFF1BMCP \u4F1A\u5148\u76F4\u4F20\u5BF9\u8C61\u5B58\u50A8\uFF0C\u518D\u521B\u5EFA\u5F02\u6B65\u8F6C\u5199\u4EFB\u52A1\uFF0C\u957F\u6587\u4EF6\u7B49\u5F85\u4EFB\u52A1\u5B8C\u6210\u3002
916
+ - **\u8F93\u51FA\u683C\u5F0F\u9009\u62E9**\uFF1A
917
+ - \u7528\u6237\u9700\u8981\u7EAF\u6587\u672C/\u603B\u7ED3\u5927\u610F\uFF1A\u4F20 \\\`output_format = "text"\\\`\u3002
918
+ - \u7528\u6237\u9700\u8981\u5B57\u5E55/\u65F6\u95F4\u8F74\uFF1A\u4F20 \\\`output_format = "srt"\\\`\u3002
919
+ - \u7528\u6237\u9700\u8981\u8BE6\u7EC6\u65F6\u95F4\u5207\u7247\u6570\u636E\uFF1A\u4F20 \\\`output_format = "verbose_json"\\\`\u3002
920
+
921
+ 2. **\u6587\u6848\u6392\u7248\u4E0E\u63D0\u70BC\u8F93\u51FA**\uFF1A
922
+ - \u63D0\u53D6\u6587\u6848\u540E\uFF0C\u4E3A\u7528\u6237\u6309\u4EE5\u4E0B\u7ED3\u6784\u6574\u7406\u5448\u73B0\uFF1A
923
+ - **\u3010\u4E3B\u9898\u6982\u8981\u3011**\uFF1A\u4E00\u53E5\u8BDD\u6982\u62EC\u89C6\u9891\u6838\u5FC3\u5185\u5BB9\u3002
924
+ - **\u3010\u5173\u952E\u91D1\u53E5 / \u6838\u5FC3\u77E5\u8BC6\u70B9\u3011**\uFF1A3~5 \u6761\u9AD8\u4EF7\u503C\u8981\u70B9\u3002
925
+ - **\u3010\u9010\u6BB5\u9010\u53E5\u6587\u6848\u3011**\uFF1A\u6E05\u6D17\u53BB\u9664\u53E3\u7656\u5206\u6BB5\u6587\u7A3F\uFF08\u82E5\u9009\u4E86 srt \u5219\u63D0\u4F9B\u6807\u51C6\u5E26\u65F6\u95F4\u8F74\u5B57\u5E55\u683C\u5F0F\uFF09\u3002
926
+
927
+ ---
928
+
929
+ ### \u5206\u652F B\uFF1A\u7528\u6237\u9700\u8981\u4E0B\u8F7D\u65E0\u6C34\u5370\u89C6\u9891 / \u83B7\u53D6\u9AD8\u6E05\u56FE\u96C6
930
+ \u5F53\u7528\u6237\u610F\u56FE\u662F\u201C\u4E0B\u8F7D\u89C6\u9891\u201D\u3001\u201C\u4FDD\u5B58\u539F\u753B\u7D20\u6750\u201D\u3001\u201C\u53BB\u9664\u6C34\u5370\u201D\u6216\u201C\u4E0B\u8F7D\u5C0F\u7EA2\u4E66/\u6296\u97F3\u56FE\u96C6\u201D\u65F6\uFF1A
931
+
932
+ 1. **\u8C03\u7528\u901A\u7528\u53BB\u6C34\u5370\u5DE5\u5177** \\\`dataclaw_video_resolve\\\`\uFF1A
933
+ - \u4F20\u53C2 \\\`url = <\u7528\u6237\u63D0\u4F9B\u7684\u94FE\u63A5\u6216\u5305\u542B\u94FE\u63A5\u7684\u6587\u6848>\\\`\u3002
934
+ - \u7CFB\u7EDF\u81EA\u52A8\u8BC6\u522B\u5E73\u53F0\uFF08\u5BF9\u5E94 \\\`/tools/material/remove-watermark\\\`\uFF09\uFF0C\u652F\u6301\u6296\u97F3\u3001\u89C6\u9891\u53F7\u3001\u5C0F\u7EA2\u4E66\u3001\u5FEB\u624B\u3001TikTok \u7B49\u5168\u5E73\u53F0\u3002
935
+ 2. **\u4E0B\u8F7D\u5F15\u5BFC\u5448\u73B0**\uFF1A
936
+ - **\u82E5\u662F\u89C6\u9891**\uFF1A\u8FD4\u56DE\u65E0\u6C34\u5370\u9AD8\u6E05\u76F4\u94FE \\\`video_play_url\\\`\u3001\u89C6\u9891\u5C01\u9762 \\\`cover_url\\\`\u3001\u6807\u9898 \\\`title\\\`\u3001\u4F5C\u8005 \\\`author\\\` \u53CA\u65F6\u957F \\\`duration\\\`\u3002
937
+ - **\u82E5\u662F\u56FE\u96C6**\uFF1A\u8FD4\u56DE\u5168\u91CF\u65E0\u6C34\u5370\u9AD8\u6E05\u56FE\u7247\u76F4\u94FE\u5217\u8868 \\\`images\\\`\u3002
938
+ - \u5F15\u5BFC\u7528\u6237\u5728\u6D4F\u89C8\u5668\u4E2D\u70B9\u51FB\u4E0B\u8F7D\uFF0C\u6216\u63D0\u4F9B\u7EC8\u7AEF \\\`curl / wget\\\` \u4E0B\u8F7D\u547D\u4EE4\u3002
939
+ `;
940
+ var DOUYIN_SKILL_TEMPLATE = `---
941
+ description: \u6296\u97F3\u70ED\u95E8\u89C6\u9891\u590D\u76D8\u3001\u7206\u6B3E\u6587\u6848\u62C6\u89E3\u4E0E\u8BC4\u8BBA\u533A\u75DB\u70B9\u6316\u6398 SOP
942
+ globs:
943
+ ---
944
+
945
+ # \u6296\u97F3\u7206\u6B3E\u89C6\u9891\u590D\u76D8\u4E0E\u75DB\u70B9\u5206\u6790 SOP (Douyin Hot Video Audit)
946
+
947
+ \u5F53\u7528\u6237\u610F\u56FE\u6D89\u53CA\u201C\u5206\u6790\u6296\u97F3\u4E0A\u505A [\u54C1\u7C7B/\u884C\u4E1A] \u7684\u7206\u6B3E\u89C6\u9891\u201D\u3001\u201C\u8C03\u7814\u7ADE\u54C1\u8BC4\u8BBA\u533A\u75DB\u70B9/\u7528\u6237\u53CD\u9988\u201D\u3001\u201C\u62C6\u89E3\u5BF9\u6807\u8D26\u53F7\u7684\u9AD8\u8D5E\u811A\u672C\u201D\u65F6\uFF0C\u8BF7\u4E25\u683C\u9075\u5FAA\u4EE5\u4E0B\u6B65\u9AA4\uFF1A
948
+
949
+ ## \u6267\u884C\u6D41\u7A0B
950
+
951
+ 1. **\u5173\u952E\u8BCD\u68C0\u7D22\u70ED\u95E8\u89C6\u9891**\uFF1A
952
+ - \u8C03\u7528\u5DE5\u5177 \\\`dataclaw_dy_search_videos\\\`\uFF1A
953
+ - \\\`keyword\\\`: \u7528\u6237\u7684\u76EE\u6807\u884C\u4E1A\u3001\u4EA7\u54C1\u6216\u4E3B\u9898\u5173\u952E\u8BCD\u3002
954
+ - \\\`sort_type\\\`: \u4F18\u5148\u4F20 \\\`"most_liked"\\\`\uFF08\u6700\u591A\u70B9\u8D5E\uFF09\u6216 \\\`"most_recent"\\\`\uFF08\u6700\u65B0\uFF09\u3002
955
+ - \\\`publish_time\\\`: \\\`"0"\\\` \u4E0D\u9650\uFF0C\\\`"1"\\\` \u6700\u8FD1\u4E00\u5929\uFF0C\\\`"7"\\\` \u6700\u8FD1\u4E00\u5468\uFF0C\\\`"180"\\\` \u6700\u8FD1\u534A\u5E74\u3002
956
+ - \u9875\u5927\u5C0F\u56FA\u5B9A\uFF0C\u4E0D\u8981\u4F9D\u8D56 \\\`count\\\`\u3002\u7FFB\u9875\u5E26\u56DE\u4E0A\u6B21\u8FD4\u56DE\u7684 \\\`cursor\\\`\u3001\\\`search_id\\\`\u3001\\\`backtrace\\\`\u3002
957
+ - \u7B5B\u9009\u51FA\u70B9\u8D5E\u91CF\u4E0E\u4E92\u52A8\u6700\u9AD8\u7684 TOP 3~5 \u4E2A\u89C6\u9891\u4F5C\u54C1\u3002
958
+
959
+ 2. **\u83B7\u53D6\u4F5C\u54C1\u8BE6\u60C5\u4E0E\u4E92\u52A8\u6307\u6807**\uFF1A
960
+ - \u9488\u5BF9\u7B5B\u9009\u51FA\u7684\u76EE\u6807\u89C6\u9891\uFF0C\u8C03\u7528 \\\`dataclaw_dy_get_video_detail\\\`\u3002
961
+ - \u53EA\u62FF\u6807\u9898\u3001\u4F5C\u8005\u3001\u5C01\u9762\u3001\u64AD\u653E\u5730\u5740\u548C\u70B9\u8D5E\u3001\u8BC4\u8BBA\u3001\u5206\u4EAB\u3001\u6536\u85CF\u6570\u3002\u6CA1\u6709\u64AD\u653E\u91CF\uFF0C\u4E5F\u6CA1\u6709\u79D2\u7EA7\u4E92\u52A8\u8D8B\u52BF\u3002
962
+ - \u9700\u8981\u64AD\u653E\u91CF\u6216\u5B8C\u64AD\u7387\u65F6\uFF0C\u53E6\u8C03 \\\`dataclaw_dy_get_play_stats\\\`\uFF0C\u4F20\u540C\u4E00\u4E2A \\\`url\\\`\u3002\u8BE6\u60C5\u548C\u64AD\u653E\u91CF\u7684\u8BF7\u6C42\u4F53\u5B57\u6BB5\u90FD\u662F \\\`url\\\`\uFF0C\u4E0D\u8981\u4F20 \\\`share_url\\\`\u3002
963
+
964
+ 3. **\u6293\u53D6\u8BC4\u8BBA**\uFF1A
965
+ - \u9488\u5BF9\u76EE\u6807\u89C6\u9891\uFF0C\u8C03\u7528 \\\`dataclaw_dy_get_comments\\\`\uFF0C\u4F20 \\\`url\\\` \u6216\u7EAF\u6570\u5B57 \\\`aweme_id\\\`\u3002\u6BCF\u9875\u9ED8\u8BA4 50 \u6761\uFF0C\u6700\u5927 50\u3002\u8981\u66F4\u591A\u5C31\u7528\u4E0A\u6B21\u8FD4\u56DE\u7684 \\\`cursor\\\` \u518D\u7FFB\u9875\u3002
966
+ - \u91CD\u70B9\u63D0\u53D6\u9AD8\u8D5E\u70ED\u8BC4\u4E0E\u69FD\u70B9\u3002
967
+
968
+ 4. **\u7206\u6B3E\u811A\u672C\u9010\u5B57\u6587\u6848\u63D0\u70BC\uFF08\u6309\u9700\uFF09**\uFF1A
969
+ - \u82E5\u7528\u6237\u9700\u8981\u6DF1\u5165\u62C6\u89E3\u7206\u6B3E\u7684\u53F0\u8BCD\u811A\u672C\u6216\u94A9\u5B50\u8BBE\u8BA1\uFF0C\u76F4\u63A5\u8C03\u7528 \\\`dataclaw_audio_transcribe\\\`\uFF08\u4F20\u5165\u8BE5\u89C6\u9891\u7684 share_url\uFF09\uFF0C\u83B7\u53D6\u9010\u5B57\u53E3\u64AD\u7A3F\u3002
970
+
971
+ 5. **\u7EFC\u5408\u590D\u76D8\u5206\u6790\u8F93\u51FA**\uFF1A
972
+ - \u7EFC\u5408\u4EE5\u4E0A\u6570\u636E\uFF0C\u4E3A\u7528\u6237\u8F93\u51FA\u6DF1\u5EA6\u590D\u76D8\u62A5\u544A\uFF1A
973
+ - **\u3010\u9EC4\u91D1\u5F00\u5934\u62C6\u89E3\u3011**\uFF1A\u524D 3 \u79D2\u94A9\u5B50\u89C6\u89C9\u4E0E\u6587\u6848\u8BBE\u8BA1\uFF0C\u5206\u6790\u5176\u9AD8\u5B8C\u64AD\u539F\u56E0\u3002
974
+ - **\u3010\u811A\u672C\u7ED3\u6784\u62C6\u89E3\u3011**\uFF1A\u75DB\u70B9\u5F15\u53D1 -> \u4EF7\u503C\u9610\u8FF0 -> \u884C\u52A8\u53EC\u5524 (CTA) \u7684\u8282\u594F\u5206\u5E03\u3002
975
+ - **\u3010\u9AD8\u9891\u5171\u9E23\u8BCD\u4E91\u3011**\uFF1A\u89C2\u4F17\u8BA8\u8BBA\u6700\u70ED\u70C8\u7684\u5173\u952E\u8BCD\u4E0E\u60C5\u7EEA\u503E\u5411\u3002
976
+ - **\u3010\u75DB\u70B9\u4E0E\u672A\u6EE1\u8DB3\u9700\u6C42\u3011**\uFF1A\u8BC4\u8BBA\u533A\u9AD8\u8D5E\u5410\u69FD\u3001\u7591\u95EE\u53CA\u6F5C\u5728\u4E70\u70B9\u3002
977
+ - **\u3010\u7206\u6B3E\u53EF\u590D\u5236\u5EFA\u8BAE\u3011**\uFF1A\u603B\u7ED3 3 \u6761\u53EF\u76F4\u63A5\u843D\u5730\u7684\u811A\u672C\u521B\u4F5C\u5EFA\u8BAE\u3002
978
+ `;
979
+ var CHANNELS_SKILL_TEMPLATE = `---
980
+ description: \u5FAE\u4FE1\u89C6\u9891\u53F7\u521B\u4F5C\u8005\u6DF1\u5EA6\u62C6\u89E3\u3001\u7206\u6B3E Feed \u4E0E\u4E92\u52A8\u76D1\u63A7 SOP
981
+ globs:
982
+ ---
983
+
984
+ # \u5FAE\u4FE1\u89C6\u9891\u53F7\u8FBE\u4EBA\u4E0E\u4F5C\u54C1\u76D1\u63A7 SOP (Channels Creator Monitor)
985
+
986
+ \u5F53\u7528\u6237\u610F\u56FE\u6D89\u53CA\u201C\u62C6\u89E3\u8FD9\u4E2A\u89C6\u9891\u53F7\u535A\u4E3B\u201D\u3001\u201C\u770B\u770B\u8FD9\u4E2A\u89C6\u9891\u53F7\u6700\u8FD1\u53D1\u4E86\u4EC0\u4E48\u201D\u3001\u201C\u5206\u6790\u89C6\u9891\u53F7\u7ADE\u54C1\u8D26\u53F7\u7684\u8868\u73B0\u201D\u65F6\uFF0C\u8BF7\u4E25\u683C\u9075\u5FAA\u4EE5\u4E0B\u6B65\u9AA4\uFF1A
987
+
988
+ ## \u6267\u884C\u6D41\u7A0B
989
+
990
+ 1. **\u521B\u4F5C\u8005\u68C0\u7D22\u4E0E\u5B9A\u4F4D**\uFF1A
991
+ - \u82E5\u7528\u6237\u63D0\u4F9B\u4E86\u535A\u4E3B\u6635\u79F0\u6216\u5173\u952E\u8BCD\uFF0C\u8C03\u7528 \\\`dataclaw_channels_search_authors\\\` \u8FDB\u884C\u641C\u7D22\u3002
992
+ - \u786E\u8BA4\u76EE\u6807\u4F5C\u8005\uFF0C\u83B7\u53D6\u5176\u552F\u4E00\u6807\u8BC6 \\\`finder_username\\\`\u3001\u5934\u50CF\u53CA\u8BA4\u8BC1\u7B80\u4ECB\u3002
993
+
994
+ 2. **\u62C9\u53D6\u8FD1\u671F\u4F5C\u54C1 Feed**\uFF1A
995
+ - \u8C03\u7528 \\\`dataclaw_channels_get_author_feeds\\\`\uFF0C\u4F20\u5165\u4F5C\u8005 \\\`finder_username\\\`\u3002
996
+ - \u6BCF\u9875 15 \u6761\u3002\u8981 10~30 \u6761\u5C31\u518D\u4F20\u4E0A\u6B21\u8FD4\u56DE\u7684 \\\`last_buffer\\\` / \\\`buffer\\\` \u7FFB\u4E00\u9875\u3002
997
+
998
+ 3. **\u4F5C\u54C1\u5168\u91CF\u6570\u636E\u4E0E\u6587\u6848/\u89C6\u9891\u63D0\u53D6\uFF08\u6309\u9700\uFF09**\uFF1A
999
+ - **\u82E5\u9700\u63D0\u53D6\u89C6\u9891\u53E3\u64AD\u6587\u6848**\uFF1A\u76F4\u63A5\u8C03\u7528 \\\`dataclaw_audio_transcribe\\\`\uFF08\u4F20\u5165\u4F5C\u54C1\u5206\u4EAB\u77ED\u94FE\uFF0C\u7CFB\u7EDF\u5E95\u5C42\u81EA\u52A8\u901A\u8FC7 WASM \u89E3\u5BC6\u89C6\u9891\u53F7\u52A0\u5BC6\u89C6\u9891\u6D41\u5E76\u8F6C\u5199\u4E3A\u6587\u6848\u6216 SRT \u5B57\u5E55\uFF09\u3002
1000
+ - **\u82E5\u9700\u65E0\u6C34\u5370\u4E0B\u8F7D\u89C6\u9891**\uFF1A\u8C03\u7528 \\\`dataclaw_video_resolve\\\`\uFF08\u4F20\u5165\u89C6\u9891\u53F7\u5206\u4EAB\u77ED\u94FE\uFF09\uFF0C\u63D0\u53D6\u9AD8\u6E05\u4E0B\u8F7D\u76F4\u94FE\u3002
1001
+
1002
+ 4. **\u8FBE\u4EBA\u77E9\u9635\u4E0E\u8FD0\u8425\u7B56\u7565\u63D0\u70BC**\uFF1A
1003
+ - \u4E3A\u7528\u6237\u8F93\u51FA\u6DF1\u5EA6\u5206\u6790\u62A5\u544A\uFF1A
1004
+ - **\u3010\u53D1\u6587\u8282\u594F\u4E0E\u504F\u597D\u3011**\uFF1A\u8FD1\u671F\u66F4\u65B0\u9891\u7387\u3001\u53D1\u5E03\u65F6\u95F4\u6BB5\u504F\u597D\u3002
1005
+ - **\u3010\u7206\u6B3E\u5185\u5BB9\u7279\u5F81\u3011**\uFF1A\u70B9\u8D5E/\u8F6C\u53D1\u7834\u4E07\u7684\u9898\u6750\u7279\u5F81\u4E0E\u5C01\u9762\u6587\u6848\u5957\u8DEF\u3002
1006
+ - **\u3010\u53E3\u64AD\u53F0\u8BCD\u5957\u8DEF\u3011**\uFF1A\u9488\u5BF9\u4EE3\u8868\u4F5C\u63D0\u53D6\u7684\u53F0\u8BCD\u811A\u672C\u8282\u594F\u4E0E\u8BDD\u672F\u62C6\u89E3\u3002
1007
+ - **\u3010\u4E92\u52A8\u8F6C\u5316\u5206\u6790\u3011**\uFF1A\u8BC4\u8BBA\u533A\u4E92\u52A8\u70ED\u70B9\u4E0E\u7528\u6237\u79C1\u57DF\u8F6C\u5316\u5F15\u5BFC\u65B9\u5F0F\u3002
1008
+ - **\u3010\u6A21\u4EFF\u4E0E\u8D85\u8D8A\u5EFA\u8BAE\u3011**\uFF1A\u5BF9\u6807\u540C\u884C\u7684\u5DEE\u5F02\u5316\u5B9A\u4F4D\u7B56\u7565\u3002
1009
+ `;
1010
+ function writeMcpConfig(configFilePath, apiKey, customBaseUrl) {
1011
+ const dir = path3.dirname(configFilePath);
1012
+ if (!fs3.existsSync(dir)) {
1013
+ fs3.mkdirSync(dir, { recursive: true });
1014
+ }
1015
+ let existingJson = {};
1016
+ if (fs3.existsSync(configFilePath)) {
1017
+ try {
1018
+ const content = fs3.readFileSync(configFilePath, "utf-8");
1019
+ const parsed = JSON.parse(content);
1020
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
1021
+ existingJson = parsed;
1022
+ }
1023
+ } catch {
1024
+ }
1025
+ }
1026
+ const existingMcpServers = existingJson.mcpServers && typeof existingJson.mcpServers === "object" && !Array.isArray(existingJson.mcpServers) ? existingJson.mcpServers : {};
1027
+ const envVars = {
1028
+ DATACLAW_API_KEY: apiKey
1029
+ };
1030
+ if (customBaseUrl) {
1031
+ envVars.DATACLAW_BASE_URL = customBaseUrl;
1032
+ }
1033
+ existingMcpServers.dataclaw = {
1034
+ command: "npx",
1035
+ args: ["-y", "dataclaw-agent-kit", "mcp"],
1036
+ env: envVars
1037
+ };
1038
+ existingJson.mcpServers = existingMcpServers;
1039
+ fs3.writeFileSync(configFilePath, JSON.stringify(existingJson, null, 2), "utf-8");
1040
+ }
1041
+ function writeSkillsToDir(skillsDir, skillKeys, extension = ".mdc") {
1042
+ if (!fs3.existsSync(skillsDir)) {
1043
+ fs3.mkdirSync(skillsDir, { recursive: true });
1044
+ }
1045
+ const written = [];
1046
+ if (skillKeys.includes("media")) {
1047
+ const file = path3.join(skillsDir, `dataclaw-media${extension}`);
1048
+ fs3.writeFileSync(file, MEDIA_SKILL_TEMPLATE, "utf-8");
1049
+ written.push(file);
1050
+ }
1051
+ if (skillKeys.includes("douyin")) {
1052
+ const file = path3.join(skillsDir, `dataclaw-douyin${extension}`);
1053
+ fs3.writeFileSync(file, DOUYIN_SKILL_TEMPLATE, "utf-8");
1054
+ written.push(file);
1055
+ }
1056
+ if (skillKeys.includes("channels")) {
1057
+ const file = path3.join(skillsDir, `dataclaw-channels${extension}`);
1058
+ fs3.writeFileSync(file, CHANNELS_SKILL_TEMPLATE, "utf-8");
1059
+ written.push(file);
1060
+ }
1061
+ return written;
1062
+ }
1063
+
1064
+ // src/cli/index.ts
1065
+ import path4 from "path";
1066
+ import pc from "picocolors";
1067
+ import prompts from "prompts";
1068
+ async function runInitWizard() {
1069
+ console.log(
1070
+ pc.cyan(`
1071
+ \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
1072
+ \u2502 \u{1F99E} DataClaw AI Agent Kit (MCP & Skills) \u5B89\u88C5\u5411\u5BFC \u2502
1073
+ \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
1074
+ `)
1075
+ );
1076
+ const defaultApiKey = process.env.DATACLAW_API_KEY || "";
1077
+ const detectedIdes = detectIdeEnvironments();
1078
+ const keyResponse = await prompts({
1079
+ type: "password",
1080
+ name: "apiKey",
1081
+ message: "\u8BF7\u8F93\u5165\u60A8\u7684 DataClaw API-Key (\u4ECE\u4E2A\u4EBA\u4E2D\u5FC3\u83B7\u53D6):",
1082
+ initial: defaultApiKey,
1083
+ validate: (val) => val.trim().length > 0 ? true : "API-Key \u4E0D\u80FD\u4E3A\u7A7A"
1084
+ });
1085
+ const apiKey = keyResponse.apiKey?.trim();
1086
+ if (!apiKey) {
1087
+ console.log(pc.yellow("\u5DF2\u53D6\u6D88\u5B89\u88C5\u5411\u5BFC\u3002"));
1088
+ return;
1089
+ }
1090
+ const client = new DataClawClient({ apiKey });
1091
+ console.log(pc.dim("\u6B63\u5728\u9A8C\u8BC1 API-Key \u6709\u6548\u6027..."));
1092
+ try {
1093
+ const me = await client.getMe();
1094
+ console.log(
1095
+ pc.green(`\u2714 \u9274\u6743\u9A8C\u8BC1\u6210\u529F\uFF01\u5F53\u524D\u7528\u6237: ${me.username} (\u79EF\u5206\u4F59\u989D: ${me.credit ?? "\u65E0\u9650\u5236"} \u70B9)`)
1096
+ );
1097
+ } catch (err) {
1098
+ const errorMsg = err instanceof Error ? err.message : String(err);
1099
+ console.log(pc.yellow(`\u26A0\uFE0F \u9274\u6743\u9A8C\u8BC1\u672A\u901A\u8FC7: ${errorMsg}`));
1100
+ const confirm = await prompts({
1101
+ type: "confirm",
1102
+ name: "continueAnyway",
1103
+ message: "\u662F\u5426\u4ECD\u8981\u7EE7\u7EED\u4F7F\u7528\u6B64 API-Key \u5B8C\u6210\u5B89\u88C5\uFF1F",
1104
+ initial: true
1105
+ });
1106
+ if (!confirm.continueAnyway) {
1107
+ console.log(pc.yellow("\u5DF2\u53D6\u6D88\u5B89\u88C5\u3002"));
1108
+ return;
1109
+ }
1110
+ }
1111
+ const ideChoices = detectedIdes.map((ide) => ({
1112
+ title: ide.name,
1113
+ value: ide.id,
1114
+ selected: ide.detected
1115
+ }));
1116
+ const ideResponse = await prompts({
1117
+ type: "multiselect",
1118
+ name: "targets",
1119
+ message: "\u8BF7\u9009\u62E9\u9700\u8981\u6CE8\u5165\u914D\u7F6E\u7684 AI \u5BBF\u4E3B\u73AF\u5883:",
1120
+ choices: ideChoices,
1121
+ min: 1,
1122
+ hint: "- \u7A7A\u683C\u5207\u6362\u9009\u4E2D\uFF0C\u56DE\u8F66\u786E\u8BA4"
1123
+ });
1124
+ const selectedTargets = ideResponse.targets || [];
1125
+ if (selectedTargets.length === 0) {
1126
+ console.log(pc.yellow("\u672A\u9009\u62E9\u4EFB\u4F55\u5BBF\u4E3B\u73AF\u5883\uFF0C\u5DF2\u9000\u51FA\u3002"));
1127
+ return;
1128
+ }
1129
+ const skillResponse = await prompts({
1130
+ type: "multiselect",
1131
+ name: "skills",
1132
+ message: "\u8BF7\u52FE\u9009\u9700\u8981\u542F\u7528\u7684\u7B2C\u4E00\u671F\u573A\u666F\u6280\u80FD:",
1133
+ choices: [
1134
+ {
1135
+ title: "[\u89C6\u9891\u5904\u7406] \u89C6\u9891\u65E0\u6C34\u5370\u76F4\u94FE\u63D0\u53D6\u4E0E\u97F3\u9891 Whisper \u8F6C\u5199 SOP",
1136
+ value: "media",
1137
+ selected: true
1138
+ },
1139
+ {
1140
+ title: "[\u6296\u97F3\u5206\u6790] \u70ED\u95E8\u89C6\u9891\u68C0\u7D22\u3001\u7206\u6B3E\u6587\u6848\u4E0E\u8BC4\u8BBA\u75DB\u70B9\u6316\u6398 SOP",
1141
+ value: "douyin",
1142
+ selected: true
1143
+ },
1144
+ {
1145
+ title: "[\u5FAE\u4FE1\u89C6\u9891\u53F7] \u8FBE\u4EBA\u4E3B\u9875\u6DF1\u5EA6\u62C6\u89E3\u4E0E\u4F5C\u54C1\u52A8\u6001\u76D1\u63A7 SOP",
1146
+ value: "channels",
1147
+ selected: true
1148
+ }
1149
+ ],
1150
+ min: 1
1151
+ });
1152
+ const selectedSkills = skillResponse.skills || [];
1153
+ if (selectedSkills.length === 0) {
1154
+ console.log(pc.yellow("\u672A\u9009\u62E9\u4EFB\u4F55\u6280\u80FD\uFF0C\u5DF2\u9000\u51FA\u3002"));
1155
+ return;
1156
+ }
1157
+ console.log("");
1158
+ for (const target of selectedTargets) {
1159
+ const ideMeta = detectedIdes.find((item) => item.id === target);
1160
+ if (!ideMeta) continue;
1161
+ if (ideMeta.configPath) {
1162
+ try {
1163
+ writeMcpConfig(ideMeta.configPath, apiKey);
1164
+ console.log(pc.green(`\u2714 \u914D\u7F6E\u6587\u4EF6\u5DF2\u5199\u5165: ${path4.relative(process.cwd(), ideMeta.configPath) || ideMeta.configPath}`));
1165
+ } catch (err) {
1166
+ console.error(pc.red(`\u2716 \u5199\u5165\u914D\u7F6E\u6587\u4EF6\u5931\u8D25 (${ideMeta.configPath}): ${err instanceof Error ? err.message : String(err)}`));
1167
+ }
1168
+ }
1169
+ if (ideMeta.skillsDir) {
1170
+ try {
1171
+ const ext = target === "cursor" ? ".mdc" : ".md";
1172
+ const writtenFiles = writeSkillsToDir(ideMeta.skillsDir, selectedSkills, ext);
1173
+ for (const f of writtenFiles) {
1174
+ console.log(pc.green(`\u2714 \u573A\u666F\u6280\u80FD\u5DF2\u751F\u6210: ${path4.relative(process.cwd(), f) || f}`));
1175
+ }
1176
+ } catch (err) {
1177
+ console.error(pc.red(`\u2716 \u5199\u5165\u6280\u80FD\u6587\u4EF6\u5931\u8D25: ${err instanceof Error ? err.message : String(err)}`));
1178
+ }
1179
+ }
1180
+ }
1181
+ console.log(
1182
+ pc.cyan(`
1183
+ \u{1F389} \u5B89\u88C5\u5B8C\u6210\uFF01\u5728 Cursor Composer \u6216 Claude \u4E2D\u76F4\u63A5\u63D0\u95EE\u4F53\u9A8C:
1184
+ "\u5E2E\u6211\u628A\u8FD9\u4E2A\u6296\u97F3\u94FE\u63A5\u7684\u5185\u5BB9\u8F6C\u6210\u6587\u7AE0: https://v.douyin.com/..."
1185
+ "\u5206\u6790\u6296\u97F3\u4E0A\u505A\u5C0F\u5403\u52A0\u76DF\u7684\u70B9\u8D5E\u6700\u9AD8\u89C6\u9891\u4E0E\u8BC4\u8BBA\u75DB\u70B9"
1186
+ `)
1187
+ );
1188
+ }
1189
+
1190
+ export {
1191
+ DataClawClient,
1192
+ VideoResolveSchema,
1193
+ AudioTranscribeSchema,
1194
+ DySearchVideosSchema,
1195
+ DyGetVideoDetailSchema,
1196
+ DyGetPlayStatsSchema,
1197
+ DyGetCommentsSchema,
1198
+ DyGetUserProfileSchema,
1199
+ ChannelsSearchAuthorsSchema,
1200
+ ChannelsGetAuthorFeedsSchema,
1201
+ ChannelsResolveShareUrlSchema,
1202
+ createMcpServer,
1203
+ startMcpServer,
1204
+ detectIdeEnvironments,
1205
+ MEDIA_SKILL_TEMPLATE,
1206
+ DOUYIN_SKILL_TEMPLATE,
1207
+ CHANNELS_SKILL_TEMPLATE,
1208
+ writeMcpConfig,
1209
+ writeSkillsToDir,
1210
+ runInitWizard
1211
+ };
1212
+ //# sourceMappingURL=chunk-T5OI3DCY.js.map