@claudeink/mcp-server 0.7.0 → 0.9.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.
- package/dist/index.js +1157 -226
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -8,6 +8,8 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
8
8
|
import { z } from "zod";
|
|
9
9
|
import { mkdir as mkdir2, readFile as readFile3 } from "fs/promises";
|
|
10
10
|
import { join as join3 } from "path";
|
|
11
|
+
import { execSync } from "child_process";
|
|
12
|
+
import { readdirSync } from "fs";
|
|
11
13
|
|
|
12
14
|
// src/lib/state.ts
|
|
13
15
|
import { readFile, writeFile, mkdir, chmod } from "fs/promises";
|
|
@@ -37,6 +39,7 @@ var DEFAULT_STATE = {
|
|
|
37
39
|
configs: {},
|
|
38
40
|
crawlerSources: {},
|
|
39
41
|
writingMasters: {},
|
|
42
|
+
knowledge: {},
|
|
40
43
|
tagQueue: {
|
|
41
44
|
queue: [],
|
|
42
45
|
failed: []
|
|
@@ -47,9 +50,17 @@ async function ensureDir() {
|
|
|
47
50
|
await mkdir(getClaudeinkDir(), { recursive: true });
|
|
48
51
|
}
|
|
49
52
|
async function readState() {
|
|
53
|
+
const statePath = getStatePath();
|
|
54
|
+
let raw;
|
|
50
55
|
try {
|
|
51
|
-
|
|
52
|
-
|
|
56
|
+
raw = await readFile(statePath, "utf-8");
|
|
57
|
+
} catch {
|
|
58
|
+
const state = { ...DEFAULT_STATE };
|
|
59
|
+
state.config.workflowDir = getWorkDir();
|
|
60
|
+
return state;
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
const parsed = JSON.parse(raw);
|
|
53
64
|
const state = {
|
|
54
65
|
...DEFAULT_STATE,
|
|
55
66
|
...parsed,
|
|
@@ -59,6 +70,12 @@ async function readState() {
|
|
|
59
70
|
state.config.workflowDir = getWorkDir();
|
|
60
71
|
return state;
|
|
61
72
|
} catch {
|
|
73
|
+
const backupPath = statePath + ".corrupted." + Date.now();
|
|
74
|
+
try {
|
|
75
|
+
await writeFile(backupPath, raw, "utf-8");
|
|
76
|
+
} catch {
|
|
77
|
+
}
|
|
78
|
+
console.error(`[ClaudeInk] state.json corrupted, backed up to ${backupPath}`);
|
|
62
79
|
const state = { ...DEFAULT_STATE };
|
|
63
80
|
state.config.workflowDir = getWorkDir();
|
|
64
81
|
return state;
|
|
@@ -118,14 +135,17 @@ async function removeCrawlerSource(id) {
|
|
|
118
135
|
delete state.crawlerSources[id];
|
|
119
136
|
await writeState(state);
|
|
120
137
|
}
|
|
121
|
-
async function
|
|
138
|
+
async function mergeCloudData(cloud) {
|
|
122
139
|
const state = await readState();
|
|
140
|
+
if (cloud.sources !== void 0) state.sources = cloud.sources;
|
|
141
|
+
if (cloud.drafts !== void 0) state.drafts = cloud.drafts;
|
|
142
|
+
if (cloud.published !== void 0) state.published = cloud.published;
|
|
143
|
+
if (cloud.configs !== void 0) state.configs = cloud.configs;
|
|
144
|
+
if (cloud.crawlerSources !== void 0) state.crawlerSources = cloud.crawlerSources;
|
|
145
|
+
if (cloud.writingMasters !== void 0) state.writingMasters = cloud.writingMasters;
|
|
123
146
|
state.lastSyncAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
124
147
|
await writeState(state);
|
|
125
148
|
}
|
|
126
|
-
async function replaceState(newState) {
|
|
127
|
-
await writeState(newState);
|
|
128
|
-
}
|
|
129
149
|
async function getTagQueue() {
|
|
130
150
|
const state = await readState();
|
|
131
151
|
return {
|
|
@@ -240,6 +260,37 @@ async function markTagFailed(file) {
|
|
|
240
260
|
await saveTagQueue(queue);
|
|
241
261
|
}
|
|
242
262
|
|
|
263
|
+
// src/lib/push.ts
|
|
264
|
+
function fireAndForgetPush(payload) {
|
|
265
|
+
doPush(payload).catch(() => {
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
async function doPush(partial) {
|
|
269
|
+
const creds = await getCredentials();
|
|
270
|
+
if (!creds?.token) return;
|
|
271
|
+
const config = await getConfig();
|
|
272
|
+
const payload = {
|
|
273
|
+
sources: partial.sources || [],
|
|
274
|
+
drafts: partial.drafts || [],
|
|
275
|
+
published: partial.published || [],
|
|
276
|
+
configs: partial.configs || [],
|
|
277
|
+
crawlerSources: partial.crawlerSources || [],
|
|
278
|
+
analytics: partial.analytics || []
|
|
279
|
+
};
|
|
280
|
+
try {
|
|
281
|
+
await fetch(`${config.apiBaseUrl}/api/sync/batch`, {
|
|
282
|
+
method: "POST",
|
|
283
|
+
headers: {
|
|
284
|
+
"Content-Type": "application/json",
|
|
285
|
+
Authorization: `Bearer ${creds.token}`
|
|
286
|
+
},
|
|
287
|
+
body: JSON.stringify(payload)
|
|
288
|
+
});
|
|
289
|
+
} catch (err) {
|
|
290
|
+
console.error("[ClaudeInk] auto-push failed:", err instanceof Error ? err.message : err);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
243
294
|
// src/tools/source.ts
|
|
244
295
|
var sourceAddSchema = z.object({
|
|
245
296
|
title: z.string().describe("\u7D20\u6750\u6807\u9898"),
|
|
@@ -267,7 +318,7 @@ async function sourceAdd(input) {
|
|
|
267
318
|
}
|
|
268
319
|
})() : "manual",
|
|
269
320
|
published: date,
|
|
270
|
-
url: input.url
|
|
321
|
+
...input.url ? { url: input.url } : {}
|
|
271
322
|
};
|
|
272
323
|
if (input.tags && input.tags.length > 0) {
|
|
273
324
|
meta.tags = input.tags;
|
|
@@ -284,6 +335,18 @@ async function sourceAdd(input) {
|
|
|
284
335
|
publishedAt: date,
|
|
285
336
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
286
337
|
});
|
|
338
|
+
fireAndForgetPush({
|
|
339
|
+
sources: [{
|
|
340
|
+
id: slug,
|
|
341
|
+
title: input.title,
|
|
342
|
+
source: meta.source,
|
|
343
|
+
tags: input.tags || [],
|
|
344
|
+
sourceUrl: input.url || "",
|
|
345
|
+
coverUrl: "",
|
|
346
|
+
sourceIcon: "",
|
|
347
|
+
createdAt: date
|
|
348
|
+
}]
|
|
349
|
+
});
|
|
287
350
|
return {
|
|
288
351
|
success: true,
|
|
289
352
|
message: `\u7D20\u6750\u5DF2\u5165\u5E93: ${filename}\uFF08\u542B ${input.tags.length} \u4E2A\u6807\u7B7E\uFF09`,
|
|
@@ -303,6 +366,18 @@ async function sourceAdd(input) {
|
|
|
303
366
|
publishedAt: date,
|
|
304
367
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
305
368
|
});
|
|
369
|
+
fireAndForgetPush({
|
|
370
|
+
sources: [{
|
|
371
|
+
id: slug,
|
|
372
|
+
title: input.title,
|
|
373
|
+
source: meta.source,
|
|
374
|
+
tags: input.tags || [],
|
|
375
|
+
sourceUrl: input.url || "",
|
|
376
|
+
coverUrl: "",
|
|
377
|
+
sourceIcon: "",
|
|
378
|
+
createdAt: date
|
|
379
|
+
}]
|
|
380
|
+
});
|
|
306
381
|
return {
|
|
307
382
|
success: true,
|
|
308
383
|
message: `\u7D20\u6750\u5DF2\u5165\u5E93: ${filename}\uFF08\u5DF2\u52A0\u5165\u6807\u7B7E\u961F\u5217\uFF0C\u7B49\u5F85 Claude \u6253\u6807\u7B7E\uFF09`,
|
|
@@ -315,7 +390,8 @@ var sourceCrawlSchema = z.object({
|
|
|
315
390
|
});
|
|
316
391
|
async function sourceCrawl(input) {
|
|
317
392
|
const config = await getConfig();
|
|
318
|
-
const
|
|
393
|
+
const crawlerDir = join3(config.workflowDir || process.cwd(), "tools", "crawler");
|
|
394
|
+
const configPath = join3(crawlerDir, "config.json");
|
|
319
395
|
let crawlerConfig;
|
|
320
396
|
try {
|
|
321
397
|
crawlerConfig = JSON.parse(await readFile3(configPath, "utf-8"));
|
|
@@ -328,18 +404,97 @@ async function sourceCrawl(input) {
|
|
|
328
404
|
message: "\u6682\u65E0\u914D\u7F6E\u722C\u866B\u6E90\u3002\u8BF7\u5148\u7528 source.subscribe \u6DFB\u52A0\u3002"
|
|
329
405
|
};
|
|
330
406
|
}
|
|
331
|
-
const targets = input.sourceId ? crawlerConfig.sources.filter((s) => s.
|
|
407
|
+
const targets = input.sourceId ? crawlerConfig.sources.filter((s) => s.id === input.sourceId) : crawlerConfig.sources.filter((s) => s.enabled !== false);
|
|
332
408
|
if (targets.length === 0) {
|
|
333
409
|
return {
|
|
334
410
|
success: false,
|
|
335
|
-
message: `\u672A\u627E\u5230\u722C\u866B\u6E90: ${input.sourceId}`
|
|
411
|
+
message: input.sourceId ? `\u672A\u627E\u5230\u722C\u866B\u6E90: ${input.sourceId}` : "\u6CA1\u6709\u5DF2\u542F\u7528\u7684\u722C\u866B\u6E90"
|
|
336
412
|
};
|
|
337
413
|
}
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
414
|
+
const crawlScript = join3(crawlerDir, "crawl.mjs");
|
|
415
|
+
const args = input.sourceId ? `--source ${input.sourceId}` : "";
|
|
416
|
+
try {
|
|
417
|
+
execSync(`node "${crawlScript}" ${args}`, {
|
|
418
|
+
cwd: crawlerDir,
|
|
419
|
+
encoding: "utf-8",
|
|
420
|
+
timeout: 5 * 60 * 1e3,
|
|
421
|
+
// 5 minute timeout
|
|
422
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
423
|
+
});
|
|
424
|
+
let newFiles = [];
|
|
425
|
+
for (const target of targets) {
|
|
426
|
+
const sourceDir = join3(config.workflowDir, "sources", "articles", target.id);
|
|
427
|
+
try {
|
|
428
|
+
const files = readdirSync(sourceDir).filter((f) => f.endsWith(".md"));
|
|
429
|
+
for (const f of files) {
|
|
430
|
+
const filePath = `sources/articles/${target.id}/${f}`;
|
|
431
|
+
newFiles.push({
|
|
432
|
+
file: filePath,
|
|
433
|
+
title: f.replace(/^\d{4}-\d{2}-\d{2}-/, "").replace(/\.md$/, "").replace(/-/g, " "),
|
|
434
|
+
sourceId: target.id
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
} catch {
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
let queued = 0;
|
|
441
|
+
for (const item of newFiles) {
|
|
442
|
+
try {
|
|
443
|
+
const fullPath = join3(config.workflowDir, item.file);
|
|
444
|
+
const source = await readSourceFile(fullPath);
|
|
445
|
+
const slug = item.file.replace(/^.*\//, "").replace(/\.md$/, "");
|
|
446
|
+
const date = source.meta.published || (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
447
|
+
await updateSource(slug, {
|
|
448
|
+
title: source.meta.title || item.title,
|
|
449
|
+
source: item.sourceId,
|
|
450
|
+
sourceIcon: "",
|
|
451
|
+
sourceUrl: source.meta.url || "",
|
|
452
|
+
coverUrl: "",
|
|
453
|
+
tags: source.meta.tags || [],
|
|
454
|
+
publishedAt: date,
|
|
455
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
456
|
+
});
|
|
457
|
+
if (!source.meta.tags || source.meta.tags.length === 0) {
|
|
458
|
+
await addToTagQueue(item.file, source.meta.title || item.title, "crawl");
|
|
459
|
+
queued++;
|
|
460
|
+
}
|
|
461
|
+
fireAndForgetPush({
|
|
462
|
+
sources: [{
|
|
463
|
+
id: slug,
|
|
464
|
+
title: source.meta.title || item.title,
|
|
465
|
+
source: item.sourceId,
|
|
466
|
+
tags: source.meta.tags || [],
|
|
467
|
+
sourceUrl: source.meta.url || "",
|
|
468
|
+
coverUrl: "",
|
|
469
|
+
sourceIcon: "",
|
|
470
|
+
createdAt: date
|
|
471
|
+
}]
|
|
472
|
+
});
|
|
473
|
+
} catch {
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
return {
|
|
477
|
+
success: true,
|
|
478
|
+
message: [
|
|
479
|
+
`\u2705 \u722C\u53D6\u5B8C\u6210`,
|
|
480
|
+
` \u76EE\u6807\u6E90: ${targets.map((t) => t.name).join(", ")}`,
|
|
481
|
+
` \u53D1\u73B0\u6587\u7AE0: ${newFiles.length} \u7BC7`,
|
|
482
|
+
queued > 0 ? ` \u52A0\u5165\u6807\u7B7E\u961F\u5217: ${queued} \u7BC7` : "",
|
|
483
|
+
` \u5143\u6570\u636E\u5DF2\u540C\u6B65\u5230\u4E91\u7AEF`
|
|
484
|
+
].filter(Boolean).join("\n"),
|
|
485
|
+
data: {
|
|
486
|
+
sources: targets.map((t) => t.name),
|
|
487
|
+
articles: newFiles.length,
|
|
488
|
+
queued
|
|
489
|
+
}
|
|
490
|
+
};
|
|
491
|
+
} catch (err) {
|
|
492
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
493
|
+
if (message.includes("ETIMEDOUT") || message.includes("timed out")) {
|
|
494
|
+
return { success: false, message: "\u722C\u53D6\u8D85\u65F6\uFF085 \u5206\u949F\u9650\u5236\uFF09\u3002\u8BF7\u5C1D\u8BD5\u6307\u5B9A\u5355\u4E2A\u6E90: source.crawl({ sourceId: 'xxx' })" };
|
|
495
|
+
}
|
|
496
|
+
return { success: false, message: `\u722C\u53D6\u5931\u8D25: ${message.slice(0, 200)}` };
|
|
497
|
+
}
|
|
343
498
|
}
|
|
344
499
|
var sourceTagSchema = z.object({
|
|
345
500
|
mode: z.enum(["queue", "single"]).describe("queue=\u5904\u7406\u6807\u7B7E\u961F\u5217, single=\u5355\u4E2A\u6587\u4EF6\u6253\u6807\u7B7E"),
|
|
@@ -422,6 +577,19 @@ async function sourceTagApply(input) {
|
|
|
422
577
|
await updateSourceTags2(fullPath, input.tags);
|
|
423
578
|
await markTagged(input.file, input.tags.length);
|
|
424
579
|
await updateSourceTags(input.file, input.tags);
|
|
580
|
+
const slug = input.file.replace(/^.*\//, "").replace(/\.md$/, "");
|
|
581
|
+
fireAndForgetPush({
|
|
582
|
+
sources: [{
|
|
583
|
+
id: slug,
|
|
584
|
+
title: "",
|
|
585
|
+
source: "",
|
|
586
|
+
tags: input.tags,
|
|
587
|
+
sourceUrl: "",
|
|
588
|
+
coverUrl: "",
|
|
589
|
+
sourceIcon: "",
|
|
590
|
+
createdAt: ""
|
|
591
|
+
}]
|
|
592
|
+
});
|
|
425
593
|
return {
|
|
426
594
|
success: true,
|
|
427
595
|
message: `\u6807\u7B7E\u5DF2\u5199\u5165: ${input.tags.join(", ")}`,
|
|
@@ -446,7 +614,18 @@ var sourceSubscribeSchema = z2.object({
|
|
|
446
614
|
name: z2.string().optional().describe("\u8BA2\u9605\u6E90\u540D\u79F0"),
|
|
447
615
|
url: z2.string().optional().describe("RSS/\u535A\u5BA2 URL"),
|
|
448
616
|
type: z2.enum(["rss", "blog", "sitemap"]).optional().describe("\u6E90\u7C7B\u578B"),
|
|
449
|
-
icon: z2.string().optional().describe("\u6765\u6E90 icon URL")
|
|
617
|
+
icon: z2.string().optional().describe("\u6765\u6E90 icon URL"),
|
|
618
|
+
crawlConfig: z2.object({
|
|
619
|
+
articleSelector: z2.string(),
|
|
620
|
+
articleUrlPattern: z2.string().optional(),
|
|
621
|
+
pagination: z2.object({
|
|
622
|
+
pattern: z2.string(),
|
|
623
|
+
startPage: z2.number(),
|
|
624
|
+
maxPages: z2.number()
|
|
625
|
+
}).optional(),
|
|
626
|
+
excludePatterns: z2.array(z2.string()).optional(),
|
|
627
|
+
maxArticles: z2.number().optional()
|
|
628
|
+
}).optional().describe("\u722C\u53D6\u9002\u914D\u89C4\u5219\uFF08Claude \u5206\u6790\u751F\u6210\uFF09")
|
|
450
629
|
});
|
|
451
630
|
async function sourceSubscribe(input) {
|
|
452
631
|
const config = await getConfig();
|
|
@@ -474,11 +653,18 @@ async function sourceSubscribe(input) {
|
|
|
474
653
|
const newSource = {
|
|
475
654
|
id: input.id,
|
|
476
655
|
name: input.name,
|
|
477
|
-
|
|
478
|
-
type: input.type || "rss",
|
|
656
|
+
blogUrl: input.url,
|
|
657
|
+
type: input.type === "blog" ? "paginated" : input.type || "rss",
|
|
479
658
|
icon: input.icon || "",
|
|
480
659
|
enabled: true
|
|
481
660
|
};
|
|
661
|
+
if (input.crawlConfig) {
|
|
662
|
+
newSource.articleSelector = input.crawlConfig.articleSelector;
|
|
663
|
+
if (input.crawlConfig.articleUrlPattern) newSource.articleUrlPattern = input.crawlConfig.articleUrlPattern;
|
|
664
|
+
if (input.crawlConfig.pagination) newSource.pagination = input.crawlConfig.pagination;
|
|
665
|
+
if (input.crawlConfig.excludePatterns) newSource.excludePatterns = input.crawlConfig.excludePatterns;
|
|
666
|
+
if (input.crawlConfig.maxArticles) newSource.maxArticles = input.crawlConfig.maxArticles;
|
|
667
|
+
}
|
|
482
668
|
crawlerConfig.sources.push(newSource);
|
|
483
669
|
await writeFile3(configPath, JSON.stringify(crawlerConfig, null, 2));
|
|
484
670
|
await updateCrawlerSource(input.id, {
|
|
@@ -488,6 +674,18 @@ async function sourceSubscribe(input) {
|
|
|
488
674
|
icon: input.icon || "",
|
|
489
675
|
enabled: true
|
|
490
676
|
});
|
|
677
|
+
const stateAfterAdd = await readState();
|
|
678
|
+
fireAndForgetPush({
|
|
679
|
+
crawlerSources: Object.entries(stateAfterAdd.crawlerSources).map(([id, cs]) => ({
|
|
680
|
+
id,
|
|
681
|
+
name: cs.name,
|
|
682
|
+
url: cs.url,
|
|
683
|
+
type: cs.type,
|
|
684
|
+
icon: cs.icon,
|
|
685
|
+
enabled: cs.enabled,
|
|
686
|
+
...id === input.id && input.crawlConfig ? { crawlConfig: input.crawlConfig } : {}
|
|
687
|
+
}))
|
|
688
|
+
});
|
|
491
689
|
return { success: true, message: `\u8BA2\u9605\u6E90 ${input.name} \u5DF2\u6DFB\u52A0` };
|
|
492
690
|
}
|
|
493
691
|
if (input.action === "remove") {
|
|
@@ -497,6 +695,17 @@ async function sourceSubscribe(input) {
|
|
|
497
695
|
crawlerConfig.sources = crawlerConfig.sources.filter((s) => s.id !== input.id);
|
|
498
696
|
await writeFile3(configPath, JSON.stringify(crawlerConfig, null, 2));
|
|
499
697
|
await removeCrawlerSource(input.id);
|
|
698
|
+
const stateAfterRemove = await readState();
|
|
699
|
+
fireAndForgetPush({
|
|
700
|
+
crawlerSources: Object.entries(stateAfterRemove.crawlerSources).map(([id, cs]) => ({
|
|
701
|
+
id,
|
|
702
|
+
name: cs.name,
|
|
703
|
+
url: cs.url,
|
|
704
|
+
type: cs.type,
|
|
705
|
+
icon: cs.icon,
|
|
706
|
+
enabled: cs.enabled
|
|
707
|
+
}))
|
|
708
|
+
});
|
|
500
709
|
return { success: true, message: `\u8BA2\u9605\u6E90 ${input.id} \u5DF2\u5220\u9664` };
|
|
501
710
|
}
|
|
502
711
|
return { success: false, message: "\u672A\u77E5\u64CD\u4F5C" };
|
|
@@ -550,6 +759,20 @@ async function saveDraft(options) {
|
|
|
550
759
|
await writeFile4(filePath, output, "utf-8");
|
|
551
760
|
return { filePath, meta, content: options.content };
|
|
552
761
|
}
|
|
762
|
+
async function updateDraft2(options) {
|
|
763
|
+
const raw = await readFile5(options.file, "utf-8");
|
|
764
|
+
const { data, content } = matter2(raw);
|
|
765
|
+
const meta = data;
|
|
766
|
+
if (options.title) meta.title = options.title;
|
|
767
|
+
if (options.status) meta.status = options.status;
|
|
768
|
+
if (options.tags) meta.tags = options.tags;
|
|
769
|
+
const newContent = options.content ?? content.trim();
|
|
770
|
+
meta.wordCount = newContent.length;
|
|
771
|
+
meta.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
772
|
+
const output = matter2.stringify(newContent, meta);
|
|
773
|
+
await writeFile4(options.file, output, "utf-8");
|
|
774
|
+
return { filePath: options.file, meta, content: newContent };
|
|
775
|
+
}
|
|
553
776
|
async function publishDraft(options) {
|
|
554
777
|
const raw = await readFile5(options.file, "utf-8");
|
|
555
778
|
const { data, content } = matter2(raw);
|
|
@@ -569,6 +792,25 @@ async function publishDraft(options) {
|
|
|
569
792
|
}
|
|
570
793
|
|
|
571
794
|
// src/tools/draft.ts
|
|
795
|
+
function fireAndForgetDraftUpload(draftId, account, title, content) {
|
|
796
|
+
(async () => {
|
|
797
|
+
const creds = await getCredentials();
|
|
798
|
+
if (!creds?.token) return;
|
|
799
|
+
const config = await getConfig();
|
|
800
|
+
try {
|
|
801
|
+
await fetch(`${config.apiBaseUrl}/api/drafts/upload`, {
|
|
802
|
+
method: "POST",
|
|
803
|
+
headers: {
|
|
804
|
+
"Content-Type": "application/json",
|
|
805
|
+
Authorization: `Bearer ${creds.token}`
|
|
806
|
+
},
|
|
807
|
+
body: JSON.stringify({ draftId, account, title, content })
|
|
808
|
+
});
|
|
809
|
+
} catch {
|
|
810
|
+
}
|
|
811
|
+
})().catch(() => {
|
|
812
|
+
});
|
|
813
|
+
}
|
|
572
814
|
var draftSaveSchema = z3.object({
|
|
573
815
|
account: z3.string().describe("\u8D26\u53F7\u540D\u79F0"),
|
|
574
816
|
platform: z3.string().describe("\u5E73\u53F0\u540D\u79F0\uFF0C\u5982 wechat"),
|
|
@@ -596,6 +838,20 @@ async function draftSave(input) {
|
|
|
596
838
|
createdAt: draft.meta.createdAt,
|
|
597
839
|
updatedAt: draft.meta.updatedAt
|
|
598
840
|
});
|
|
841
|
+
fireAndForgetPush({
|
|
842
|
+
drafts: [{
|
|
843
|
+
id: draft.meta.id,
|
|
844
|
+
account: input.account,
|
|
845
|
+
platform: input.platform,
|
|
846
|
+
title: draft.meta.title,
|
|
847
|
+
status: draft.meta.status,
|
|
848
|
+
wordCount: draft.meta.wordCount,
|
|
849
|
+
tags: input.tags || [],
|
|
850
|
+
createdAt: draft.meta.createdAt,
|
|
851
|
+
updatedAt: draft.meta.updatedAt
|
|
852
|
+
}]
|
|
853
|
+
});
|
|
854
|
+
fireAndForgetDraftUpload(draft.meta.id, input.account, draft.meta.title, input.content);
|
|
599
855
|
return {
|
|
600
856
|
success: true,
|
|
601
857
|
message: `\u8349\u7A3F\u5DF2\u4FDD\u5B58: ${draft.meta.title} (${draft.meta.wordCount} \u5B57)`,
|
|
@@ -606,6 +862,55 @@ async function draftSave(input) {
|
|
|
606
862
|
}
|
|
607
863
|
};
|
|
608
864
|
}
|
|
865
|
+
var draftUpdateSchema = z3.object({
|
|
866
|
+
file: z3.string().describe("\u8349\u7A3F\u6587\u4EF6\u8DEF\u5F84"),
|
|
867
|
+
content: z3.string().optional().describe("\u66F4\u65B0\u540E\u7684\u6B63\u6587\uFF08Markdown\uFF09"),
|
|
868
|
+
title: z3.string().optional().describe("\u66F4\u65B0\u540E\u7684\u6807\u9898"),
|
|
869
|
+
status: z3.enum(["draft", "review", "ready"]).optional().describe("\u66F4\u65B0\u72B6\u6001"),
|
|
870
|
+
tags: z3.array(z3.string()).optional().describe("\u66F4\u65B0\u6807\u7B7E")
|
|
871
|
+
});
|
|
872
|
+
async function draftUpdate(input) {
|
|
873
|
+
const draft = await updateDraft2({
|
|
874
|
+
file: input.file,
|
|
875
|
+
content: input.content,
|
|
876
|
+
title: input.title,
|
|
877
|
+
status: input.status,
|
|
878
|
+
tags: input.tags
|
|
879
|
+
});
|
|
880
|
+
await updateDraft(draft.meta.id, {
|
|
881
|
+
title: draft.meta.title,
|
|
882
|
+
account: draft.meta.account,
|
|
883
|
+
platform: draft.meta.platform,
|
|
884
|
+
status: draft.meta.status,
|
|
885
|
+
wordCount: draft.meta.wordCount,
|
|
886
|
+
tags: draft.meta.tags || [],
|
|
887
|
+
createdAt: draft.meta.createdAt,
|
|
888
|
+
updatedAt: draft.meta.updatedAt
|
|
889
|
+
});
|
|
890
|
+
fireAndForgetPush({
|
|
891
|
+
drafts: [{
|
|
892
|
+
id: draft.meta.id,
|
|
893
|
+
account: draft.meta.account,
|
|
894
|
+
platform: draft.meta.platform,
|
|
895
|
+
title: draft.meta.title,
|
|
896
|
+
status: draft.meta.status,
|
|
897
|
+
wordCount: draft.meta.wordCount,
|
|
898
|
+
tags: draft.meta.tags || [],
|
|
899
|
+
createdAt: draft.meta.createdAt,
|
|
900
|
+
updatedAt: draft.meta.updatedAt
|
|
901
|
+
}]
|
|
902
|
+
});
|
|
903
|
+
fireAndForgetDraftUpload(draft.meta.id, draft.meta.account, draft.meta.title, draft.content);
|
|
904
|
+
return {
|
|
905
|
+
success: true,
|
|
906
|
+
message: `\u8349\u7A3F\u5DF2\u66F4\u65B0: ${draft.meta.title} (${draft.meta.wordCount} \u5B57)`,
|
|
907
|
+
data: {
|
|
908
|
+
file: draft.filePath,
|
|
909
|
+
id: draft.meta.id,
|
|
910
|
+
status: draft.meta.status
|
|
911
|
+
}
|
|
912
|
+
};
|
|
913
|
+
}
|
|
609
914
|
var draftPublishSchema = z3.object({
|
|
610
915
|
file: z3.string().describe("\u8349\u7A3F\u6587\u4EF6\u8DEF\u5F84"),
|
|
611
916
|
platformUrl: z3.string().optional().describe("\u5E73\u53F0\u53D1\u5E03\u94FE\u63A5")
|
|
@@ -622,6 +927,16 @@ async function draftPublish(input) {
|
|
|
622
927
|
platformUrl: draft.meta.platformUrl || "",
|
|
623
928
|
publishedAt: draft.meta.publishedAt || (/* @__PURE__ */ new Date()).toISOString()
|
|
624
929
|
});
|
|
930
|
+
fireAndForgetPush({
|
|
931
|
+
published: [{
|
|
932
|
+
id: draft.meta.id,
|
|
933
|
+
account: draft.meta.account,
|
|
934
|
+
platform: draft.meta.platform,
|
|
935
|
+
title: draft.meta.title,
|
|
936
|
+
platformUrl: draft.meta.platformUrl,
|
|
937
|
+
publishedAt: draft.meta.publishedAt || (/* @__PURE__ */ new Date()).toISOString()
|
|
938
|
+
}]
|
|
939
|
+
});
|
|
625
940
|
return {
|
|
626
941
|
success: true,
|
|
627
942
|
message: `\u5DF2\u53D1\u5E03: ${draft.meta.title} \u2192 ${draft.filePath}`,
|
|
@@ -738,7 +1053,6 @@ import { z as z5 } from "zod";
|
|
|
738
1053
|
import { writeFile as writeFile5, mkdir as mkdir4 } from "fs/promises";
|
|
739
1054
|
import { join as join6 } from "path";
|
|
740
1055
|
var syncSchema = z5.object({
|
|
741
|
-
action: z5.enum(["push", "pull", "sync"]).describe("push=\u63A8\u9001\u672C\u5730\u5230\u4E91\u7AEF, pull=\u62C9\u53D6\u4E91\u7AEF\u5230\u672C\u5730, sync=\u5148push\u518Dpull"),
|
|
742
1056
|
workDir: z5.string().optional().describe("\u5DE5\u4F5C\u76EE\u5F55\uFF08\u9ED8\u8BA4\u4F7F\u7528\u914D\u7F6E\u4E2D\u7684 workflowDir\uFF09")
|
|
743
1057
|
});
|
|
744
1058
|
async function sync(input) {
|
|
@@ -747,160 +1061,31 @@ async function sync(input) {
|
|
|
747
1061
|
if (!creds?.token) {
|
|
748
1062
|
return { success: false, message: "\u672A\u6FC0\u6D3B\uFF0C\u8BF7\u5148\u4F7F\u7528 workflow.init \u6FC0\u6D3B License" };
|
|
749
1063
|
}
|
|
750
|
-
|
|
751
|
-
return doPush(creds.token);
|
|
752
|
-
} else if (input.action === "pull") {
|
|
753
|
-
return doPull(creds.token, input.workDir);
|
|
754
|
-
} else {
|
|
755
|
-
const pushResult = await doPush(creds.token);
|
|
756
|
-
const pullResult = await doPull(creds.token, input.workDir);
|
|
757
|
-
const messages = [];
|
|
758
|
-
if (pushResult.success) messages.push(pushResult.message);
|
|
759
|
-
else messages.push(`Push \u5931\u8D25: ${pushResult.message}`);
|
|
760
|
-
if (pullResult.success) messages.push(pullResult.message);
|
|
761
|
-
else messages.push(`Pull \u5931\u8D25: ${pullResult.message}`);
|
|
762
|
-
return {
|
|
763
|
-
success: pushResult.success && pullResult.success,
|
|
764
|
-
message: messages.join("\n\n"),
|
|
765
|
-
data: { push: pushResult.data, pull: pullResult.data }
|
|
766
|
-
};
|
|
767
|
-
}
|
|
768
|
-
}
|
|
769
|
-
async function doPush(token) {
|
|
770
|
-
const config = await getConfig();
|
|
771
|
-
const state = await readState();
|
|
772
|
-
const payload = {
|
|
773
|
-
sources: Object.entries(state.sources).map(([id, s]) => ({
|
|
774
|
-
id,
|
|
775
|
-
title: s.title,
|
|
776
|
-
source: s.source,
|
|
777
|
-
tags: s.tags,
|
|
778
|
-
sourceUrl: s.sourceUrl,
|
|
779
|
-
coverUrl: s.coverUrl,
|
|
780
|
-
sourceIcon: s.sourceIcon,
|
|
781
|
-
createdAt: s.publishedAt
|
|
782
|
-
})),
|
|
783
|
-
drafts: Object.entries(state.drafts).map(([id, d]) => ({
|
|
784
|
-
id,
|
|
785
|
-
account: d.account,
|
|
786
|
-
platform: d.platform,
|
|
787
|
-
title: d.title,
|
|
788
|
-
status: d.status,
|
|
789
|
-
wordCount: d.wordCount,
|
|
790
|
-
tags: d.tags,
|
|
791
|
-
createdAt: d.createdAt,
|
|
792
|
-
updatedAt: d.updatedAt
|
|
793
|
-
})),
|
|
794
|
-
published: Object.entries(state.published).map(([id, p]) => ({
|
|
795
|
-
id,
|
|
796
|
-
account: p.account,
|
|
797
|
-
platform: p.platform,
|
|
798
|
-
title: p.title,
|
|
799
|
-
platformUrl: p.platformUrl,
|
|
800
|
-
publishedAt: p.publishedAt
|
|
801
|
-
})),
|
|
802
|
-
configs: Object.entries(state.configs).map(([_key, c]) => ({
|
|
803
|
-
type: c.type,
|
|
804
|
-
name: c.name,
|
|
805
|
-
displayName: c.displayName,
|
|
806
|
-
content: c.content,
|
|
807
|
-
metadata: c.metadata
|
|
808
|
-
})),
|
|
809
|
-
crawlerSources: Object.entries(state.crawlerSources).map(([id, cs]) => ({
|
|
810
|
-
id,
|
|
811
|
-
name: cs.name,
|
|
812
|
-
url: cs.url,
|
|
813
|
-
type: cs.type,
|
|
814
|
-
icon: cs.icon,
|
|
815
|
-
enabled: cs.enabled
|
|
816
|
-
})),
|
|
817
|
-
analytics: []
|
|
818
|
-
};
|
|
819
|
-
try {
|
|
820
|
-
const res = await fetch(`${config.apiBaseUrl}/api/sync/batch`, {
|
|
821
|
-
method: "POST",
|
|
822
|
-
headers: {
|
|
823
|
-
"Content-Type": "application/json",
|
|
824
|
-
"Authorization": `Bearer ${token}`
|
|
825
|
-
},
|
|
826
|
-
body: JSON.stringify(payload)
|
|
827
|
-
});
|
|
828
|
-
const result = await res.json();
|
|
829
|
-
if (res.ok) {
|
|
830
|
-
await updateLastSyncAt();
|
|
831
|
-
return {
|
|
832
|
-
success: true,
|
|
833
|
-
message: [
|
|
834
|
-
"\u2705 \u63A8\u9001\u5B8C\u6210",
|
|
835
|
-
` \u7D20\u6750: ${payload.sources.length} \u7BC7`,
|
|
836
|
-
` \u8349\u7A3F: ${payload.drafts.length} \u7BC7`,
|
|
837
|
-
` \u5DF2\u53D1\u5E03: ${payload.published.length} \u7BC7`,
|
|
838
|
-
` \u914D\u7F6E: ${payload.configs.length} \u4E2A`,
|
|
839
|
-
` \u8BA2\u9605\u6E90: ${payload.crawlerSources.length} \u4E2A`,
|
|
840
|
-
` \u4E91\u7AEF\u63A5\u53D7: ${result.accepted || 0} \u6761`
|
|
841
|
-
].join("\n"),
|
|
842
|
-
data: { accepted: result.accepted || 0 }
|
|
843
|
-
};
|
|
844
|
-
} else {
|
|
845
|
-
return { success: false, message: `\u63A8\u9001\u5931\u8D25: HTTP ${res.status}` };
|
|
846
|
-
}
|
|
847
|
-
} catch (err) {
|
|
848
|
-
return { success: false, message: `\u63A8\u9001\u7F51\u7EDC\u9519\u8BEF: ${err instanceof Error ? err.message : err}` };
|
|
849
|
-
}
|
|
1064
|
+
return doPull(creds.token, input.workDir);
|
|
850
1065
|
}
|
|
851
1066
|
async function doPull(token, workDir) {
|
|
852
1067
|
const config = await getConfig();
|
|
853
1068
|
const effectiveWorkDir = workDir || config.workflowDir;
|
|
854
1069
|
try {
|
|
855
1070
|
const res = await fetch(`${config.apiBaseUrl}/api/sync/pull`, {
|
|
856
|
-
headers: {
|
|
1071
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
857
1072
|
});
|
|
858
1073
|
if (!res.ok) {
|
|
859
1074
|
if (res.status === 404) {
|
|
860
|
-
return { success: true, message: "\u4E91\u7AEF\u65E0\
|
|
1075
|
+
return { success: true, message: "\u4E91\u7AEF\u65E0\u6570\u636E" };
|
|
861
1076
|
}
|
|
862
1077
|
return { success: false, message: `\u62C9\u53D6\u5931\u8D25: HTTP ${res.status}` };
|
|
863
1078
|
}
|
|
864
1079
|
const cloudData = await res.json();
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
}
|
|
868
|
-
let updated = 0;
|
|
869
|
-
const state = await readState();
|
|
870
|
-
for (const cfgItem of cloudData.configs) {
|
|
871
|
-
const stateKey = `${cfgItem.type}:${cfgItem.name}`;
|
|
872
|
-
const localUpdatedAt = state.configs[stateKey]?.updatedAt || "";
|
|
873
|
-
if (!localUpdatedAt || cfgItem.updatedAt > localUpdatedAt) {
|
|
874
|
-
let filePath = "";
|
|
875
|
-
if (cfgItem.type === "base_rules") {
|
|
876
|
-
filePath = join6(effectiveWorkDir, "base-rules.md");
|
|
877
|
-
} else if (cfgItem.type === "platform") {
|
|
878
|
-
filePath = join6(effectiveWorkDir, "platforms", `${cfgItem.name}.md`);
|
|
879
|
-
} else if (cfgItem.type === "account") {
|
|
880
|
-
filePath = join6(effectiveWorkDir, "accounts", `${cfgItem.name}.yaml`);
|
|
881
|
-
}
|
|
882
|
-
if (filePath && cfgItem.content) {
|
|
883
|
-
await writeFile5(filePath, cfgItem.content, "utf-8");
|
|
884
|
-
state.configs[stateKey] = {
|
|
885
|
-
type: cfgItem.type,
|
|
886
|
-
name: cfgItem.name,
|
|
887
|
-
displayName: cfgItem.displayName,
|
|
888
|
-
content: cfgItem.content,
|
|
889
|
-
metadata: cfgItem.metadata || {},
|
|
890
|
-
updatedAt: cfgItem.updatedAt
|
|
891
|
-
};
|
|
892
|
-
updated++;
|
|
893
|
-
}
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
|
-
let crawlerCount = 0;
|
|
897
|
-
state.crawlerSources = {};
|
|
1080
|
+
const counts = { configs: 0, sources: 0, drafts: 0, published: 0, crawlerSources: 0, writingMasters: 0 };
|
|
1081
|
+
const configsRecord = {};
|
|
1082
|
+
const crawlerSourcesRecord = {};
|
|
898
1083
|
for (const cfg of cloudData.configs || []) {
|
|
899
1084
|
if (cfg.type === "crawler" && cfg.content) {
|
|
900
1085
|
try {
|
|
901
1086
|
const sources = JSON.parse(cfg.content);
|
|
902
1087
|
for (const s of sources) {
|
|
903
|
-
|
|
1088
|
+
crawlerSourcesRecord[s.id] = {
|
|
904
1089
|
name: s.name,
|
|
905
1090
|
url: s.url,
|
|
906
1091
|
type: s.type || "rss",
|
|
@@ -910,37 +1095,62 @@ async function doPull(token, workDir) {
|
|
|
910
1095
|
}
|
|
911
1096
|
const crawlerDir = join6(effectiveWorkDir, "tools", "crawler");
|
|
912
1097
|
await mkdir4(crawlerDir, { recursive: true });
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
crawlerCount = sources.length;
|
|
1098
|
+
await writeFile5(join6(crawlerDir, "config.json"), JSON.stringify({ sources }, null, 2), "utf-8");
|
|
1099
|
+
counts.crawlerSources = sources.length;
|
|
916
1100
|
} catch (e) {
|
|
917
1101
|
console.error("[sync] Failed to parse crawler sources:", e);
|
|
918
1102
|
}
|
|
919
|
-
}
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
1103
|
+
} else {
|
|
1104
|
+
const stateKey = `${cfg.type}:${cfg.name}`;
|
|
1105
|
+
const state = await readState();
|
|
1106
|
+
const localUpdatedAt = state.configs[stateKey]?.updatedAt || "";
|
|
1107
|
+
if (!localUpdatedAt || cfg.updatedAt > localUpdatedAt) {
|
|
1108
|
+
let filePath = "";
|
|
1109
|
+
if (cfg.type === "base_rules") filePath = join6(effectiveWorkDir, "base-rules.md");
|
|
1110
|
+
else if (cfg.type === "platform") filePath = join6(effectiveWorkDir, "platforms", `${cfg.name}.md`);
|
|
1111
|
+
else if (cfg.type === "account") filePath = join6(effectiveWorkDir, "accounts", `${cfg.name}.yaml`);
|
|
1112
|
+
if (filePath && cfg.content) {
|
|
1113
|
+
await writeFile5(filePath, cfg.content, "utf-8");
|
|
1114
|
+
counts.configs++;
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
configsRecord[stateKey] = {
|
|
1118
|
+
type: cfg.type,
|
|
1119
|
+
name: cfg.name,
|
|
1120
|
+
displayName: cfg.displayName,
|
|
1121
|
+
content: cfg.content,
|
|
1122
|
+
metadata: cfg.metadata || {},
|
|
1123
|
+
updatedAt: cfg.updatedAt
|
|
928
1124
|
};
|
|
929
1125
|
}
|
|
930
1126
|
}
|
|
931
|
-
|
|
932
|
-
|
|
1127
|
+
counts.sources = Object.keys(cloudData.sources || {}).length;
|
|
1128
|
+
counts.drafts = Object.keys(cloudData.drafts || {}).length;
|
|
1129
|
+
counts.published = Object.keys(cloudData.published || {}).length;
|
|
1130
|
+
counts.writingMasters = Object.keys(cloudData.writingMasters || {}).length;
|
|
1131
|
+
await mergeCloudData({
|
|
1132
|
+
sources: cloudData.sources || {},
|
|
1133
|
+
drafts: cloudData.drafts || {},
|
|
1134
|
+
published: cloudData.published || {},
|
|
1135
|
+
configs: configsRecord,
|
|
1136
|
+
crawlerSources: crawlerSourcesRecord,
|
|
1137
|
+
writingMasters: cloudData.writingMasters || {}
|
|
1138
|
+
});
|
|
1139
|
+
const parts = [
|
|
1140
|
+
counts.sources > 0 ? `\u7D20\u6750 ${counts.sources}` : "",
|
|
1141
|
+
counts.drafts > 0 ? `\u8349\u7A3F ${counts.drafts}` : "",
|
|
1142
|
+
counts.published > 0 ? `\u5DF2\u53D1\u5E03 ${counts.published}` : "",
|
|
1143
|
+
counts.configs > 0 ? `\u914D\u7F6E\u66F4\u65B0 ${counts.configs}` : "",
|
|
1144
|
+
counts.crawlerSources > 0 ? `\u8BA2\u9605\u6E90 ${counts.crawlerSources}` : "",
|
|
1145
|
+
counts.writingMasters > 0 ? `\u5199\u4F5C\u5927\u5E08 ${counts.writingMasters}` : ""
|
|
1146
|
+
].filter(Boolean);
|
|
933
1147
|
return {
|
|
934
1148
|
success: true,
|
|
935
|
-
message:
|
|
936
|
-
|
|
937
|
-
crawlerCount > 0 ? ` \u8BA2\u9605\u6E90: ${crawlerCount} \u4E2A` : "",
|
|
938
|
-
masterCount > 0 ? ` \u5199\u4F5C\u5927\u5E08: ${masterCount} \u4E2A` : ""
|
|
939
|
-
].filter(Boolean).join("\n") : "\u672C\u5730\u914D\u7F6E\u5DF2\u662F\u6700\u65B0\uFF0C\u65E0\u9700\u66F4\u65B0",
|
|
940
|
-
data: { updated, crawlerSources: crawlerCount, writingMasters: masterCount }
|
|
1149
|
+
message: parts.length > 0 ? `\u2705 \u5DF2\u540C\u6B65: ${parts.join(", ")}` : "\u4E91\u7AEF\u65E0\u6570\u636E",
|
|
1150
|
+
data: counts
|
|
941
1151
|
};
|
|
942
1152
|
} catch (err) {
|
|
943
|
-
return { success: false, message: `\
|
|
1153
|
+
return { success: false, message: `\u540C\u6B65\u5931\u8D25: ${err instanceof Error ? err.message : err}` };
|
|
944
1154
|
}
|
|
945
1155
|
}
|
|
946
1156
|
async function syncPull(input) {
|
|
@@ -954,7 +1164,7 @@ async function syncPull(input) {
|
|
|
954
1164
|
|
|
955
1165
|
// src/tools/workflow.ts
|
|
956
1166
|
import { z as z6 } from "zod";
|
|
957
|
-
import { cp, mkdir as mkdir5, access as access2,
|
|
1167
|
+
import { cp, mkdir as mkdir5, access as access2, unlink as unlink2 } from "fs/promises";
|
|
958
1168
|
import { join as join7, dirname } from "path";
|
|
959
1169
|
import { fileURLToPath } from "url";
|
|
960
1170
|
var DEFAULT_API_BASE_URL = "https://app.claudeink.com";
|
|
@@ -983,58 +1193,31 @@ async function workflowInit(input) {
|
|
|
983
1193
|
}
|
|
984
1194
|
}
|
|
985
1195
|
const claudeinkDir = join7(cwd, ".claudeink");
|
|
986
|
-
const dirs = [
|
|
987
|
-
"sources/articles",
|
|
988
|
-
"sources/video-transcripts",
|
|
989
|
-
"sources/notes",
|
|
990
|
-
"sources/data",
|
|
991
|
-
"sources/daily",
|
|
992
|
-
"templates"
|
|
993
|
-
];
|
|
1196
|
+
const dirs = ["sources/articles", "sources/video-transcripts", "sources/notes", "sources/data", "sources/daily", "templates"];
|
|
994
1197
|
for (const dir of dirs) {
|
|
995
1198
|
await mkdir5(join7(cwd, dir), { recursive: true });
|
|
996
1199
|
}
|
|
997
1200
|
await mkdir5(claudeinkDir, { recursive: true });
|
|
998
1201
|
results.push("\u2705 \u8FD0\u884C\u65F6\u76EE\u5F55\u5DF2\u521B\u5EFA");
|
|
999
|
-
const
|
|
1202
|
+
const state = await readState();
|
|
1203
|
+
state.config.workflowDir = cwd;
|
|
1204
|
+
const oldCredsPath = join7(claudeinkDir, "credentials.json");
|
|
1000
1205
|
try {
|
|
1001
|
-
await access2(
|
|
1002
|
-
|
|
1206
|
+
await access2(oldCredsPath);
|
|
1207
|
+
await unlink2(oldCredsPath);
|
|
1208
|
+
results.push("\u{1F9F9} \u5DF2\u6E05\u7406\u65E7\u7248 credentials.json");
|
|
1003
1209
|
} catch {
|
|
1004
|
-
const initialState = {
|
|
1005
|
-
credentials: null,
|
|
1006
|
-
config: {
|
|
1007
|
-
apiBaseUrl: DEFAULT_API_BASE_URL,
|
|
1008
|
-
workflowDir: cwd
|
|
1009
|
-
},
|
|
1010
|
-
sources: {},
|
|
1011
|
-
drafts: {},
|
|
1012
|
-
published: {},
|
|
1013
|
-
configs: {},
|
|
1014
|
-
crawlerSources: {},
|
|
1015
|
-
writingMasters: {},
|
|
1016
|
-
tagQueue: {
|
|
1017
|
-
queue: [],
|
|
1018
|
-
failed: []
|
|
1019
|
-
},
|
|
1020
|
-
lastSyncAt: ""
|
|
1021
|
-
};
|
|
1022
|
-
await writeFile6(statePath, JSON.stringify(initialState, null, 2), "utf-8");
|
|
1023
|
-
await chmod2(statePath, 384);
|
|
1024
|
-
results.push("\u2705 state.json");
|
|
1025
1210
|
}
|
|
1026
1211
|
let activated = false;
|
|
1027
1212
|
if (input.licenseKey) {
|
|
1028
1213
|
try {
|
|
1029
|
-
const
|
|
1030
|
-
const res = await fetch(activateUrl, {
|
|
1214
|
+
const res = await fetch(`${DEFAULT_API_BASE_URL}/api/auth/activate`, {
|
|
1031
1215
|
method: "POST",
|
|
1032
1216
|
headers: { "Content-Type": "application/json" },
|
|
1033
1217
|
body: JSON.stringify({ key: input.licenseKey })
|
|
1034
1218
|
});
|
|
1035
1219
|
const data = await res.json();
|
|
1036
1220
|
if (data.userId) {
|
|
1037
|
-
const state = await readState();
|
|
1038
1221
|
state.credentials = {
|
|
1039
1222
|
licenseKey: input.licenseKey,
|
|
1040
1223
|
token: data.token,
|
|
@@ -1042,7 +1225,6 @@ async function workflowInit(input) {
|
|
|
1042
1225
|
plan: data.plan,
|
|
1043
1226
|
expiresAt: data.expiresAt
|
|
1044
1227
|
};
|
|
1045
|
-
await writeState(state);
|
|
1046
1228
|
results.push(`\u2705 License \u6FC0\u6D3B\u6210\u529F\uFF08\u5957\u9910: ${data.plan}\uFF09`);
|
|
1047
1229
|
activated = true;
|
|
1048
1230
|
} else {
|
|
@@ -1052,25 +1234,30 @@ async function workflowInit(input) {
|
|
|
1052
1234
|
results.push(`\u26A0\uFE0F \u6FC0\u6D3B\u7F51\u7EDC\u9519\u8BEF: ${err instanceof Error ? err.message : err}`);
|
|
1053
1235
|
}
|
|
1054
1236
|
}
|
|
1237
|
+
await writeState(state);
|
|
1238
|
+
results.push("\u2705 state.json");
|
|
1055
1239
|
if (activated) {
|
|
1056
1240
|
try {
|
|
1057
1241
|
const pullResult = await syncPull({ workDir: cwd });
|
|
1058
|
-
if (pullResult.success && pullResult.data
|
|
1059
|
-
|
|
1242
|
+
if (pullResult.success && pullResult.data) {
|
|
1243
|
+
const d = pullResult.data;
|
|
1244
|
+
const total = (d.sources || 0) + (d.drafts || 0) + (d.published || 0) + (d.configs || 0);
|
|
1245
|
+
if (total > 0) {
|
|
1246
|
+
results.push("\u2705 \u5DF2\u4ECE\u4E91\u7AEF\u540C\u6B65\u6570\u636E");
|
|
1247
|
+
} else {
|
|
1248
|
+
results.push("\u2139\uFE0F \u4E91\u7AEF\u65E0\u5DF2\u6709\u6570\u636E\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u6A21\u677F");
|
|
1249
|
+
}
|
|
1060
1250
|
} else {
|
|
1061
|
-
results.push("\u2139\uFE0F \u4E91\u7AEF\u65E0\u5DF2\u6709\
|
|
1251
|
+
results.push("\u2139\uFE0F \u4E91\u7AEF\u65E0\u5DF2\u6709\u6570\u636E\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u6A21\u677F");
|
|
1062
1252
|
}
|
|
1063
1253
|
} catch {
|
|
1064
|
-
results.push("\u2139\uFE0F \u4E91\u7AEF\
|
|
1254
|
+
results.push("\u2139\uFE0F \u4E91\u7AEF\u540C\u6B65\u8DF3\u8FC7");
|
|
1065
1255
|
}
|
|
1066
1256
|
}
|
|
1067
1257
|
try {
|
|
1068
1258
|
await access2(join7(cwd, "tools", "crawler", "package.json"));
|
|
1069
|
-
const { execSync } = await import("child_process");
|
|
1070
|
-
|
|
1071
|
-
cwd: join7(cwd, "tools", "crawler"),
|
|
1072
|
-
stdio: "pipe"
|
|
1073
|
-
});
|
|
1259
|
+
const { execSync: execSync2 } = await import("child_process");
|
|
1260
|
+
execSync2("npm install --silent", { cwd: join7(cwd, "tools", "crawler"), stdio: "pipe" });
|
|
1074
1261
|
results.push("\u2705 \u722C\u866B\u4F9D\u8D56\u5DF2\u5B89\u88C5");
|
|
1075
1262
|
} catch {
|
|
1076
1263
|
}
|
|
@@ -1094,16 +1281,724 @@ async function workflowInit(input) {
|
|
|
1094
1281
|
}
|
|
1095
1282
|
}
|
|
1096
1283
|
|
|
1284
|
+
// src/tools/ink.ts
|
|
1285
|
+
import { z as z7 } from "zod";
|
|
1286
|
+
|
|
1287
|
+
// src/lib/knowledge.ts
|
|
1288
|
+
import { readFile as readFile6, writeFile as writeFile6, mkdir as mkdir6, unlink as unlink3 } from "fs/promises";
|
|
1289
|
+
import { join as join8 } from "path";
|
|
1290
|
+
import matter3 from "gray-matter";
|
|
1291
|
+
function getKnowledgeDir() {
|
|
1292
|
+
return join8(getWorkDir(), ".claudeink", "knowledge");
|
|
1293
|
+
}
|
|
1294
|
+
function getIndexPath() {
|
|
1295
|
+
return join8(getWorkDir(), ".claudeink", "knowledge", "index.json");
|
|
1296
|
+
}
|
|
1297
|
+
async function generateId() {
|
|
1298
|
+
const now = /* @__PURE__ */ new Date();
|
|
1299
|
+
const dateStr = now.toISOString().slice(0, 10).replace(/-/g, "");
|
|
1300
|
+
const index = await readIndex();
|
|
1301
|
+
let maxSeq = 0;
|
|
1302
|
+
const prefix = `k_${dateStr}_`;
|
|
1303
|
+
for (const id of Object.keys(index.entries)) {
|
|
1304
|
+
if (id.startsWith(prefix)) {
|
|
1305
|
+
const seq = parseInt(id.slice(prefix.length), 10);
|
|
1306
|
+
if (seq > maxSeq) maxSeq = seq;
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
const nextSeq = String(maxSeq + 1).padStart(3, "0");
|
|
1310
|
+
return `${prefix}${nextSeq}`;
|
|
1311
|
+
}
|
|
1312
|
+
function toSlug(title) {
|
|
1313
|
+
return title.replace(/[^\u4e00-\u9fa5a-zA-Z0-9\s-]/g, "").replace(/\s+/g, "-").slice(0, 50).replace(/-+$/, "");
|
|
1314
|
+
}
|
|
1315
|
+
async function readIndex() {
|
|
1316
|
+
try {
|
|
1317
|
+
const raw = await readFile6(getIndexPath(), "utf-8");
|
|
1318
|
+
return JSON.parse(raw);
|
|
1319
|
+
} catch {
|
|
1320
|
+
return { entries: {}, updated_at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
async function saveIndex(index) {
|
|
1324
|
+
index.updated_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
1325
|
+
const dir = getKnowledgeDir();
|
|
1326
|
+
await mkdir6(dir, { recursive: true });
|
|
1327
|
+
await writeFile6(getIndexPath(), JSON.stringify(index, null, 2), "utf-8");
|
|
1328
|
+
}
|
|
1329
|
+
async function addToIndex(entry) {
|
|
1330
|
+
const index = await readIndex();
|
|
1331
|
+
index.entries[entry.id] = entry;
|
|
1332
|
+
await saveIndex(index);
|
|
1333
|
+
}
|
|
1334
|
+
async function removeFromIndex(id) {
|
|
1335
|
+
const index = await readIndex();
|
|
1336
|
+
delete index.entries[id];
|
|
1337
|
+
await saveIndex(index);
|
|
1338
|
+
}
|
|
1339
|
+
async function writeKnowledgeFile(meta, content) {
|
|
1340
|
+
const now = /* @__PURE__ */ new Date();
|
|
1341
|
+
const monthDir = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}`;
|
|
1342
|
+
const dir = join8(getKnowledgeDir(), monthDir);
|
|
1343
|
+
await mkdir6(dir, { recursive: true });
|
|
1344
|
+
const slug = toSlug(meta.title) || meta.id;
|
|
1345
|
+
const filename = `${now.toISOString().slice(0, 10)}-${slug}.md`;
|
|
1346
|
+
const filePath = join8(dir, filename);
|
|
1347
|
+
const frontmatter = {
|
|
1348
|
+
id: meta.id,
|
|
1349
|
+
title: meta.title,
|
|
1350
|
+
tags: meta.tags,
|
|
1351
|
+
category: meta.category,
|
|
1352
|
+
source_type: meta.source_type,
|
|
1353
|
+
created_at: meta.created_at,
|
|
1354
|
+
updated_at: meta.updated_at
|
|
1355
|
+
};
|
|
1356
|
+
if (meta.url) frontmatter.url = meta.url;
|
|
1357
|
+
const output = matter3.stringify(content, frontmatter);
|
|
1358
|
+
await writeFile6(filePath, output, "utf-8");
|
|
1359
|
+
return join8(monthDir, filename);
|
|
1360
|
+
}
|
|
1361
|
+
async function readKnowledgeFile(id) {
|
|
1362
|
+
const index = await readIndex();
|
|
1363
|
+
const entry = index.entries[id];
|
|
1364
|
+
if (!entry) return null;
|
|
1365
|
+
const filePath = join8(getKnowledgeDir(), entry.file_path);
|
|
1366
|
+
try {
|
|
1367
|
+
const raw = await readFile6(filePath, "utf-8");
|
|
1368
|
+
const { data, content } = matter3(raw);
|
|
1369
|
+
return {
|
|
1370
|
+
meta: data,
|
|
1371
|
+
content: content.trim(),
|
|
1372
|
+
filePath
|
|
1373
|
+
};
|
|
1374
|
+
} catch {
|
|
1375
|
+
return null;
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
async function updateKnowledgeFile(id, updates) {
|
|
1379
|
+
const file = await readKnowledgeFile(id);
|
|
1380
|
+
if (!file) return null;
|
|
1381
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1382
|
+
const meta = { ...file.meta };
|
|
1383
|
+
if (updates.title !== void 0) meta.title = updates.title;
|
|
1384
|
+
if (updates.tags !== void 0) meta.tags = updates.tags;
|
|
1385
|
+
if (updates.category !== void 0) meta.category = updates.category;
|
|
1386
|
+
meta.updated_at = now;
|
|
1387
|
+
const content = updates.content !== void 0 ? updates.content : file.content;
|
|
1388
|
+
const frontmatter = {
|
|
1389
|
+
id: meta.id,
|
|
1390
|
+
title: meta.title,
|
|
1391
|
+
tags: meta.tags,
|
|
1392
|
+
category: meta.category,
|
|
1393
|
+
source_type: meta.source_type,
|
|
1394
|
+
created_at: meta.created_at,
|
|
1395
|
+
updated_at: meta.updated_at
|
|
1396
|
+
};
|
|
1397
|
+
if (meta.url) frontmatter.url = meta.url;
|
|
1398
|
+
const output = matter3.stringify(content, frontmatter);
|
|
1399
|
+
await writeFile6(file.filePath, output, "utf-8");
|
|
1400
|
+
const index = await readIndex();
|
|
1401
|
+
const entry = index.entries[id];
|
|
1402
|
+
if (entry) {
|
|
1403
|
+
entry.title = meta.title;
|
|
1404
|
+
entry.tags = meta.tags;
|
|
1405
|
+
entry.category = meta.category;
|
|
1406
|
+
entry.preview = generatePreview(content);
|
|
1407
|
+
entry.updated_at = now;
|
|
1408
|
+
await saveIndex(index);
|
|
1409
|
+
}
|
|
1410
|
+
return { meta, content, filePath: file.filePath };
|
|
1411
|
+
}
|
|
1412
|
+
async function deleteKnowledgeFile(id) {
|
|
1413
|
+
const index = await readIndex();
|
|
1414
|
+
const entry = index.entries[id];
|
|
1415
|
+
if (!entry) return false;
|
|
1416
|
+
const filePath = join8(getKnowledgeDir(), entry.file_path);
|
|
1417
|
+
try {
|
|
1418
|
+
await unlink3(filePath);
|
|
1419
|
+
} catch {
|
|
1420
|
+
}
|
|
1421
|
+
await removeFromIndex(id);
|
|
1422
|
+
return true;
|
|
1423
|
+
}
|
|
1424
|
+
function generatePreview(content, maxLen = 200) {
|
|
1425
|
+
return content.replace(/^#+\s.+$/gm, "").replace(/\n+/g, " ").trim().slice(0, maxLen);
|
|
1426
|
+
}
|
|
1427
|
+
function searchKnowledge(index, filters) {
|
|
1428
|
+
let entries = Object.values(index.entries);
|
|
1429
|
+
if (filters.tags && filters.tags.length > 0) {
|
|
1430
|
+
const filterTags = filters.tags.map((t) => t.toLowerCase());
|
|
1431
|
+
entries = entries.filter(
|
|
1432
|
+
(e) => e.tags.some((t) => filterTags.includes(t.toLowerCase()))
|
|
1433
|
+
);
|
|
1434
|
+
}
|
|
1435
|
+
if (filters.category) {
|
|
1436
|
+
entries = entries.filter((e) => e.category === filters.category);
|
|
1437
|
+
}
|
|
1438
|
+
if (filters.date_from) {
|
|
1439
|
+
entries = entries.filter((e) => e.created_at >= filters.date_from);
|
|
1440
|
+
}
|
|
1441
|
+
if (filters.date_to) {
|
|
1442
|
+
entries = entries.filter((e) => e.created_at <= filters.date_to);
|
|
1443
|
+
}
|
|
1444
|
+
if (filters.query) {
|
|
1445
|
+
const q = filters.query.toLowerCase();
|
|
1446
|
+
entries = entries.map((e) => {
|
|
1447
|
+
let score = 0;
|
|
1448
|
+
if (e.title.toLowerCase().includes(q)) score += 10;
|
|
1449
|
+
if (e.preview.toLowerCase().includes(q)) score += 5;
|
|
1450
|
+
if (e.tags.some((t) => t.toLowerCase().includes(q))) score += 3;
|
|
1451
|
+
return { ...e, _score: score };
|
|
1452
|
+
}).filter((e) => e._score > 0).sort((a, b) => b._score - a._score);
|
|
1453
|
+
} else {
|
|
1454
|
+
entries.sort((a, b) => b.created_at.localeCompare(a.created_at));
|
|
1455
|
+
}
|
|
1456
|
+
const total = entries.length;
|
|
1457
|
+
const limit = filters.limit || 10;
|
|
1458
|
+
const results = entries.slice(0, limit).map((e) => ({
|
|
1459
|
+
id: e.id,
|
|
1460
|
+
title: e.title,
|
|
1461
|
+
tags: e.tags,
|
|
1462
|
+
category: e.category,
|
|
1463
|
+
created_at: e.created_at,
|
|
1464
|
+
preview: e.preview
|
|
1465
|
+
}));
|
|
1466
|
+
return { results, total };
|
|
1467
|
+
}
|
|
1468
|
+
async function saveKnowledge(params) {
|
|
1469
|
+
const id = await generateId();
|
|
1470
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1471
|
+
const meta = {
|
|
1472
|
+
id,
|
|
1473
|
+
title: params.title,
|
|
1474
|
+
tags: params.tags,
|
|
1475
|
+
category: params.category,
|
|
1476
|
+
source_type: params.source_type,
|
|
1477
|
+
url: params.url,
|
|
1478
|
+
created_at: now,
|
|
1479
|
+
updated_at: now
|
|
1480
|
+
};
|
|
1481
|
+
const relativePath = await writeKnowledgeFile(meta, params.content);
|
|
1482
|
+
await addToIndex({
|
|
1483
|
+
id,
|
|
1484
|
+
title: params.title,
|
|
1485
|
+
tags: params.tags,
|
|
1486
|
+
category: params.category,
|
|
1487
|
+
source_type: params.source_type,
|
|
1488
|
+
preview: generatePreview(params.content),
|
|
1489
|
+
file_path: relativePath,
|
|
1490
|
+
created_at: now,
|
|
1491
|
+
updated_at: now
|
|
1492
|
+
});
|
|
1493
|
+
return { id, filePath: relativePath };
|
|
1494
|
+
}
|
|
1495
|
+
async function readMultipleKnowledgeFiles(ids) {
|
|
1496
|
+
const results = [];
|
|
1497
|
+
for (const id of ids) {
|
|
1498
|
+
const file = await readKnowledgeFile(id);
|
|
1499
|
+
if (file) results.push(file);
|
|
1500
|
+
}
|
|
1501
|
+
return results;
|
|
1502
|
+
}
|
|
1503
|
+
function getAllTags(index) {
|
|
1504
|
+
const tagCounts = {};
|
|
1505
|
+
for (const entry of Object.values(index.entries)) {
|
|
1506
|
+
for (const tag of entry.tags) {
|
|
1507
|
+
tagCounts[tag] = (tagCounts[tag] || 0) + 1;
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
return Object.entries(tagCounts).map(([name, count]) => ({ name, count })).sort((a, b) => b.count - a.count);
|
|
1511
|
+
}
|
|
1512
|
+
async function renameTag(oldTag, newTag) {
|
|
1513
|
+
const index = await readIndex();
|
|
1514
|
+
let count = 0;
|
|
1515
|
+
for (const entry of Object.values(index.entries)) {
|
|
1516
|
+
const idx = entry.tags.indexOf(oldTag);
|
|
1517
|
+
if (idx !== -1) {
|
|
1518
|
+
entry.tags[idx] = newTag;
|
|
1519
|
+
count++;
|
|
1520
|
+
const file = await readKnowledgeFile(entry.id);
|
|
1521
|
+
if (file) {
|
|
1522
|
+
const tagIdx = file.meta.tags.indexOf(oldTag);
|
|
1523
|
+
if (tagIdx !== -1) {
|
|
1524
|
+
file.meta.tags[tagIdx] = newTag;
|
|
1525
|
+
await updateKnowledgeFile(entry.id, { tags: file.meta.tags });
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
if (count > 0) {
|
|
1531
|
+
await saveIndex(index);
|
|
1532
|
+
}
|
|
1533
|
+
return count;
|
|
1534
|
+
}
|
|
1535
|
+
async function mergeTags(sourceTags, targetTag) {
|
|
1536
|
+
const index = await readIndex();
|
|
1537
|
+
let count = 0;
|
|
1538
|
+
for (const entry of Object.values(index.entries)) {
|
|
1539
|
+
const hasSource = entry.tags.some((t) => sourceTags.includes(t));
|
|
1540
|
+
if (hasSource) {
|
|
1541
|
+
const newTags = entry.tags.filter((t) => !sourceTags.includes(t));
|
|
1542
|
+
if (!newTags.includes(targetTag)) {
|
|
1543
|
+
newTags.push(targetTag);
|
|
1544
|
+
}
|
|
1545
|
+
entry.tags = newTags;
|
|
1546
|
+
count++;
|
|
1547
|
+
await updateKnowledgeFile(entry.id, { tags: newTags });
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
if (count > 0) {
|
|
1551
|
+
await saveIndex(index);
|
|
1552
|
+
}
|
|
1553
|
+
return count;
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
// src/tools/ink.ts
|
|
1557
|
+
var inkSaveSchema = z7.object({
|
|
1558
|
+
content: z7.string().describe("\u8981\u4FDD\u5B58\u7684\u5185\u5BB9\uFF08\u5FC5\u586B\uFF09"),
|
|
1559
|
+
title: z7.string().describe("\u6807\u9898\uFF08Claude \u81EA\u52A8\u751F\u6210\uFF09"),
|
|
1560
|
+
tags: z7.array(z7.string()).optional().default([]).describe("\u6807\u7B7E\uFF08Claude \u81EA\u52A8\u751F\u6210\uFF0C\u7528\u6237\u53EF\u6307\u5B9A\uFF09"),
|
|
1561
|
+
category: z7.enum(["insight", "decision", "analysis", "idea", "reference", "action"]).optional().default("reference").describe("\u5206\u7C7B\uFF1Ainsight / decision / analysis / idea / reference / action"),
|
|
1562
|
+
source_type: z7.enum(["conversation", "url", "manual"]).optional().default("conversation").describe("\u6765\u6E90\u7C7B\u578B\uFF1Aconversation / url / manual"),
|
|
1563
|
+
url: z7.string().optional().describe("\u6765\u6E90 URL\uFF08\u5982\u679C\u662F\u5916\u90E8\u7D20\u6750\uFF09")
|
|
1564
|
+
});
|
|
1565
|
+
async function inkSave(input) {
|
|
1566
|
+
try {
|
|
1567
|
+
const { id } = await saveKnowledge({
|
|
1568
|
+
content: input.content,
|
|
1569
|
+
title: input.title,
|
|
1570
|
+
tags: input.tags,
|
|
1571
|
+
category: input.category,
|
|
1572
|
+
source_type: input.source_type,
|
|
1573
|
+
url: input.url
|
|
1574
|
+
});
|
|
1575
|
+
const categoryNames = {
|
|
1576
|
+
insight: "\u6D1E\u5BDF",
|
|
1577
|
+
decision: "\u51B3\u7B56",
|
|
1578
|
+
analysis: "\u5206\u6790",
|
|
1579
|
+
idea: "\u60F3\u6CD5",
|
|
1580
|
+
reference: "\u53C2\u8003",
|
|
1581
|
+
action: "\u884C\u52A8"
|
|
1582
|
+
};
|
|
1583
|
+
const catName = categoryNames[input.category] || input.category;
|
|
1584
|
+
const tagStr = input.tags.length > 0 ? input.tags.join("\u3001") : "\u65E0\u6807\u7B7E";
|
|
1585
|
+
return {
|
|
1586
|
+
success: true,
|
|
1587
|
+
message: `\u5DF2\u4FDD\u5B58\u5230\u300C${catName}\u300D\uFF0C\u6807\u7B7E\uFF1A${tagStr}`,
|
|
1588
|
+
data: { id }
|
|
1589
|
+
};
|
|
1590
|
+
} catch (err) {
|
|
1591
|
+
return {
|
|
1592
|
+
success: false,
|
|
1593
|
+
message: `\u4FDD\u5B58\u5931\u8D25\uFF1A${err.message}`
|
|
1594
|
+
};
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
var inkSearchSchema = z7.object({
|
|
1598
|
+
query: z7.string().optional().describe("\u641C\u7D22\u5173\u952E\u8BCD"),
|
|
1599
|
+
tags: z7.array(z7.string()).optional().describe("\u6309\u6807\u7B7E\u8FC7\u6EE4"),
|
|
1600
|
+
category: z7.enum(["insight", "decision", "analysis", "idea", "reference", "action"]).optional().describe("\u6309\u5206\u7C7B\u8FC7\u6EE4"),
|
|
1601
|
+
date_from: z7.string().optional().describe("\u8D77\u59CB\u65E5\u671F\uFF08ISO \u683C\u5F0F\uFF09"),
|
|
1602
|
+
date_to: z7.string().optional().describe("\u622A\u6B62\u65E5\u671F\uFF08ISO \u683C\u5F0F\uFF09"),
|
|
1603
|
+
limit: z7.number().optional().default(10).describe("\u8FD4\u56DE\u6570\u91CF\uFF0C\u9ED8\u8BA4 10")
|
|
1604
|
+
});
|
|
1605
|
+
async function inkSearch(input) {
|
|
1606
|
+
try {
|
|
1607
|
+
const index = await readIndex();
|
|
1608
|
+
const { results, total } = searchKnowledge(index, {
|
|
1609
|
+
query: input.query,
|
|
1610
|
+
tags: input.tags,
|
|
1611
|
+
category: input.category,
|
|
1612
|
+
date_from: input.date_from,
|
|
1613
|
+
date_to: input.date_to,
|
|
1614
|
+
limit: input.limit
|
|
1615
|
+
});
|
|
1616
|
+
if (total === 0) {
|
|
1617
|
+
return {
|
|
1618
|
+
success: true,
|
|
1619
|
+
message: "\u6CA1\u6709\u627E\u5230\u5339\u914D\u7684\u77E5\u8BC6\u7247\u6BB5",
|
|
1620
|
+
data: { results: [], total: 0 }
|
|
1621
|
+
};
|
|
1622
|
+
}
|
|
1623
|
+
return {
|
|
1624
|
+
success: true,
|
|
1625
|
+
message: `\u627E\u5230 ${total} \u6761\u76F8\u5173\u8BB0\u5F55${total > results.length ? `\uFF0C\u663E\u793A\u524D ${results.length} \u6761` : ""}`,
|
|
1626
|
+
data: { results, total }
|
|
1627
|
+
};
|
|
1628
|
+
} catch (err) {
|
|
1629
|
+
return {
|
|
1630
|
+
success: false,
|
|
1631
|
+
message: `\u641C\u7D22\u5931\u8D25\uFF1A${err.message}`
|
|
1632
|
+
};
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
var inkGetSchema = z7.object({
|
|
1636
|
+
id: z7.string().describe("\u77E5\u8BC6\u7247\u6BB5 ID")
|
|
1637
|
+
});
|
|
1638
|
+
async function inkGet(input) {
|
|
1639
|
+
try {
|
|
1640
|
+
const file = await readKnowledgeFile(input.id);
|
|
1641
|
+
if (!file) {
|
|
1642
|
+
return {
|
|
1643
|
+
success: false,
|
|
1644
|
+
message: `\u672A\u627E\u5230 ID \u4E3A ${input.id} \u7684\u77E5\u8BC6\u7247\u6BB5`
|
|
1645
|
+
};
|
|
1646
|
+
}
|
|
1647
|
+
return {
|
|
1648
|
+
success: true,
|
|
1649
|
+
message: file.meta.title,
|
|
1650
|
+
data: {
|
|
1651
|
+
id: file.meta.id,
|
|
1652
|
+
title: file.meta.title,
|
|
1653
|
+
content: file.content,
|
|
1654
|
+
tags: file.meta.tags,
|
|
1655
|
+
category: file.meta.category,
|
|
1656
|
+
source_type: file.meta.source_type,
|
|
1657
|
+
url: file.meta.url,
|
|
1658
|
+
created_at: file.meta.created_at,
|
|
1659
|
+
updated_at: file.meta.updated_at
|
|
1660
|
+
}
|
|
1661
|
+
};
|
|
1662
|
+
} catch (err) {
|
|
1663
|
+
return {
|
|
1664
|
+
success: false,
|
|
1665
|
+
message: `\u83B7\u53D6\u5931\u8D25\uFF1A${err.message}`
|
|
1666
|
+
};
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
var inkReviewSchema = z7.object({
|
|
1670
|
+
period: z7.enum(["today", "week", "month", "custom"]).optional().default("week").describe("\u56DE\u987E\u65F6\u95F4\u6BB5\uFF1Atoday / week / month / custom"),
|
|
1671
|
+
date_from: z7.string().optional().describe("\u81EA\u5B9A\u4E49\u8D77\u59CB\u65E5\u671F\uFF08ISO \u683C\u5F0F\uFF0Cperiod=custom \u65F6\u4F7F\u7528\uFF09"),
|
|
1672
|
+
date_to: z7.string().optional().describe("\u81EA\u5B9A\u4E49\u622A\u6B62\u65E5\u671F\uFF08ISO \u683C\u5F0F\uFF0Cperiod=custom \u65F6\u4F7F\u7528\uFF09"),
|
|
1673
|
+
category: z7.enum(["insight", "decision", "analysis", "idea", "reference", "action"]).optional().describe("\u6309\u5206\u7C7B\u8FC7\u6EE4")
|
|
1674
|
+
});
|
|
1675
|
+
async function inkReview(input) {
|
|
1676
|
+
try {
|
|
1677
|
+
const index = await readIndex();
|
|
1678
|
+
const entries = Object.values(index.entries);
|
|
1679
|
+
const now = /* @__PURE__ */ new Date();
|
|
1680
|
+
let dateFrom;
|
|
1681
|
+
let dateTo = now.toISOString();
|
|
1682
|
+
switch (input.period) {
|
|
1683
|
+
case "today":
|
|
1684
|
+
dateFrom = new Date(now.getFullYear(), now.getMonth(), now.getDate()).toISOString();
|
|
1685
|
+
break;
|
|
1686
|
+
case "week": {
|
|
1687
|
+
const weekAgo = new Date(now);
|
|
1688
|
+
weekAgo.setDate(weekAgo.getDate() - 7);
|
|
1689
|
+
dateFrom = weekAgo.toISOString();
|
|
1690
|
+
break;
|
|
1691
|
+
}
|
|
1692
|
+
case "month": {
|
|
1693
|
+
const monthAgo = new Date(now);
|
|
1694
|
+
monthAgo.setMonth(monthAgo.getMonth() - 1);
|
|
1695
|
+
dateFrom = monthAgo.toISOString();
|
|
1696
|
+
break;
|
|
1697
|
+
}
|
|
1698
|
+
case "custom":
|
|
1699
|
+
dateFrom = input.date_from || (/* @__PURE__ */ new Date(0)).toISOString();
|
|
1700
|
+
dateTo = input.date_to || now.toISOString();
|
|
1701
|
+
break;
|
|
1702
|
+
}
|
|
1703
|
+
let filtered = entries.filter(
|
|
1704
|
+
(e) => e.created_at >= dateFrom && e.created_at <= dateTo
|
|
1705
|
+
);
|
|
1706
|
+
if (input.category) {
|
|
1707
|
+
filtered = filtered.filter((e) => e.category === input.category);
|
|
1708
|
+
}
|
|
1709
|
+
filtered.sort((a, b) => b.created_at.localeCompare(a.created_at));
|
|
1710
|
+
const byCategory = {};
|
|
1711
|
+
for (const e of filtered) {
|
|
1712
|
+
byCategory[e.category] = (byCategory[e.category] || 0) + 1;
|
|
1713
|
+
}
|
|
1714
|
+
const tagCounts = {};
|
|
1715
|
+
for (const e of filtered) {
|
|
1716
|
+
for (const t of e.tags) {
|
|
1717
|
+
tagCounts[t] = (tagCounts[t] || 0) + 1;
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
const topTags = Object.entries(tagCounts).sort((a, b) => b[1] - a[1]).slice(0, 10).map(([tag, count]) => ({ tag, count }));
|
|
1721
|
+
const categoryNames = {
|
|
1722
|
+
insight: "\u6D1E\u5BDF",
|
|
1723
|
+
decision: "\u51B3\u7B56",
|
|
1724
|
+
analysis: "\u5206\u6790",
|
|
1725
|
+
idea: "\u60F3\u6CD5",
|
|
1726
|
+
reference: "\u53C2\u8003",
|
|
1727
|
+
action: "\u884C\u52A8"
|
|
1728
|
+
};
|
|
1729
|
+
const periodNames = {
|
|
1730
|
+
today: "\u4ECA\u65E5",
|
|
1731
|
+
week: "\u672C\u5468",
|
|
1732
|
+
month: "\u672C\u6708",
|
|
1733
|
+
custom: "\u81EA\u5B9A\u4E49\u65F6\u6BB5"
|
|
1734
|
+
};
|
|
1735
|
+
return {
|
|
1736
|
+
success: true,
|
|
1737
|
+
message: `${periodNames[input.period]}\u77E5\u8BC6\u56DE\u987E\uFF1A\u5171 ${filtered.length} \u6761\u8BB0\u5F55`,
|
|
1738
|
+
data: {
|
|
1739
|
+
period: input.period,
|
|
1740
|
+
date_from: dateFrom,
|
|
1741
|
+
date_to: dateTo,
|
|
1742
|
+
total: filtered.length,
|
|
1743
|
+
by_category: Object.entries(byCategory).map(([cat, count]) => ({
|
|
1744
|
+
category: cat,
|
|
1745
|
+
label: categoryNames[cat] || cat,
|
|
1746
|
+
count
|
|
1747
|
+
})),
|
|
1748
|
+
top_tags: topTags,
|
|
1749
|
+
items: filtered.map((e) => ({
|
|
1750
|
+
id: e.id,
|
|
1751
|
+
title: e.title,
|
|
1752
|
+
category: e.category,
|
|
1753
|
+
tags: e.tags,
|
|
1754
|
+
created_at: e.created_at,
|
|
1755
|
+
preview: e.preview
|
|
1756
|
+
}))
|
|
1757
|
+
}
|
|
1758
|
+
};
|
|
1759
|
+
} catch (err) {
|
|
1760
|
+
return {
|
|
1761
|
+
success: false,
|
|
1762
|
+
message: `\u56DE\u987E\u5931\u8D25\uFF1A${err.message}`
|
|
1763
|
+
};
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1766
|
+
var inkUpdateSchema = z7.object({
|
|
1767
|
+
id: z7.string().describe("\u77E5\u8BC6\u7247\u6BB5 ID"),
|
|
1768
|
+
title: z7.string().optional().describe("\u65B0\u6807\u9898"),
|
|
1769
|
+
content: z7.string().optional().describe("\u65B0\u5185\u5BB9"),
|
|
1770
|
+
tags: z7.array(z7.string()).optional().describe("\u65B0\u6807\u7B7E\uFF08\u5B8C\u6574\u66FF\u6362\uFF09"),
|
|
1771
|
+
category: z7.enum(["insight", "decision", "analysis", "idea", "reference", "action"]).optional().describe("\u65B0\u5206\u7C7B")
|
|
1772
|
+
});
|
|
1773
|
+
async function inkUpdate(input) {
|
|
1774
|
+
try {
|
|
1775
|
+
const { id, ...updates } = input;
|
|
1776
|
+
if (!updates.title && !updates.content && !updates.tags && !updates.category) {
|
|
1777
|
+
return {
|
|
1778
|
+
success: false,
|
|
1779
|
+
message: "\u8BF7\u81F3\u5C11\u63D0\u4F9B\u4E00\u4E2A\u8981\u66F4\u65B0\u7684\u5B57\u6BB5\uFF08title / content / tags / category\uFF09"
|
|
1780
|
+
};
|
|
1781
|
+
}
|
|
1782
|
+
const result = await updateKnowledgeFile(id, updates);
|
|
1783
|
+
if (!result) {
|
|
1784
|
+
return {
|
|
1785
|
+
success: false,
|
|
1786
|
+
message: `\u672A\u627E\u5230 ID \u4E3A ${id} \u7684\u77E5\u8BC6\u7247\u6BB5`
|
|
1787
|
+
};
|
|
1788
|
+
}
|
|
1789
|
+
const changed = Object.keys(updates).filter(
|
|
1790
|
+
(k) => updates[k] !== void 0
|
|
1791
|
+
);
|
|
1792
|
+
return {
|
|
1793
|
+
success: true,
|
|
1794
|
+
message: `\u5DF2\u66F4\u65B0\u300C${result.meta.title}\u300D\u7684 ${changed.join("\u3001")}`,
|
|
1795
|
+
data: {
|
|
1796
|
+
id: result.meta.id,
|
|
1797
|
+
title: result.meta.title,
|
|
1798
|
+
tags: result.meta.tags,
|
|
1799
|
+
category: result.meta.category,
|
|
1800
|
+
updated_at: result.meta.updated_at
|
|
1801
|
+
}
|
|
1802
|
+
};
|
|
1803
|
+
} catch (err) {
|
|
1804
|
+
return {
|
|
1805
|
+
success: false,
|
|
1806
|
+
message: `\u66F4\u65B0\u5931\u8D25\uFF1A${err.message}`
|
|
1807
|
+
};
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
var inkDeleteSchema = z7.object({
|
|
1811
|
+
id: z7.string().describe("\u77E5\u8BC6\u7247\u6BB5 ID"),
|
|
1812
|
+
confirm: z7.boolean().optional().default(false).describe("\u786E\u8BA4\u5220\u9664\uFF08\u5FC5\u987B\u4E3A true \u624D\u4F1A\u6267\u884C\u5220\u9664\uFF09")
|
|
1813
|
+
});
|
|
1814
|
+
async function inkDelete(input) {
|
|
1815
|
+
try {
|
|
1816
|
+
if (!input.confirm) {
|
|
1817
|
+
const index = await readIndex();
|
|
1818
|
+
const entry = index.entries[input.id];
|
|
1819
|
+
if (!entry) {
|
|
1820
|
+
return {
|
|
1821
|
+
success: false,
|
|
1822
|
+
message: `\u672A\u627E\u5230 ID \u4E3A ${input.id} \u7684\u77E5\u8BC6\u7247\u6BB5`
|
|
1823
|
+
};
|
|
1824
|
+
}
|
|
1825
|
+
return {
|
|
1826
|
+
success: false,
|
|
1827
|
+
message: `\u786E\u8BA4\u5220\u9664\u300C${entry.title}\u300D\uFF1F\u8BF7\u5C06 confirm \u8BBE\u4E3A true \u4EE5\u786E\u8BA4\u5220\u9664\u3002`,
|
|
1828
|
+
data: {
|
|
1829
|
+
id: entry.id,
|
|
1830
|
+
title: entry.title,
|
|
1831
|
+
category: entry.category,
|
|
1832
|
+
tags: entry.tags,
|
|
1833
|
+
created_at: entry.created_at
|
|
1834
|
+
}
|
|
1835
|
+
};
|
|
1836
|
+
}
|
|
1837
|
+
const deleted = await deleteKnowledgeFile(input.id);
|
|
1838
|
+
if (!deleted) {
|
|
1839
|
+
return {
|
|
1840
|
+
success: false,
|
|
1841
|
+
message: `\u672A\u627E\u5230 ID \u4E3A ${input.id} \u7684\u77E5\u8BC6\u7247\u6BB5`
|
|
1842
|
+
};
|
|
1843
|
+
}
|
|
1844
|
+
return {
|
|
1845
|
+
success: true,
|
|
1846
|
+
message: `\u5DF2\u5220\u9664\u77E5\u8BC6\u7247\u6BB5 ${input.id}`,
|
|
1847
|
+
data: { id: input.id }
|
|
1848
|
+
};
|
|
1849
|
+
} catch (err) {
|
|
1850
|
+
return {
|
|
1851
|
+
success: false,
|
|
1852
|
+
message: `\u5220\u9664\u5931\u8D25\uFF1A${err.message}`
|
|
1853
|
+
};
|
|
1854
|
+
}
|
|
1855
|
+
}
|
|
1856
|
+
var inkCompileSchema = z7.object({
|
|
1857
|
+
ids: z7.array(z7.string()).optional().describe("\u6307\u5B9A\u8981\u6574\u5408\u7684\u77E5\u8BC6\u7247\u6BB5 ID \u5217\u8868"),
|
|
1858
|
+
query: z7.string().optional().describe("\u641C\u7D22\u6761\u4EF6\uFF08\u4E0E ids \u4E8C\u9009\u4E00\uFF09"),
|
|
1859
|
+
tags: z7.array(z7.string()).optional().describe("\u6309\u6807\u7B7E\u7B5B\u9009\uFF08\u4E0E query \u642D\u914D\uFF09"),
|
|
1860
|
+
format: z7.enum(["outline", "article", "summary"]).optional().default("summary").describe("\u8F93\u51FA\u683C\u5F0F\uFF1Aoutline\uFF08\u5927\u7EB2\uFF09/ article\uFF08\u6587\u7AE0\uFF09/ summary\uFF08\u6458\u8981\uFF09"),
|
|
1861
|
+
instruction: z7.string().optional().describe("\u989D\u5916\u6574\u5408\u6307\u4EE4")
|
|
1862
|
+
});
|
|
1863
|
+
async function inkCompile(input) {
|
|
1864
|
+
try {
|
|
1865
|
+
let ids = [];
|
|
1866
|
+
if (input.ids && input.ids.length > 0) {
|
|
1867
|
+
ids = input.ids;
|
|
1868
|
+
} else if (input.query || input.tags && input.tags.length > 0) {
|
|
1869
|
+
const index = await readIndex();
|
|
1870
|
+
const { results } = searchKnowledge(index, {
|
|
1871
|
+
query: input.query,
|
|
1872
|
+
tags: input.tags,
|
|
1873
|
+
limit: 20
|
|
1874
|
+
});
|
|
1875
|
+
ids = results.map((r) => r.id);
|
|
1876
|
+
}
|
|
1877
|
+
if (ids.length === 0) {
|
|
1878
|
+
return {
|
|
1879
|
+
success: false,
|
|
1880
|
+
message: "\u6CA1\u6709\u627E\u5230\u8981\u6574\u5408\u7684\u77E5\u8BC6\u7247\u6BB5\uFF0C\u8BF7\u6307\u5B9A ids \u6216\u63D0\u4F9B\u641C\u7D22\u6761\u4EF6"
|
|
1881
|
+
};
|
|
1882
|
+
}
|
|
1883
|
+
const files = await readMultipleKnowledgeFiles(ids);
|
|
1884
|
+
if (files.length === 0) {
|
|
1885
|
+
return {
|
|
1886
|
+
success: false,
|
|
1887
|
+
message: "\u672A\u80FD\u8BFB\u53D6\u4EFB\u4F55\u77E5\u8BC6\u7247\u6BB5\u5185\u5BB9"
|
|
1888
|
+
};
|
|
1889
|
+
}
|
|
1890
|
+
const sections = files.map((f, i) => {
|
|
1891
|
+
const tagStr = f.meta.tags.length > 0 ? ` [${f.meta.tags.join(", ")}]` : "";
|
|
1892
|
+
return `### ${i + 1}. ${f.meta.title}${tagStr}
|
|
1893
|
+
> ${f.meta.category} | ${f.meta.created_at.slice(0, 10)}
|
|
1894
|
+
|
|
1895
|
+
${f.content}`;
|
|
1896
|
+
});
|
|
1897
|
+
const formatLabels = {
|
|
1898
|
+
outline: "\u5927\u7EB2",
|
|
1899
|
+
article: "\u6587\u7AE0",
|
|
1900
|
+
summary: "\u6458\u8981"
|
|
1901
|
+
};
|
|
1902
|
+
const compiled = sections.join("\n\n---\n\n");
|
|
1903
|
+
return {
|
|
1904
|
+
success: true,
|
|
1905
|
+
message: `\u5DF2\u6574\u5408 ${files.length} \u6761\u77E5\u8BC6\u7247\u6BB5\uFF0C\u683C\u5F0F\uFF1A${formatLabels[input.format]}`,
|
|
1906
|
+
data: {
|
|
1907
|
+
source_count: files.length,
|
|
1908
|
+
source_ids: files.map((f) => f.meta.id),
|
|
1909
|
+
format: input.format,
|
|
1910
|
+
instruction: input.instruction || null,
|
|
1911
|
+
compiled_content: compiled
|
|
1912
|
+
}
|
|
1913
|
+
};
|
|
1914
|
+
} catch (err) {
|
|
1915
|
+
return {
|
|
1916
|
+
success: false,
|
|
1917
|
+
message: `\u6574\u5408\u5931\u8D25\uFF1A${err.message}`
|
|
1918
|
+
};
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
var inkTagsSchema = z7.object({
|
|
1922
|
+
action: z7.enum(["list", "rename", "merge"]).describe("\u64CD\u4F5C\uFF1Alist\uFF08\u5217\u51FA\u6240\u6709\u6807\u7B7E\uFF09/ rename\uFF08\u91CD\u547D\u540D\uFF09/ merge\uFF08\u5408\u5E76\uFF09"),
|
|
1923
|
+
tag: z7.string().optional().describe("\u76EE\u6807\u6807\u7B7E\uFF08rename/merge \u65F6\u4F7F\u7528\uFF09"),
|
|
1924
|
+
new_tag: z7.string().optional().describe("\u65B0\u6807\u7B7E\u540D\uFF08rename \u65F6\u4F7F\u7528\uFF09"),
|
|
1925
|
+
source_tags: z7.array(z7.string()).optional().describe("\u8981\u5408\u5E76\u7684\u6E90\u6807\u7B7E\u5217\u8868\uFF08merge \u65F6\u4F7F\u7528\uFF09")
|
|
1926
|
+
});
|
|
1927
|
+
async function inkTags(input) {
|
|
1928
|
+
try {
|
|
1929
|
+
const index = await readIndex();
|
|
1930
|
+
switch (input.action) {
|
|
1931
|
+
case "list": {
|
|
1932
|
+
const tags = getAllTags(index);
|
|
1933
|
+
return {
|
|
1934
|
+
success: true,
|
|
1935
|
+
message: tags.length > 0 ? `\u5171 ${tags.length} \u4E2A\u6807\u7B7E` : "\u6682\u65E0\u6807\u7B7E",
|
|
1936
|
+
data: { tags }
|
|
1937
|
+
};
|
|
1938
|
+
}
|
|
1939
|
+
case "rename": {
|
|
1940
|
+
if (!input.tag || !input.new_tag) {
|
|
1941
|
+
return {
|
|
1942
|
+
success: false,
|
|
1943
|
+
message: "rename \u64CD\u4F5C\u9700\u8981\u63D0\u4F9B tag\uFF08\u539F\u6807\u7B7E\uFF09\u548C new_tag\uFF08\u65B0\u6807\u7B7E\u540D\uFF09"
|
|
1944
|
+
};
|
|
1945
|
+
}
|
|
1946
|
+
const count = await renameTag(input.tag, input.new_tag);
|
|
1947
|
+
if (count === 0) {
|
|
1948
|
+
return {
|
|
1949
|
+
success: false,
|
|
1950
|
+
message: `\u672A\u627E\u5230\u4F7F\u7528\u6807\u7B7E\u300C${input.tag}\u300D\u7684\u77E5\u8BC6\u7247\u6BB5`
|
|
1951
|
+
};
|
|
1952
|
+
}
|
|
1953
|
+
return {
|
|
1954
|
+
success: true,
|
|
1955
|
+
message: `\u5DF2\u5C06 ${count} \u6761\u77E5\u8BC6\u7247\u6BB5\u7684\u6807\u7B7E\u300C${input.tag}\u300D\u91CD\u547D\u540D\u4E3A\u300C${input.new_tag}\u300D`,
|
|
1956
|
+
data: { old_tag: input.tag, new_tag: input.new_tag, affected: count }
|
|
1957
|
+
};
|
|
1958
|
+
}
|
|
1959
|
+
case "merge": {
|
|
1960
|
+
if (!input.source_tags || input.source_tags.length === 0 || !input.new_tag) {
|
|
1961
|
+
return {
|
|
1962
|
+
success: false,
|
|
1963
|
+
message: "merge \u64CD\u4F5C\u9700\u8981\u63D0\u4F9B source_tags\uFF08\u6E90\u6807\u7B7E\u5217\u8868\uFF09\u548C new_tag\uFF08\u76EE\u6807\u6807\u7B7E\uFF09"
|
|
1964
|
+
};
|
|
1965
|
+
}
|
|
1966
|
+
const count = await mergeTags(input.source_tags, input.new_tag);
|
|
1967
|
+
if (count === 0) {
|
|
1968
|
+
return {
|
|
1969
|
+
success: false,
|
|
1970
|
+
message: `\u672A\u627E\u5230\u4F7F\u7528\u8FD9\u4E9B\u6807\u7B7E\u7684\u77E5\u8BC6\u7247\u6BB5`
|
|
1971
|
+
};
|
|
1972
|
+
}
|
|
1973
|
+
return {
|
|
1974
|
+
success: true,
|
|
1975
|
+
message: `\u5DF2\u5C06 ${count} \u6761\u77E5\u8BC6\u7247\u6BB5\u7684\u6807\u7B7E\u5408\u5E76\u4E3A\u300C${input.new_tag}\u300D`,
|
|
1976
|
+
data: {
|
|
1977
|
+
source_tags: input.source_tags,
|
|
1978
|
+
target_tag: input.new_tag,
|
|
1979
|
+
affected: count
|
|
1980
|
+
}
|
|
1981
|
+
};
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1984
|
+
} catch (err) {
|
|
1985
|
+
return {
|
|
1986
|
+
success: false,
|
|
1987
|
+
message: `\u6807\u7B7E\u64CD\u4F5C\u5931\u8D25\uFF1A${err.message}`
|
|
1988
|
+
};
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
|
|
1097
1992
|
// src/index.ts
|
|
1098
1993
|
var server = new McpServer({
|
|
1099
1994
|
name: "ClaudeInk",
|
|
1100
|
-
version: "0.
|
|
1995
|
+
version: "0.9.0"
|
|
1101
1996
|
});
|
|
1102
1997
|
server.tool("workflow.init", "\u521D\u59CB\u5316\u5199\u4F5C\u5DE5\u4F5C\u6D41\uFF08\u91CA\u653E\u4E09\u5C42\u914D\u7F6E + \u6FC0\u6D3B License + \u81EA\u52A8\u540C\u6B65\u4E91\u7AEF\u914D\u7F6E\uFF09", workflowInitSchema.shape, async (input) => {
|
|
1103
1998
|
const result = await workflowInit(input);
|
|
1104
1999
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1105
2000
|
});
|
|
1106
|
-
server.tool("sync", "\u540C\u6B65\
|
|
2001
|
+
server.tool("sync", "\u4ECE\u4E91\u7AEF\u540C\u6B65\u5168\u91CF\u5143\u6570\u636E\u5230\u672C\u5730", syncSchema.shape, async (input) => {
|
|
1107
2002
|
const result = await sync(input);
|
|
1108
2003
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1109
2004
|
});
|
|
@@ -1127,10 +2022,14 @@ server.tool("source.subscribe", "\u7BA1\u7406\u8BA2\u9605\u6E90\uFF08\u6DFB\u52A
|
|
|
1127
2022
|
const result = await sourceSubscribe(input);
|
|
1128
2023
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1129
2024
|
});
|
|
1130
|
-
server.tool("draft.save", "\
|
|
2025
|
+
server.tool("draft.save", "\u65B0\u5EFA\u8349\u7A3F", draftSaveSchema.shape, async (input) => {
|
|
1131
2026
|
const result = await draftSave(input);
|
|
1132
2027
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1133
2028
|
});
|
|
2029
|
+
server.tool("draft.update", "\u66F4\u65B0\u5DF2\u6709\u8349\u7A3F\uFF08\u5185\u5BB9/\u6807\u9898/\u72B6\u6001/\u6807\u7B7E\uFF09", draftUpdateSchema.shape, async (input) => {
|
|
2030
|
+
const result = await draftUpdate(input);
|
|
2031
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
2032
|
+
});
|
|
1134
2033
|
server.tool("draft.publish", "\u6807\u8BB0\u8349\u7A3F\u4E3A\u5DF2\u53D1\u5E03", draftPublishSchema.shape, async (input) => {
|
|
1135
2034
|
const result = await draftPublish(input);
|
|
1136
2035
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
@@ -1143,6 +2042,38 @@ server.tool("analytics.report", "\u83B7\u53D6\u6570\u636E\u5206\u6790\u62A5\u544
|
|
|
1143
2042
|
const result = await analyticsReport(input);
|
|
1144
2043
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1145
2044
|
});
|
|
2045
|
+
server.tool("ink.save", "\u4FDD\u5B58\u77E5\u8BC6\u7247\u6BB5\u5230\u672C\u5730\u77E5\u8BC6\u5E93\uFF08\u5BF9\u8BDD\u6D1E\u5BDF\u3001\u51B3\u7B56\u3001\u5206\u6790\u7B49\uFF09", inkSaveSchema.shape, async (input) => {
|
|
2046
|
+
const result = await inkSave(input);
|
|
2047
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
2048
|
+
});
|
|
2049
|
+
server.tool("ink.search", "\u641C\u7D22\u77E5\u8BC6\u5E93\uFF08\u652F\u6301\u5173\u952E\u8BCD\u3001\u6807\u7B7E\u3001\u5206\u7C7B\u3001\u65F6\u95F4\u8303\u56F4\u8FC7\u6EE4\uFF09", inkSearchSchema.shape, async (input) => {
|
|
2050
|
+
const result = await inkSearch(input);
|
|
2051
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
2052
|
+
});
|
|
2053
|
+
server.tool("ink.get", "\u83B7\u53D6\u77E5\u8BC6\u7247\u6BB5\u5B8C\u6574\u5185\u5BB9", inkGetSchema.shape, async (input) => {
|
|
2054
|
+
const result = await inkGet(input);
|
|
2055
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
2056
|
+
});
|
|
2057
|
+
server.tool("ink.review", "\u6309\u65F6\u95F4\u6BB5\u56DE\u987E\u77E5\u8BC6\u5E93\uFF08\u7EDF\u8BA1 + \u5217\u8868\uFF09", inkReviewSchema.shape, async (input) => {
|
|
2058
|
+
const result = await inkReview(input);
|
|
2059
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
2060
|
+
});
|
|
2061
|
+
server.tool("ink.update", "\u66F4\u65B0\u5DF2\u4FDD\u5B58\u7684\u77E5\u8BC6\u7247\u6BB5\uFF08\u6807\u9898/\u5185\u5BB9/\u6807\u7B7E/\u5206\u7C7B\uFF09", inkUpdateSchema.shape, async (input) => {
|
|
2062
|
+
const result = await inkUpdate(input);
|
|
2063
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
2064
|
+
});
|
|
2065
|
+
server.tool("ink.delete", "\u5220\u9664\u77E5\u8BC6\u7247\u6BB5\uFF08\u9700\u786E\u8BA4\uFF09", inkDeleteSchema.shape, async (input) => {
|
|
2066
|
+
const result = await inkDelete(input);
|
|
2067
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
2068
|
+
});
|
|
2069
|
+
server.tool("ink.compile", "\u6574\u5408\u591A\u6761\u77E5\u8BC6\u7247\u6BB5\u4E3A\u7ED3\u6784\u5316\u5185\u5BB9\uFF08\u6309 ID \u6216\u641C\u7D22\u6761\u4EF6\uFF09", inkCompileSchema.shape, async (input) => {
|
|
2070
|
+
const result = await inkCompile(input);
|
|
2071
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
2072
|
+
});
|
|
2073
|
+
server.tool("ink.tags", "\u6807\u7B7E\u7BA1\u7406\uFF08\u5217\u51FA / \u91CD\u547D\u540D / \u5408\u5E76\u6807\u7B7E\uFF09", inkTagsSchema.shape, async (input) => {
|
|
2074
|
+
const result = await inkTags(input);
|
|
2075
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
2076
|
+
});
|
|
1146
2077
|
async function main() {
|
|
1147
2078
|
try {
|
|
1148
2079
|
const state = await readState();
|
|
@@ -1154,7 +2085,7 @@ async function main() {
|
|
|
1154
2085
|
}
|
|
1155
2086
|
const transport = new StdioServerTransport();
|
|
1156
2087
|
await server.connect(transport);
|
|
1157
|
-
console.error("[ClaudeInk MCP] Server started on stdio (
|
|
2088
|
+
console.error("[ClaudeInk MCP] Server started on stdio (17 tools)");
|
|
1158
2089
|
}
|
|
1159
2090
|
main().catch((err) => {
|
|
1160
2091
|
console.error("[ClaudeInk MCP] Fatal error:", err);
|