@claudeink/mcp-server 2.0.2 → 2.2.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.
Files changed (3) hide show
  1. package/dist/cli.js +474 -372
  2. package/dist/index.js +442 -339
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -24477,8 +24477,8 @@ var StdioServerTransport = class {
24477
24477
  };
24478
24478
 
24479
24479
  // src/tools/workflow.ts
24480
- import { mkdir as mkdir3, access, unlink } from "fs/promises";
24481
- import { join as join3 } from "path";
24480
+ import { mkdir as mkdir5, access, unlink as unlink2 } from "fs/promises";
24481
+ import { join as join5 } from "path";
24482
24482
 
24483
24483
  // src/lib/state.ts
24484
24484
  import { readFile, writeFile, mkdir, chmod } from "fs/promises";
@@ -24576,160 +24576,14 @@ async function addToTagQueue(item) {
24576
24576
  await writeState(state);
24577
24577
  }
24578
24578
 
24579
- // src/lib/logger.ts
24580
- import { appendFile, mkdir as mkdir2 } from "fs/promises";
24581
- import { join as join2 } from "path";
24582
- var currentLevel = "INFO";
24583
- var LEVELS = { ERROR: 0, WARN: 1, INFO: 2, DEBUG: 3 };
24584
- function setLogLevel(level) {
24585
- currentLevel = level;
24586
- }
24587
- async function log(level, scope, message) {
24588
- if (LEVELS[level] > LEVELS[currentLevel]) return;
24589
- const dir = join2(getClaudeinkDir(), "logs");
24590
- await mkdir2(dir, { recursive: true });
24591
- const date3 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
24592
- const line = `${(/* @__PURE__ */ new Date()).toISOString()} [${level}] ${scope} | ${message}
24593
- `;
24594
- await appendFile(join2(dir, `mcp-${date3}.log`), line);
24595
- console.error(`[ClaudeInk] ${line.trim()}`);
24596
- }
24597
-
24598
24579
  // src/tools/sync.ts
24599
- var syncSchema = external_exports.object({
24600
- workDir: external_exports.string().optional().describe("\u5DE5\u4F5C\u76EE\u5F55\uFF08\u9ED8\u8BA4\u4F7F\u7528\u914D\u7F6E\u4E2D\u7684 workflowDir\uFF09")
24601
- });
24602
- async function sync(input) {
24603
- if (input.workDir) setWorkDir(input.workDir);
24604
- const creds = await getCredentials();
24605
- if (!creds?.token) {
24606
- return { success: false, message: "\u672A\u6FC0\u6D3B\uFF0C\u8BF7\u5148\u4F7F\u7528 workflow.init \u6FC0\u6D3B License" };
24607
- }
24608
- return doPull(creds.token);
24609
- }
24610
- async function doPull(token) {
24611
- const config2 = await getConfig();
24612
- try {
24613
- const res = await fetch(`${config2.apiBaseUrl}/api/sync/pull`, {
24614
- headers: { Authorization: `Bearer ${token}` }
24615
- });
24616
- if (!res.ok) {
24617
- if (res.status === 404) {
24618
- return { success: true, message: "\u4E91\u7AEF\u65E0\u6570\u636E" };
24619
- }
24620
- await log("ERROR", "ink.sync", `pull failed: HTTP ${res.status}`);
24621
- return { success: false, message: `\u62C9\u53D6\u5931\u8D25: HTTP ${res.status}` };
24622
- }
24623
- await updateLastSyncAt();
24624
- await log("INFO", "ink.sync", "pull completed");
24625
- return {
24626
- success: true,
24627
- message: "\u2705 \u540C\u6B65\u5B8C\u6210"
24628
- };
24629
- } catch (err) {
24630
- return { success: false, message: `\u540C\u6B65\u5931\u8D25: ${err instanceof Error ? err.message : err}` };
24631
- }
24632
- }
24633
- async function syncPull(input) {
24634
- if (input.workDir) setWorkDir(input.workDir);
24635
- const creds = await getCredentials();
24636
- if (!creds?.token) {
24637
- return { success: false, message: "\u672A\u6FC0\u6D3B" };
24638
- }
24639
- return doPull(creds.token);
24640
- }
24641
-
24642
- // src/tools/workflow.ts
24643
- var DEFAULT_API_BASE_URL = "https://app.claudeink.com";
24644
- var workflowInitSchema = external_exports.object({
24645
- workDir: external_exports.string().describe("\u5DE5\u4F5C\u6D41\u521D\u59CB\u5316\u76EE\u6807\u76EE\u5F55\uFF08\u7EDD\u5BF9\u8DEF\u5F84\uFF09"),
24646
- licenseKey: external_exports.string().optional().describe("License Key\uFF08\u53EF\u9009\uFF0C\u4F20\u5165\u5219\u81EA\u52A8\u6FC0\u6D3B\uFF09")
24647
- });
24648
- async function workflowInit(input) {
24649
- const cwd = input.workDir;
24650
- const results = [];
24651
- try {
24652
- setWorkDir(cwd);
24653
- const claudeinkDir = join3(cwd, ".claudeink");
24654
- await mkdir3(join3(claudeinkDir, "knowledge"), { recursive: true });
24655
- results.push("\u2705 \u77E5\u8BC6\u5E93\u76EE\u5F55\u5DF2\u521B\u5EFA");
24656
- const state = await readState();
24657
- state.config.workflowDir = cwd;
24658
- const oldCredsPath = join3(claudeinkDir, "credentials.json");
24659
- try {
24660
- await access(oldCredsPath);
24661
- await unlink(oldCredsPath);
24662
- results.push("\u{1F9F9} \u5DF2\u6E05\u7406\u65E7\u7248 credentials.json");
24663
- } catch {
24664
- }
24665
- let activated = false;
24666
- if (input.licenseKey) {
24667
- try {
24668
- const res = await fetch(`${DEFAULT_API_BASE_URL}/api/auth/activate`, {
24669
- method: "POST",
24670
- headers: { "Content-Type": "application/json" },
24671
- body: JSON.stringify({ key: input.licenseKey })
24672
- });
24673
- const data = await res.json();
24674
- if (data.userId) {
24675
- state.credentials = {
24676
- licenseKey: input.licenseKey,
24677
- token: data.token,
24678
- userId: data.userId,
24679
- plan: data.plan,
24680
- expiresAt: data.expiresAt
24681
- };
24682
- results.push(`\u2705 License \u6FC0\u6D3B\u6210\u529F\uFF08\u5957\u9910: ${data.plan}\uFF09`);
24683
- activated = true;
24684
- } else {
24685
- results.push(`\u26A0\uFE0F License \u6FC0\u6D3B\u5931\u8D25: ${JSON.stringify(data)}`);
24686
- }
24687
- } catch (err) {
24688
- results.push(`\u26A0\uFE0F \u6FC0\u6D3B\u7F51\u7EDC\u9519\u8BEF: ${err instanceof Error ? err.message : err}`);
24689
- }
24690
- }
24691
- await writeState(state);
24692
- results.push("\u2705 state.json");
24693
- if (activated) {
24694
- try {
24695
- const pullResult = await syncPull({ workDir: cwd });
24696
- if (pullResult.success) {
24697
- results.push("\u2705 \u5DF2\u4ECE\u4E91\u7AEF\u540C\u6B65\u6570\u636E");
24698
- } else {
24699
- results.push("\u2139\uFE0F \u4E91\u7AEF\u65E0\u5DF2\u6709\u6570\u636E");
24700
- }
24701
- } catch {
24702
- results.push("\u2139\uFE0F \u4E91\u7AEF\u540C\u6B65\u8DF3\u8FC7");
24703
- }
24704
- }
24705
- return {
24706
- success: true,
24707
- message: [
24708
- "\u{1F389} ClaudeInk \u77E5\u8BC6\u5E93\u521D\u59CB\u5316\u5B8C\u6210\uFF01",
24709
- "",
24710
- ...results,
24711
- "",
24712
- "\u{1F3AF} \u4E0B\u4E00\u6B65\uFF1A",
24713
- "1. \u5728\u5BF9\u8BDD\u4E2D\u8BF4\u300C\u5E2E\u6211\u5B58\u4E00\u4E0B\u300D\u4FDD\u5B58\u6709\u4EF7\u503C\u5185\u5BB9",
24714
- "2. \u7528\u300C\u627E\u4E00\u4E0B\u5173\u4E8E XX \u7684\u300D\u68C0\u7D22\u77E5\u8BC6\u5E93"
24715
- ].join("\n")
24716
- };
24717
- } catch (err) {
24718
- return {
24719
- success: false,
24720
- message: `\u521D\u59CB\u5316\u5931\u8D25: ${err instanceof Error ? err.message : err}`
24721
- };
24722
- }
24723
- }
24724
-
24725
- // src/tools/ink.ts
24726
- import { readFile as readFile3 } from "fs/promises";
24727
- import { extname } from "path";
24580
+ import { writeFile as writeFile3, mkdir as mkdir4 } from "fs/promises";
24581
+ import { join as join4 } from "path";
24728
24582
 
24729
24583
  // src/lib/knowledge.ts
24730
24584
  var import_gray_matter = __toESM(require_gray_matter(), 1);
24731
- import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir4, unlink as unlink2 } from "fs/promises";
24732
- import { join as join4 } from "path";
24585
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, unlink } from "fs/promises";
24586
+ import { join as join2 } from "path";
24733
24587
  var _lockPromise = Promise.resolve();
24734
24588
  function withIndexLock(fn) {
24735
24589
  const prev = _lockPromise;
@@ -24739,11 +24593,11 @@ function withIndexLock(fn) {
24739
24593
  });
24740
24594
  return prev.then(fn).finally(() => resolve());
24741
24595
  }
24742
- function getKnowledgeDir() {
24743
- return join4(getWorkDir(), ".claudeink", "knowledge");
24596
+ function getStoreDir(store = "knowledge") {
24597
+ return join2(getWorkDir(), ".claudeink", store);
24744
24598
  }
24745
- function getIndexPath() {
24746
- return join4(getWorkDir(), ".claudeink", "knowledge", "index.json");
24599
+ function getIndexPath(store = "knowledge") {
24600
+ return join2(getWorkDir(), ".claudeink", store, "index.json");
24747
24601
  }
24748
24602
  async function generateIdInternal(index) {
24749
24603
  const now = /* @__PURE__ */ new Date();
@@ -24765,28 +24619,28 @@ function toSlug(title) {
24765
24619
  function generatePreview(content, maxLen = 200) {
24766
24620
  return content.replace(/^#+\s.+$/gm, "").replace(/\n+/g, " ").trim().slice(0, maxLen);
24767
24621
  }
24768
- async function readIndex() {
24622
+ async function readIndex(store = "knowledge") {
24769
24623
  try {
24770
- const raw = await readFile2(getIndexPath(), "utf-8");
24624
+ const raw = await readFile2(getIndexPath(store), "utf-8");
24771
24625
  return JSON.parse(raw);
24772
24626
  } catch {
24773
24627
  return { entries: {}, updated_at: (/* @__PURE__ */ new Date()).toISOString() };
24774
24628
  }
24775
24629
  }
24776
- async function saveIndex(index) {
24630
+ async function saveIndex(index, store = "knowledge") {
24777
24631
  index.updated_at = (/* @__PURE__ */ new Date()).toISOString();
24778
- const dir = getKnowledgeDir();
24779
- await mkdir4(dir, { recursive: true });
24780
- await writeFile2(getIndexPath(), JSON.stringify(index, null, 2), "utf-8");
24632
+ const dir = getStoreDir(store);
24633
+ await mkdir2(dir, { recursive: true });
24634
+ await writeFile2(getIndexPath(store), JSON.stringify(index, null, 2), "utf-8");
24781
24635
  }
24782
- async function writeKnowledgeFile(meta, content) {
24636
+ async function writeKnowledgeFile(meta, content, store = "knowledge") {
24783
24637
  const now = /* @__PURE__ */ new Date();
24784
24638
  const monthDir = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}`;
24785
- const dir = join4(getKnowledgeDir(), monthDir);
24786
- await mkdir4(dir, { recursive: true });
24639
+ const dir = join2(getStoreDir(store), monthDir);
24640
+ await mkdir2(dir, { recursive: true });
24787
24641
  const slug = toSlug(meta.title) || meta.id;
24788
24642
  const filename = `${now.toISOString().slice(0, 10)}-${slug}.md`;
24789
- const filePath = join4(dir, filename);
24643
+ const filePath = join2(dir, filename);
24790
24644
  const frontmatter = {
24791
24645
  id: meta.id,
24792
24646
  title: meta.title,
@@ -24799,13 +24653,13 @@ async function writeKnowledgeFile(meta, content) {
24799
24653
  if (meta.url) frontmatter.url = meta.url;
24800
24654
  const output = import_gray_matter.default.stringify(content, frontmatter);
24801
24655
  await writeFile2(filePath, output, "utf-8");
24802
- return join4(monthDir, filename);
24656
+ return join2(monthDir, filename);
24803
24657
  }
24804
- async function readKnowledgeFile(id) {
24805
- const index = await readIndex();
24658
+ async function readKnowledgeFile(id, store = "knowledge") {
24659
+ const index = await readIndex(store);
24806
24660
  const entry = index.entries[id];
24807
24661
  if (!entry) return null;
24808
- const filePath = join4(getKnowledgeDir(), entry.file_path);
24662
+ const filePath = join2(getStoreDir(store), entry.file_path);
24809
24663
  try {
24810
24664
  const raw = await readFile2(filePath, "utf-8");
24811
24665
  const { data, content } = (0, import_gray_matter.default)(raw);
@@ -24819,8 +24673,9 @@ async function readKnowledgeFile(id) {
24819
24673
  }
24820
24674
  }
24821
24675
  async function saveKnowledge(params) {
24676
+ const store = params.store || "knowledge";
24822
24677
  return withIndexLock(async () => {
24823
- const index = await readIndex();
24678
+ const index = await readIndex(store);
24824
24679
  const id = await generateIdInternal(index);
24825
24680
  const now = (/* @__PURE__ */ new Date()).toISOString();
24826
24681
  const meta = {
@@ -24833,7 +24688,7 @@ async function saveKnowledge(params) {
24833
24688
  created_at: now,
24834
24689
  updated_at: now
24835
24690
  };
24836
- const relativePath = await writeKnowledgeFile(meta, params.content);
24691
+ const relativePath = await writeKnowledgeFile(meta, params.content, store);
24837
24692
  index.entries[id] = {
24838
24693
  id,
24839
24694
  title: params.title,
@@ -24845,13 +24700,13 @@ async function saveKnowledge(params) {
24845
24700
  created_at: now,
24846
24701
  updated_at: now
24847
24702
  };
24848
- await saveIndex(index);
24703
+ await saveIndex(index, store);
24849
24704
  return { id, filePath: relativePath };
24850
24705
  });
24851
24706
  }
24852
- async function updateKnowledgeFile(id, updates) {
24707
+ async function updateKnowledgeFile(id, updates, store = "knowledge") {
24853
24708
  return withIndexLock(async () => {
24854
- const file = await readKnowledgeFile(id);
24709
+ const file = await readKnowledgeFile(id, store);
24855
24710
  if (!file) return null;
24856
24711
  const now = (/* @__PURE__ */ new Date()).toISOString();
24857
24712
  const meta = { ...file.meta };
@@ -24872,7 +24727,7 @@ async function updateKnowledgeFile(id, updates) {
24872
24727
  if (meta.url) frontmatter.url = meta.url;
24873
24728
  const output = import_gray_matter.default.stringify(content, frontmatter);
24874
24729
  await writeFile2(file.filePath, output, "utf-8");
24875
- const index = await readIndex();
24730
+ const index = await readIndex(store);
24876
24731
  const entry = index.entries[id];
24877
24732
  if (entry) {
24878
24733
  entry.title = meta.title;
@@ -24880,23 +24735,23 @@ async function updateKnowledgeFile(id, updates) {
24880
24735
  entry.category = meta.category;
24881
24736
  entry.preview = generatePreview(content);
24882
24737
  entry.updated_at = now;
24883
- await saveIndex(index);
24738
+ await saveIndex(index, store);
24884
24739
  }
24885
24740
  return { meta, content, filePath: file.filePath };
24886
24741
  });
24887
24742
  }
24888
- async function deleteKnowledgeFile(id) {
24743
+ async function deleteKnowledgeFile(id, store = "knowledge") {
24889
24744
  return withIndexLock(async () => {
24890
- const index = await readIndex();
24745
+ const index = await readIndex(store);
24891
24746
  const entry = index.entries[id];
24892
24747
  if (!entry) return false;
24893
- const filePath = join4(getKnowledgeDir(), entry.file_path);
24748
+ const filePath = join2(getStoreDir(store), entry.file_path);
24894
24749
  try {
24895
- await unlink2(filePath);
24750
+ await unlink(filePath);
24896
24751
  } catch {
24897
24752
  }
24898
24753
  delete index.entries[id];
24899
- await saveIndex(index);
24754
+ await saveIndex(index, store);
24900
24755
  return true;
24901
24756
  });
24902
24757
  }
@@ -24909,16 +24764,16 @@ function getAllTags(index) {
24909
24764
  }
24910
24765
  return Object.entries(tagCounts).map(([name, count]) => ({ name, count })).sort((a, b) => b.count - a.count);
24911
24766
  }
24912
- async function renameTag(oldTag, newTag) {
24767
+ async function renameTag(oldTag, newTag, store = "knowledge") {
24913
24768
  return withIndexLock(async () => {
24914
- const index = await readIndex();
24769
+ const index = await readIndex(store);
24915
24770
  let count = 0;
24916
24771
  for (const entry of Object.values(index.entries)) {
24917
24772
  const idx = entry.tags.indexOf(oldTag);
24918
24773
  if (idx !== -1) {
24919
24774
  entry.tags[idx] = newTag;
24920
24775
  count++;
24921
- const filePath = join4(getKnowledgeDir(), entry.file_path);
24776
+ const filePath = join2(getStoreDir(store), entry.file_path);
24922
24777
  try {
24923
24778
  const raw = await readFile2(filePath, "utf-8");
24924
24779
  const { data, content } = (0, import_gray_matter.default)(raw);
@@ -24935,14 +24790,14 @@ async function renameTag(oldTag, newTag) {
24935
24790
  }
24936
24791
  }
24937
24792
  if (count > 0) {
24938
- await saveIndex(index);
24793
+ await saveIndex(index, store);
24939
24794
  }
24940
24795
  return count;
24941
24796
  });
24942
24797
  }
24943
- async function mergeTags(sourceTags, targetTag) {
24798
+ async function mergeTags(sourceTags, targetTag, store = "knowledge") {
24944
24799
  return withIndexLock(async () => {
24945
- const index = await readIndex();
24800
+ const index = await readIndex(store);
24946
24801
  let count = 0;
24947
24802
  for (const entry of Object.values(index.entries)) {
24948
24803
  const hasSource = entry.tags.some((t) => sourceTags.includes(t));
@@ -24953,7 +24808,7 @@ async function mergeTags(sourceTags, targetTag) {
24953
24808
  }
24954
24809
  entry.tags = newTags;
24955
24810
  count++;
24956
- const filePath = join4(getKnowledgeDir(), entry.file_path);
24811
+ const filePath = join2(getStoreDir(store), entry.file_path);
24957
24812
  try {
24958
24813
  const raw = await readFile2(filePath, "utf-8");
24959
24814
  const { data, content } = (0, import_gray_matter.default)(raw);
@@ -24965,12 +24820,213 @@ async function mergeTags(sourceTags, targetTag) {
24965
24820
  }
24966
24821
  }
24967
24822
  if (count > 0) {
24968
- await saveIndex(index);
24823
+ await saveIndex(index, store);
24969
24824
  }
24970
24825
  return count;
24971
24826
  });
24972
24827
  }
24973
24828
 
24829
+ // src/lib/logger.ts
24830
+ import { appendFile, mkdir as mkdir3 } from "fs/promises";
24831
+ import { join as join3 } from "path";
24832
+ var currentLevel = "INFO";
24833
+ var LEVELS = { ERROR: 0, WARN: 1, INFO: 2, DEBUG: 3 };
24834
+ function setLogLevel(level) {
24835
+ currentLevel = level;
24836
+ }
24837
+ async function log(level, scope, message) {
24838
+ if (LEVELS[level] > LEVELS[currentLevel]) return;
24839
+ const dir = join3(getClaudeinkDir(), "logs");
24840
+ await mkdir3(dir, { recursive: true });
24841
+ const date3 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
24842
+ const line = `${(/* @__PURE__ */ new Date()).toISOString()} [${level}] ${scope} | ${message}
24843
+ `;
24844
+ await appendFile(join3(dir, `mcp-${date3}.log`), line);
24845
+ console.error(`[ClaudeInk] ${line.trim()}`);
24846
+ }
24847
+
24848
+ // src/tools/sync.ts
24849
+ var syncSchema = external_exports.object({
24850
+ workDir: external_exports.string().optional().describe("\u5DE5\u4F5C\u76EE\u5F55\uFF08\u9ED8\u8BA4\u4F7F\u7528\u914D\u7F6E\u4E2D\u7684 workflowDir\uFF09")
24851
+ });
24852
+ async function sync(input) {
24853
+ if (input.workDir) setWorkDir(input.workDir);
24854
+ const creds = await getCredentials();
24855
+ if (!creds?.token) {
24856
+ return { success: false, message: "\u672A\u6FC0\u6D3B\uFF0C\u8BF7\u5148\u4F7F\u7528 ink.init \u6FC0\u6D3B License" };
24857
+ }
24858
+ return doPull(creds.token);
24859
+ }
24860
+ async function doPull(token) {
24861
+ const config2 = await getConfig();
24862
+ try {
24863
+ const res = await fetch(`${config2.apiBaseUrl}/api/sync/pull`, {
24864
+ headers: { Authorization: `Bearer ${token}` }
24865
+ });
24866
+ if (!res.ok) {
24867
+ if (res.status === 404) {
24868
+ return { success: true, message: "\u4E91\u7AEF\u65E0\u6570\u636E" };
24869
+ }
24870
+ await log("ERROR", "ink.sync", `pull failed: HTTP ${res.status}`);
24871
+ return { success: false, message: `\u62C9\u53D6\u5931\u8D25: HTTP ${res.status}` };
24872
+ }
24873
+ const data = await res.json();
24874
+ const results = [];
24875
+ if (data.configs) {
24876
+ const crawlerConfig = data.configs.find((c) => c.type === "crawler" && c.name === "config");
24877
+ if (crawlerConfig?.content) {
24878
+ try {
24879
+ const sources = JSON.parse(crawlerConfig.content);
24880
+ const crawlerDir = join4(config2.workflowDir || getWorkDir(), "tools", "crawler");
24881
+ await mkdir4(crawlerDir, { recursive: true });
24882
+ await writeFile3(join4(crawlerDir, "config.json"), JSON.stringify({ sources }, null, 2));
24883
+ results.push(`\u8BA2\u9605\u6E90: ${sources.length} \u4E2A`);
24884
+ } catch {
24885
+ }
24886
+ }
24887
+ }
24888
+ if (data.sources && Object.keys(data.sources).length > 0) {
24889
+ const index = await readIndex();
24890
+ let added = 0;
24891
+ for (const [localId, meta] of Object.entries(data.sources)) {
24892
+ if (!index.entries[localId]) {
24893
+ const entry = {
24894
+ id: localId,
24895
+ title: meta.title || "",
24896
+ tags: meta.tags || [],
24897
+ category: "reference",
24898
+ source_type: "url",
24899
+ preview: "",
24900
+ file_path: "",
24901
+ created_at: meta.publishedAt || (/* @__PURE__ */ new Date()).toISOString(),
24902
+ updated_at: meta.updatedAt || (/* @__PURE__ */ new Date()).toISOString()
24903
+ };
24904
+ index.entries[localId] = entry;
24905
+ added++;
24906
+ }
24907
+ }
24908
+ if (added > 0) {
24909
+ index.updated_at = (/* @__PURE__ */ new Date()).toISOString();
24910
+ const knowledgeDir = join4(getWorkDir(), ".claudeink", "knowledge");
24911
+ await mkdir4(knowledgeDir, { recursive: true });
24912
+ await writeFile3(join4(knowledgeDir, "index.json"), JSON.stringify(index, null, 2));
24913
+ results.push(`\u7D20\u6750\u7D22\u5F15: +${added} \u6761\uFF08\u5171 ${Object.keys(index.entries).length} \u6761\uFF09`);
24914
+ } else {
24915
+ results.push(`\u7D20\u6750\u7D22\u5F15: ${Object.keys(index.entries).length} \u6761\uFF08\u5DF2\u540C\u6B65\uFF09`);
24916
+ }
24917
+ }
24918
+ const draftCount = data.drafts ? Object.keys(data.drafts).length : 0;
24919
+ const pubCount = data.published ? Object.keys(data.published).length : 0;
24920
+ if (draftCount > 0 || pubCount > 0) {
24921
+ results.push(`\u8349\u7A3F: ${draftCount} \u7BC7, \u5DF2\u53D1\u5E03: ${pubCount} \u7BC7`);
24922
+ }
24923
+ await updateLastSyncAt();
24924
+ await log("INFO", "ink.sync", `pull completed: ${results.join(", ")}`);
24925
+ return {
24926
+ success: true,
24927
+ message: results.length > 0 ? `\u2705 \u540C\u6B65\u5B8C\u6210
24928
+ ${results.join("\n")}` : "\u2705 \u540C\u6B65\u5B8C\u6210\uFF08\u4E91\u7AEF\u65E0\u65B0\u6570\u636E\uFF09"
24929
+ };
24930
+ } catch (err) {
24931
+ return { success: false, message: `\u540C\u6B65\u5931\u8D25: ${err instanceof Error ? err.message : err}` };
24932
+ }
24933
+ }
24934
+ async function syncPull(input) {
24935
+ if (input.workDir) setWorkDir(input.workDir);
24936
+ const creds = await getCredentials();
24937
+ if (!creds?.token) {
24938
+ return { success: false, message: "\u672A\u6FC0\u6D3B" };
24939
+ }
24940
+ return doPull(creds.token);
24941
+ }
24942
+
24943
+ // src/tools/workflow.ts
24944
+ var DEFAULT_API_BASE_URL = "https://app.claudeink.com";
24945
+ var workflowInitSchema = external_exports.object({
24946
+ workDir: external_exports.string().describe("\u5DE5\u4F5C\u6D41\u521D\u59CB\u5316\u76EE\u6807\u76EE\u5F55\uFF08\u7EDD\u5BF9\u8DEF\u5F84\uFF09"),
24947
+ licenseKey: external_exports.string().optional().describe("License Key\uFF08\u53EF\u9009\uFF0C\u4F20\u5165\u5219\u81EA\u52A8\u6FC0\u6D3B\uFF09")
24948
+ });
24949
+ async function workflowInit(input) {
24950
+ const cwd = input.workDir;
24951
+ const results = [];
24952
+ try {
24953
+ setWorkDir(cwd);
24954
+ const claudeinkDir = join5(cwd, ".claudeink");
24955
+ await mkdir5(join5(claudeinkDir, "knowledge"), { recursive: true });
24956
+ results.push("\u2705 \u77E5\u8BC6\u5E93\u76EE\u5F55\u5DF2\u521B\u5EFA");
24957
+ const state = await readState();
24958
+ state.config.workflowDir = cwd;
24959
+ const oldCredsPath = join5(claudeinkDir, "credentials.json");
24960
+ try {
24961
+ await access(oldCredsPath);
24962
+ await unlink2(oldCredsPath);
24963
+ results.push("\u{1F9F9} \u5DF2\u6E05\u7406\u65E7\u7248 credentials.json");
24964
+ } catch {
24965
+ }
24966
+ let activated = false;
24967
+ if (input.licenseKey) {
24968
+ try {
24969
+ const res = await fetch(`${DEFAULT_API_BASE_URL}/api/auth/activate`, {
24970
+ method: "POST",
24971
+ headers: { "Content-Type": "application/json" },
24972
+ body: JSON.stringify({ key: input.licenseKey })
24973
+ });
24974
+ const data = await res.json();
24975
+ if (data.userId) {
24976
+ state.credentials = {
24977
+ licenseKey: input.licenseKey,
24978
+ token: data.token,
24979
+ userId: data.userId,
24980
+ plan: data.plan,
24981
+ expiresAt: data.expiresAt
24982
+ };
24983
+ results.push(`\u2705 License \u6FC0\u6D3B\u6210\u529F\uFF08\u5957\u9910: ${data.plan}\uFF09`);
24984
+ activated = true;
24985
+ } else {
24986
+ results.push(`\u26A0\uFE0F License \u6FC0\u6D3B\u5931\u8D25: ${JSON.stringify(data)}`);
24987
+ }
24988
+ } catch (err) {
24989
+ results.push(`\u26A0\uFE0F \u6FC0\u6D3B\u7F51\u7EDC\u9519\u8BEF: ${err instanceof Error ? err.message : err}`);
24990
+ }
24991
+ }
24992
+ await writeState(state);
24993
+ results.push("\u2705 state.json");
24994
+ if (state.credentials?.token) {
24995
+ try {
24996
+ const pullResult = await syncPull({ workDir: cwd });
24997
+ if (pullResult.success) {
24998
+ results.push("\u2705 \u5DF2\u4ECE\u4E91\u7AEF\u540C\u6B65\u6570\u636E");
24999
+ } else {
25000
+ results.push("\u2139\uFE0F \u4E91\u7AEF\u65E0\u5DF2\u6709\u6570\u636E");
25001
+ }
25002
+ } catch {
25003
+ results.push("\u2139\uFE0F \u4E91\u7AEF\u540C\u6B65\u8DF3\u8FC7");
25004
+ }
25005
+ }
25006
+ return {
25007
+ success: true,
25008
+ message: [
25009
+ "\u{1F389} ClaudeInk \u77E5\u8BC6\u5E93\u521D\u59CB\u5316\u5B8C\u6210\uFF01",
25010
+ "",
25011
+ ...results,
25012
+ "",
25013
+ "\u{1F3AF} \u4E0B\u4E00\u6B65\uFF1A",
25014
+ "1. \u5728\u5BF9\u8BDD\u4E2D\u8BF4\u300C\u5E2E\u6211\u5B58\u4E00\u4E0B\u300D\u4FDD\u5B58\u6709\u4EF7\u503C\u5185\u5BB9",
25015
+ "2. \u7528\u300C\u627E\u4E00\u4E0B\u5173\u4E8E XX \u7684\u300D\u68C0\u7D22\u77E5\u8BC6\u5E93"
25016
+ ].join("\n")
25017
+ };
25018
+ } catch (err) {
25019
+ return {
25020
+ success: false,
25021
+ message: `\u521D\u59CB\u5316\u5931\u8D25: ${err instanceof Error ? err.message : err}`
25022
+ };
25023
+ }
25024
+ }
25025
+
25026
+ // src/tools/ink.ts
25027
+ import { readFile as readFile3 } from "fs/promises";
25028
+ import { extname } from "path";
25029
+
24974
25030
  // src/lib/push.ts
24975
25031
  function fireAndForgetPush(payload) {
24976
25032
  doPush(payload).catch(() => {
@@ -25001,9 +25057,9 @@ async function doPush(partial2) {
25001
25057
 
25002
25058
  // src/tools/ink.ts
25003
25059
  var inkSaveSchema = external_exports.object({
25004
- id: external_exports.string().optional().describe("\u77E5\u8BC6\u7247\u6BB5 ID\uFF08\u63D0\u4F9B\u5219\u66F4\u65B0\uFF0C\u4E0D\u63D0\u4F9B\u5219\u65B0\u5EFA\uFF09"),
25005
- content: external_exports.string().describe("\u8981\u4FDD\u5B58\u7684\u5185\u5BB9\uFF08\u5FC5\u586B\uFF09"),
25006
- title: external_exports.string().describe("\u6807\u9898\uFF08Claude \u81EA\u52A8\u751F\u6210\uFF09"),
25060
+ id: external_exports.string().optional().describe("\u672C\u5730 ID\uFF08\u63D0\u4F9B\u5219\u66F4\u65B0\u5DF2\u6709\u7D20\u6750\uFF0C\u4E0D\u63D0\u4F9B\u5219\u65B0\u5EFA\uFF09"),
25061
+ title: external_exports.string().describe("\u7D20\u6750\u6807\u9898"),
25062
+ content: external_exports.string().describe("Markdown \u5168\u6587"),
25007
25063
  tags: external_exports.array(external_exports.string()).optional().default([]).describe("\u6807\u7B7E"),
25008
25064
  category: external_exports.enum(["insight", "decision", "analysis", "idea", "reference", "action"]).optional().default("reference").describe("\u5206\u7C7B"),
25009
25065
  source_type: external_exports.enum(["conversation", "url", "manual"]).optional().default("conversation").describe("\u6765\u6E90\u7C7B\u578B"),
@@ -25011,120 +25067,102 @@ var inkSaveSchema = external_exports.object({
25011
25067
  });
25012
25068
  async function inkSave(input) {
25013
25069
  try {
25070
+ let localId;
25014
25071
  if (input.id) {
25015
25072
  const result = await updateKnowledgeFile(input.id, {
25016
25073
  title: input.title,
25017
25074
  content: input.content,
25018
25075
  tags: input.tags,
25019
25076
  category: input.category
25020
- });
25077
+ }, "knowledge");
25021
25078
  if (!result) {
25022
- return { success: false, message: `\u672A\u627E\u5230 ID \u4E3A ${input.id} \u7684\u77E5\u8BC6\u7247\u6BB5` };
25079
+ return { success: false, message: `\u672A\u627E\u5230 ID \u4E3A ${input.id} \u7684\u7D20\u6750` };
25023
25080
  }
25024
- fireAndForgetPush({
25025
- knowledge: [{
25026
- id: result.meta.id,
25027
- title: result.meta.title,
25028
- tags: result.meta.tags,
25029
- category: result.meta.category,
25030
- source_type: result.meta.source_type,
25031
- url: result.meta.url,
25032
- created_at: result.meta.created_at,
25033
- updated_at: result.meta.updated_at
25034
- }]
25035
- });
25036
- await log("INFO", "ink.save", `updated id=${input.id} title="${input.title}"`);
25037
- return {
25038
- success: true,
25039
- message: `\u5DF2\u66F4\u65B0\u300C${result.meta.title}\u300D`,
25040
- data: { id: result.meta.id }
25041
- };
25042
- }
25043
- const { id } = await saveKnowledge({
25044
- content: input.content,
25045
- title: input.title,
25046
- tags: input.tags,
25047
- category: input.category,
25048
- source_type: input.source_type,
25049
- url: input.url
25050
- });
25051
- fireAndForgetPush({
25052
- knowledge: [{
25053
- id,
25081
+ localId = input.id;
25082
+ } else {
25083
+ const { id } = await saveKnowledge({
25084
+ content: input.content,
25054
25085
  title: input.title,
25055
25086
  tags: input.tags,
25056
25087
  category: input.category,
25057
25088
  source_type: input.source_type,
25058
25089
  url: input.url,
25059
- created_at: (/* @__PURE__ */ new Date()).toISOString(),
25060
- updated_at: (/* @__PURE__ */ new Date()).toISOString()
25061
- }]
25062
- });
25063
- await log("INFO", "ink.save", `created id=${id} title="${input.title}" tags=${JSON.stringify(input.tags)}`);
25090
+ store: "knowledge"
25091
+ });
25092
+ localId = id;
25093
+ }
25094
+ const creds = await getCredentials();
25095
+ if (creds?.token) {
25096
+ const index = await readIndex("knowledge");
25097
+ fireAndForgetPush({
25098
+ knowledge: Object.values(index.entries).map((e) => ({
25099
+ id: e.id,
25100
+ title: e.title,
25101
+ tags: e.tags,
25102
+ category: e.category,
25103
+ source_type: e.source_type,
25104
+ created_at: e.created_at,
25105
+ updated_at: e.updated_at
25106
+ })),
25107
+ crawlerSources: []
25108
+ });
25109
+ }
25110
+ await log("INFO", "ink.save", `saved source id=${localId} title="${input.title}"`);
25064
25111
  return {
25065
25112
  success: true,
25066
- message: `\u5DF2\u4FDD\u5B58\u300C${input.title}\u300D`,
25067
- data: { id }
25113
+ message: `\u5DF2\u4FDD\u5B58\u7D20\u6750\u300C${input.title}\u300D\u5230\u672C\u5730\u77E5\u8BC6\u5E93` + (creds?.token ? "\uFF08\u5143\u6570\u636E\u5DF2\u63A8\u4E91\u7AEF\uFF09" : "\uFF08\u672A\u767B\u5F55\uFF0C\u8DF3\u8FC7\u4E91\u7AEF\u540C\u6B65\uFF09"),
25114
+ data: { id: localId }
25068
25115
  };
25069
25116
  } catch (err) {
25070
25117
  await log("ERROR", "ink.save", `failed: ${err.message}`);
25071
- return { success: false, message: `\u4FDD\u5B58\u5931\u8D25\uFF1A${err.message}` };
25072
- }
25073
- }
25074
- var inkDeleteSchema = external_exports.object({
25075
- id: external_exports.string().describe("\u77E5\u8BC6\u7247\u6BB5 ID"),
25076
- confirm: external_exports.boolean().optional().default(false).describe("\u786E\u8BA4\u5220\u9664\uFF08\u5FC5\u987B\u4E3A true \u624D\u4F1A\u6267\u884C\u5220\u9664\uFF09")
25077
- });
25078
- async function inkDelete(input) {
25079
- try {
25080
- if (!input.confirm) {
25081
- const index = await readIndex();
25082
- const entry = index.entries[input.id];
25083
- if (!entry) {
25084
- return { success: false, message: `\u672A\u627E\u5230 ID \u4E3A ${input.id} \u7684\u77E5\u8BC6\u7247\u6BB5` };
25085
- }
25086
- return {
25087
- success: false,
25088
- message: `\u786E\u8BA4\u5220\u9664\u300C${entry.title}\u300D\uFF1F\u8BF7\u5C06 confirm \u8BBE\u4E3A true \u4EE5\u786E\u8BA4\u5220\u9664\u3002`,
25089
- data: { id: entry.id, title: entry.title, category: entry.category, tags: entry.tags }
25090
- };
25091
- }
25092
- const deleted = await deleteKnowledgeFile(input.id);
25093
- if (!deleted) {
25094
- return { success: false, message: `\u672A\u627E\u5230 ID \u4E3A ${input.id} \u7684\u77E5\u8BC6\u7247\u6BB5` };
25095
- }
25096
- fireAndForgetPush({
25097
- knowledge: [{
25098
- id: input.id,
25099
- title: "",
25100
- tags: [],
25101
- category: "reference",
25102
- source_type: "conversation",
25103
- created_at: "",
25104
- updated_at: (/* @__PURE__ */ new Date()).toISOString()
25105
- }]
25106
- });
25107
- await log("INFO", "ink.delete", `deleted id=${input.id}`);
25108
- return { success: true, message: `\u5DF2\u5220\u9664\u77E5\u8BC6\u7247\u6BB5 ${input.id}`, data: { id: input.id } };
25109
- } catch (err) {
25110
- await log("ERROR", "ink.delete", `failed: ${err.message}`);
25111
- return { success: false, message: `\u5220\u9664\u5931\u8D25\uFF1A${err.message}` };
25118
+ return { success: false, message: `\u4FDD\u5B58\u7D20\u6750\u5931\u8D25: ${err.message}` };
25112
25119
  }
25113
25120
  }
25114
25121
  var inkWriteSchema = external_exports.object({
25122
+ id: external_exports.string().optional().describe("\u672C\u5730 ID\uFF08\u63D0\u4F9B\u5219\u66F4\u65B0\u5DF2\u6709\u8349\u7A3F\uFF0C\u4E0D\u63D0\u4F9B\u5219\u65B0\u5EFA\uFF09"),
25115
25123
  title: external_exports.string().describe("\u6587\u7AE0\u6807\u9898"),
25116
25124
  content: external_exports.string().describe("Markdown \u5168\u6587"),
25125
+ tags: external_exports.array(external_exports.string()).optional().default([]).describe("\u6807\u7B7E"),
25126
+ category: external_exports.enum(["insight", "decision", "analysis", "idea", "reference", "action"]).optional().default("reference").describe("\u5206\u7C7B"),
25117
25127
  cover: external_exports.string().optional().describe("\u5C01\u9762\u56FE\uFF08\u672C\u5730\u8DEF\u5F84\u6216 URL\uFF0C\u81EA\u52A8\u4E0A\u4F20\uFF09"),
25118
- tags: external_exports.array(external_exports.string()).optional().default([]).describe("\u6587\u7AE0\u6807\u7B7E"),
25119
- category: external_exports.string().optional().describe("\u6587\u7AE0\u5206\u7C7B"),
25120
25128
  excerpt: external_exports.string().optional().describe("\u6458\u8981\uFF08\u4E0D\u586B\u81EA\u52A8\u622A\u53D6\u524D 200 \u5B57\uFF09"),
25121
- source_ids: external_exports.array(external_exports.string()).optional().describe("\u5173\u8054\u7684\u7D20\u6750 ink ID\uFF08\u6EAF\u6E90\uFF09")
25129
+ source_ids: external_exports.array(external_exports.string()).optional().describe("\u5173\u8054\u7684\u7D20\u6750 ID\uFF08\u6EAF\u6E90\uFF09")
25122
25130
  });
25123
25131
  async function inkWrite(input) {
25124
- const creds = await getCredentials();
25125
- if (!creds?.token) return { success: false, message: "\u672A\u8BA4\u8BC1\uFF0C\u8BF7\u5148\u8FD0\u884C ink.init" };
25126
- const config2 = await getConfig();
25127
25132
  try {
25133
+ let localId;
25134
+ if (input.id) {
25135
+ const result = await updateKnowledgeFile(input.id, {
25136
+ title: input.title,
25137
+ content: input.content,
25138
+ tags: input.tags,
25139
+ category: input.category
25140
+ }, "drafts");
25141
+ if (!result) {
25142
+ return { success: false, message: `\u672A\u627E\u5230 ID \u4E3A ${input.id} \u7684\u8349\u7A3F` };
25143
+ }
25144
+ localId = input.id;
25145
+ } else {
25146
+ const { id } = await saveKnowledge({
25147
+ content: input.content,
25148
+ title: input.title,
25149
+ tags: input.tags,
25150
+ category: input.category,
25151
+ source_type: "conversation",
25152
+ store: "drafts"
25153
+ });
25154
+ localId = id;
25155
+ }
25156
+ const creds = await getCredentials();
25157
+ if (!creds?.token) {
25158
+ await log("INFO", "ink.write", `saved draft locally id=${localId} title="${input.title}" (no cloud auth)`);
25159
+ return {
25160
+ success: true,
25161
+ message: `\u5DF2\u4FDD\u5B58\u8349\u7A3F\u300C${input.title}\u300D\u5230\u672C\u5730\uFF08\u672A\u767B\u5F55\uFF0C\u8DF3\u8FC7\u4E91\u7AEF\u540C\u6B65\uFF09`,
25162
+ data: { id: localId }
25163
+ };
25164
+ }
25165
+ const config2 = await getConfig();
25128
25166
  let coverUrl = input.cover || null;
25129
25167
  if (coverUrl) {
25130
25168
  if (coverUrl.startsWith("http://") || coverUrl.startsWith("https://")) {
@@ -25142,38 +25180,103 @@ async function inkWrite(input) {
25142
25180
  }
25143
25181
  const slug = `${Date.now().toString(36)}-${input.title.toLowerCase().replace(/[^a-z0-9\u4e00-\u9fff]+/g, "-").replace(/^-|-$/g, "").slice(0, 60)}`;
25144
25182
  const excerpt = input.excerpt || input.content.replace(/^#+\s.+$/gm, "").replace(/\n+/g, " ").trim().slice(0, 200);
25145
- const res = await fetch(`${config2.apiBaseUrl}/api/blog/manage`, {
25146
- method: "POST",
25147
- headers: {
25148
- "Content-Type": "application/json",
25149
- Authorization: `Bearer ${creds.token}`
25150
- },
25151
- body: JSON.stringify({
25152
- title: input.title,
25153
- slug,
25154
- content: input.content,
25155
- cover_url: coverUrl,
25156
- tags: input.tags,
25157
- category: input.category,
25158
- excerpt,
25159
- source_ids: input.source_ids
25160
- })
25161
- });
25162
- if (!res.ok) {
25163
- const err = await res.text();
25164
- await log("ERROR", "ink.write", `save failed: ${err}`);
25165
- return { success: false, message: `\u4FDD\u5B58\u5931\u8D25: ${err}` };
25183
+ try {
25184
+ const controller = new AbortController();
25185
+ const timeout = setTimeout(() => controller.abort(), 1e4);
25186
+ const res = await fetch(`${config2.apiBaseUrl}/api/blog/manage`, {
25187
+ method: "POST",
25188
+ signal: controller.signal,
25189
+ headers: {
25190
+ "Content-Type": "application/json",
25191
+ Authorization: `Bearer ${creds.token}`
25192
+ },
25193
+ body: JSON.stringify({
25194
+ title: input.title,
25195
+ slug,
25196
+ content: input.content,
25197
+ cover_url: coverUrl,
25198
+ tags: input.tags,
25199
+ category: input.category,
25200
+ excerpt,
25201
+ source_type: "conversation",
25202
+ source_ids: input.source_ids,
25203
+ local_id: localId
25204
+ })
25205
+ });
25206
+ clearTimeout(timeout);
25207
+ if (!res.ok) {
25208
+ const err = await res.text();
25209
+ await log("ERROR", "ink.write", `cloud save failed: ${err}`);
25210
+ return {
25211
+ success: true,
25212
+ message: `\u5DF2\u4FDD\u5B58\u8349\u7A3F\u300C${input.title}\u300D\u5230\u672C\u5730\uFF0C\u4F46\u4E91\u7AEF\u540C\u6B65\u5931\u8D25: ${err}`,
25213
+ data: { id: localId }
25214
+ };
25215
+ }
25216
+ const data = await res.json();
25217
+ await log("INFO", "ink.write", `saved draft id=${localId} cloud_id=${data.id} title="${input.title}"`);
25218
+ return {
25219
+ success: true,
25220
+ message: `\u5DF2\u4FDD\u5B58\u8349\u7A3F\u300C${input.title}\u300D\uFF08\u672C\u5730 + \u4E91\u7AEF\uFF09`,
25221
+ data: { id: localId, cloud_id: data.id, slug: data.slug, status: data.status }
25222
+ };
25223
+ } catch (cloudErr) {
25224
+ await log("WARN", "ink.write", `cloud upload failed (local saved): ${cloudErr.message}`);
25225
+ return {
25226
+ success: true,
25227
+ message: `\u5DF2\u4FDD\u5B58\u8349\u7A3F\u300C${input.title}\u300D\u5230\u672C\u5730\uFF0C\u4F46\u4E91\u7AEF\u540C\u6B65\u5931\u8D25: ${cloudErr.message}`,
25228
+ data: { id: localId }
25229
+ };
25166
25230
  }
25167
- const data = await res.json();
25168
- await log("INFO", "ink.write", `saved article="${input.title}" id=${data.id} slug=${data.slug}`);
25169
- return {
25170
- success: true,
25171
- message: `\u6587\u7AE0\u300C${input.title}\u300D\u5DF2\u4FDD\u5B58\u4E3A\u8349\u7A3F\uFF0C\u8BF7\u5230 Dashboard \u53D1\u5E03`,
25172
- data: { id: data.id, slug: data.slug, status: data.status }
25173
- };
25174
25231
  } catch (err) {
25175
25232
  await log("ERROR", "ink.write", `failed: ${err.message}`);
25176
- return { success: false, message: `\u5199\u6587\u7AE0\u5931\u8D25: ${err.message}` };
25233
+ return { success: false, message: `\u4FDD\u5B58\u8349\u7A3F\u5931\u8D25: ${err.message}` };
25234
+ }
25235
+ }
25236
+ var inkDeleteSchema = external_exports.object({
25237
+ id: external_exports.string().describe("\u6761\u76EE ID"),
25238
+ store: external_exports.enum(["knowledge", "drafts"]).optional().default("knowledge").describe("\u5B58\u50A8\u7C7B\u578B\uFF1Aknowledge\uFF08\u7D20\u6750\uFF09\u6216 drafts\uFF08\u8349\u7A3F\uFF09"),
25239
+ confirm: external_exports.boolean().optional().default(false).describe("\u786E\u8BA4\u5220\u9664\uFF08\u5FC5\u987B\u4E3A true \u624D\u4F1A\u6267\u884C\u5220\u9664\uFF09")
25240
+ });
25241
+ async function inkDelete(input) {
25242
+ try {
25243
+ const store = input.store;
25244
+ if (!input.confirm) {
25245
+ const index = await readIndex(store);
25246
+ const entry = index.entries[input.id];
25247
+ if (!entry) {
25248
+ return { success: false, message: `\u672A\u627E\u5230 ID \u4E3A ${input.id} \u7684\u6761\u76EE` };
25249
+ }
25250
+ return {
25251
+ success: false,
25252
+ message: `\u786E\u8BA4\u5220\u9664\u300C${entry.title}\u300D\uFF1F\u8BF7\u5C06 confirm \u8BBE\u4E3A true \u4EE5\u786E\u8BA4\u5220\u9664\u3002`,
25253
+ data: { id: entry.id, title: entry.title, store, category: entry.category, tags: entry.tags }
25254
+ };
25255
+ }
25256
+ const deleted = await deleteKnowledgeFile(input.id, store);
25257
+ if (!deleted) {
25258
+ return { success: false, message: `\u672A\u627E\u5230 ID \u4E3A ${input.id} \u7684\u6761\u76EE` };
25259
+ }
25260
+ try {
25261
+ const creds = await getCredentials();
25262
+ if (creds?.token) {
25263
+ const config2 = await getConfig();
25264
+ await fetch(`${config2.apiBaseUrl}/api/blog/manage`, {
25265
+ method: "DELETE",
25266
+ headers: {
25267
+ "Content-Type": "application/json",
25268
+ Authorization: `Bearer ${creds.token}`
25269
+ },
25270
+ body: JSON.stringify({ local_id: input.id })
25271
+ });
25272
+ }
25273
+ } catch {
25274
+ }
25275
+ await log("INFO", "ink.delete", `deleted id=${input.id} store=${store}`);
25276
+ return { success: true, message: `\u5DF2\u5220\u9664 ${input.id}`, data: { id: input.id } };
25277
+ } catch (err) {
25278
+ await log("ERROR", "ink.delete", `failed: ${err.message}`);
25279
+ return { success: false, message: `\u5220\u9664\u5931\u8D25\uFF1A${err.message}` };
25177
25280
  }
25178
25281
  }
25179
25282
  var inkTagsSchema = external_exports.object({
@@ -25184,7 +25287,7 @@ var inkTagsSchema = external_exports.object({
25184
25287
  });
25185
25288
  async function inkTags(input) {
25186
25289
  try {
25187
- const index = await readIndex();
25290
+ const index = await readIndex("knowledge");
25188
25291
  switch (input.action) {
25189
25292
  case "list": {
25190
25293
  const tags = getAllTags(index);
@@ -25198,14 +25301,14 @@ async function inkTags(input) {
25198
25301
  if (!input.tag || !input.new_tag) {
25199
25302
  return { success: false, message: "rename \u9700\u8981 tag\uFF08\u539F\u6807\u7B7E\uFF09\u548C new_tag\uFF08\u65B0\u6807\u7B7E\u540D\uFF09" };
25200
25303
  }
25201
- const count = await renameTag(input.tag, input.new_tag);
25304
+ const count = await renameTag(input.tag, input.new_tag, "knowledge");
25202
25305
  if (count === 0) {
25203
- return { success: false, message: `\u672A\u627E\u5230\u4F7F\u7528\u6807\u7B7E\u300C${input.tag}\u300D\u7684\u77E5\u8BC6\u7247\u6BB5` };
25306
+ return { success: false, message: `\u672A\u627E\u5230\u4F7F\u7528\u6807\u7B7E\u300C${input.tag}\u300D\u7684\u6761\u76EE` };
25204
25307
  }
25205
25308
  await log("INFO", "ink.tags", `renamed "${input.tag}" \u2192 "${input.new_tag}", affected=${count}`);
25206
25309
  return {
25207
25310
  success: true,
25208
- 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`,
25311
+ message: `\u5DF2\u5C06 ${count} \u6761\u7684\u6807\u7B7E\u300C${input.tag}\u300D\u91CD\u547D\u540D\u4E3A\u300C${input.new_tag}\u300D`,
25209
25312
  data: { old_tag: input.tag, new_tag: input.new_tag, affected: count }
25210
25313
  };
25211
25314
  }
@@ -25213,14 +25316,14 @@ async function inkTags(input) {
25213
25316
  if (!input.source_tags || input.source_tags.length === 0 || !input.new_tag) {
25214
25317
  return { success: false, message: "merge \u9700\u8981 source_tags\uFF08\u6E90\u6807\u7B7E\u5217\u8868\uFF09\u548C new_tag\uFF08\u76EE\u6807\u6807\u7B7E\uFF09" };
25215
25318
  }
25216
- const count = await mergeTags(input.source_tags, input.new_tag);
25319
+ const count = await mergeTags(input.source_tags, input.new_tag, "knowledge");
25217
25320
  if (count === 0) {
25218
- return { success: false, message: "\u672A\u627E\u5230\u4F7F\u7528\u8FD9\u4E9B\u6807\u7B7E\u7684\u77E5\u8BC6\u7247\u6BB5" };
25321
+ return { success: false, message: "\u672A\u627E\u5230\u4F7F\u7528\u8FD9\u4E9B\u6807\u7B7E\u7684\u6761\u76EE" };
25219
25322
  }
25220
25323
  await log("INFO", "ink.tags", `merged [${input.source_tags.join(",")}] \u2192 "${input.new_tag}", affected=${count}`);
25221
25324
  return {
25222
25325
  success: true,
25223
- message: `\u5DF2\u5C06 ${count} \u6761\u77E5\u8BC6\u7247\u6BB5\u7684\u6807\u7B7E\u5408\u5E76\u4E3A\u300C${input.new_tag}\u300D`,
25326
+ message: `\u5DF2\u5C06 ${count} \u6761\u7684\u6807\u7B7E\u5408\u5E76\u4E3A\u300C${input.new_tag}\u300D`,
25224
25327
  data: { source_tags: input.source_tags, target_tag: input.new_tag, affected: count }
25225
25328
  };
25226
25329
  }
@@ -25289,7 +25392,7 @@ async function uploadImageFromUrl(imageUrl, type, token, apiBase) {
25289
25392
  // src/tools/source.ts
25290
25393
  var import_gray_matter2 = __toESM(require_gray_matter(), 1);
25291
25394
  import { readFile as readFile4 } from "fs/promises";
25292
- import { join as join5 } from "path";
25395
+ import { join as join6 } from "path";
25293
25396
  import { readdirSync } from "fs";
25294
25397
  import { execSync } from "child_process";
25295
25398
  var sourceCrawlSchema = external_exports.object({
@@ -25297,8 +25400,8 @@ var sourceCrawlSchema = external_exports.object({
25297
25400
  });
25298
25401
  async function sourceCrawl(input) {
25299
25402
  const config2 = await getConfig();
25300
- const crawlerDir = join5(config2.workflowDir || process.cwd(), "tools", "crawler");
25301
- const configPath = join5(crawlerDir, "config.json");
25403
+ const crawlerDir = join6(config2.workflowDir || process.cwd(), "tools", "crawler");
25404
+ const configPath = join6(crawlerDir, "config.json");
25302
25405
  let crawlerConfig;
25303
25406
  try {
25304
25407
  crawlerConfig = JSON.parse(await readFile4(configPath, "utf-8"));
@@ -25315,7 +25418,7 @@ async function sourceCrawl(input) {
25315
25418
  message: input.sourceId ? `\u672A\u627E\u5230\u722C\u866B\u6E90: ${input.sourceId}` : "\u6CA1\u6709\u5DF2\u542F\u7528\u7684\u722C\u866B\u6E90"
25316
25419
  };
25317
25420
  }
25318
- const crawlScript = join5(crawlerDir, "crawl.mjs");
25421
+ const crawlScript = join6(crawlerDir, "crawl.mjs");
25319
25422
  const args = input.sourceId ? `--source ${input.sourceId}` : "";
25320
25423
  try {
25321
25424
  execSync(`node "${crawlScript}" ${args}`, {
@@ -25327,7 +25430,7 @@ async function sourceCrawl(input) {
25327
25430
  let saved = 0;
25328
25431
  let queued = 0;
25329
25432
  for (const target of targets) {
25330
- const sourceDir = join5(config2.workflowDir, "sources", "articles", target.id);
25433
+ const sourceDir = join6(config2.workflowDir, "sources", "articles", target.id);
25331
25434
  let files;
25332
25435
  try {
25333
25436
  files = readdirSync(sourceDir).filter((f) => f.endsWith(".md"));
@@ -25335,7 +25438,7 @@ async function sourceCrawl(input) {
25335
25438
  continue;
25336
25439
  }
25337
25440
  for (const f of files) {
25338
- const filePath = join5(sourceDir, f);
25441
+ const filePath = join6(sourceDir, f);
25339
25442
  try {
25340
25443
  const raw = await readFile4(filePath, "utf-8");
25341
25444
  const { data, content } = (0, import_gray_matter2.default)(raw);
@@ -25396,8 +25499,8 @@ async function sourceCrawl(input) {
25396
25499
  }
25397
25500
 
25398
25501
  // src/tools/subscribe.ts
25399
- import { readFile as readFile5, writeFile as writeFile3, mkdir as mkdir5 } from "fs/promises";
25400
- import { join as join6 } from "path";
25502
+ import { readFile as readFile5, writeFile as writeFile4, mkdir as mkdir6 } from "fs/promises";
25503
+ import { join as join7 } from "path";
25401
25504
  var sourceSubscribeSchema = external_exports.object({
25402
25505
  action: external_exports.enum(["add", "remove", "list"]).describe("\u64CD\u4F5C\u7C7B\u578B"),
25403
25506
  id: external_exports.string().optional().describe("\u8BA2\u9605\u6E90 ID"),
@@ -25426,9 +25529,9 @@ async function readCrawlerConfig(configPath) {
25426
25529
  }
25427
25530
  async function sourceSubscribe(input) {
25428
25531
  const config2 = await getConfig();
25429
- const crawlerDir = join6(config2.workflowDir || process.cwd(), "tools", "crawler");
25430
- await mkdir5(crawlerDir, { recursive: true });
25431
- const configPath = join6(crawlerDir, "config.json");
25532
+ const crawlerDir = join7(config2.workflowDir || process.cwd(), "tools", "crawler");
25533
+ await mkdir6(crawlerDir, { recursive: true });
25534
+ const configPath = join7(crawlerDir, "config.json");
25432
25535
  const crawlerConfig = await readCrawlerConfig(configPath);
25433
25536
  if (input.action === "list") {
25434
25537
  return {
@@ -25460,7 +25563,7 @@ async function sourceSubscribe(input) {
25460
25563
  if (input.crawlConfig.maxArticles) newSource.maxArticles = input.crawlConfig.maxArticles;
25461
25564
  }
25462
25565
  crawlerConfig.sources.push(newSource);
25463
- await writeFile3(configPath, JSON.stringify(crawlerConfig, null, 2));
25566
+ await writeFile4(configPath, JSON.stringify(crawlerConfig, null, 2));
25464
25567
  fireAndForgetPush({
25465
25568
  crawlerSources: crawlerConfig.sources.map((s) => ({
25466
25569
  id: s.id,
@@ -25478,7 +25581,7 @@ async function sourceSubscribe(input) {
25478
25581
  return { success: false, message: "\u5220\u9664\u8BA2\u9605\u6E90\u9700\u8981 id \u53C2\u6570" };
25479
25582
  }
25480
25583
  crawlerConfig.sources = crawlerConfig.sources.filter((s) => s.id !== input.id);
25481
- await writeFile3(configPath, JSON.stringify(crawlerConfig, null, 2));
25584
+ await writeFile4(configPath, JSON.stringify(crawlerConfig, null, 2));
25482
25585
  fireAndForgetPush({
25483
25586
  crawlerSources: crawlerConfig.sources.map((s) => ({
25484
25587
  id: s.id,
@@ -25497,7 +25600,7 @@ async function sourceSubscribe(input) {
25497
25600
  // src/index.ts
25498
25601
  var server = new McpServer({
25499
25602
  name: "ClaudeInk",
25500
- version: "2.0.2"
25603
+ version: "2.2.0"
25501
25604
  });
25502
25605
  server.tool("ink.init", "\u521D\u59CB\u5316\u77E5\u8BC6\u5E93\uFF08\u521B\u5EFA\u76EE\u5F55 + \u6FC0\u6D3B License + \u540C\u6B65\u4E91\u7AEF\uFF09", workflowInitSchema.shape, async (input) => {
25503
25606
  const result = await workflowInit(input);
@@ -25507,23 +25610,23 @@ server.tool("ink.sync", "\u4ECE\u4E91\u7AEF\u540C\u6B65\u77E5\u8BC6\u5143\u6570\
25507
25610
  const result = await sync(input);
25508
25611
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
25509
25612
  });
25510
- server.tool("ink.save", "\u4FDD\u5B58\u6216\u66F4\u65B0\u77E5\u8BC6\u7D20\u6750\uFF08\u6709 id \u66F4\u65B0\uFF0C\u65E0 id \u65B0\u5EFA\uFF09", inkSaveSchema.shape, async (input) => {
25613
+ server.tool("ink.save", "\u6536\u96C6\u7D20\u6750\u5230\u77E5\u8BC6\u5E93\uFF08\u539F\u59CB\u5185\u5BB9\u3001\u7F51\u9875\u6458\u5F55\u3001\u5BF9\u8BDD\u7247\u6BB5\u7B49\uFF09\u3002\u5B58\u5165\u672C\u5730 knowledge/ \u76EE\u5F55\uFF0C\u5143\u6570\u636E\u540C\u6B65\u5230\u4E91\u7AEF source_meta\u3002", inkSaveSchema.shape, async (input) => {
25511
25614
  const result = await inkSave(input);
25512
25615
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
25513
25616
  });
25514
- server.tool("ink.delete", "\u5220\u9664\u77E5\u8BC6\u7247\u6BB5\uFF08\u9700\u786E\u8BA4\uFF09", inkDeleteSchema.shape, async (input) => {
25515
- const result = await inkDelete(input);
25617
+ server.tool("ink.write", "\u4FDD\u5B58\u8349\u7A3F/\u6587\u7AE0\uFF08AI \u521B\u4F5C\u7684\u5B8C\u6574\u6587\u7AE0\uFF09\u3002\u5B58\u5165\u672C\u5730 drafts/ \u76EE\u5F55\uFF0C\u5168\u6587\u4E0A\u4F20\u5230\u4E91\u7AEF blog_posts\u3002", inkWriteSchema.shape, async (input) => {
25618
+ const result = await inkWrite(input);
25516
25619
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
25517
25620
  });
25518
- server.tool("ink.write", "\u5199\u6587\u7AE0\u4FDD\u5B58\u4E3A\u8349\u7A3F\u3002Claude \u5199\u5B8C\u535A\u5BA2\u6587\u7AE0\u540E\u81EA\u52A8\u8C03\u7528\uFF0C\u63D0\u4F9B\u6807\u9898\u3001Markdown \u5185\u5BB9\u3001\u5C01\u9762\u56FE\u548C\u6807\u7B7E\u3002", inkWriteSchema.shape, async (input) => {
25519
- const result = await inkWrite(input);
25621
+ server.tool("ink.delete", "\u5220\u9664\u6761\u76EE\uFF08\u672C\u5730 + \u4E91\u7AEF\uFF09", inkDeleteSchema.shape, async (input) => {
25622
+ const result = await inkDelete(input);
25520
25623
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
25521
25624
  });
25522
25625
  server.tool("ink.tags", "\u6807\u7B7E\u7BA1\u7406\uFF08\u5217\u51FA / \u91CD\u547D\u540D / \u5408\u5E76\u6807\u7B7E\uFF09", inkTagsSchema.shape, async (input) => {
25523
25626
  const result = await inkTags(input);
25524
25627
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
25525
25628
  });
25526
- server.tool("ink.crawl", "\u89E6\u53D1\u722C\u866B\u6293\u53D6\uFF08\u5185\u5BB9\u81EA\u52A8\u5B58\u5165\u77E5\u8BC6\u5E93\uFF09", sourceCrawlSchema.shape, async (input) => {
25629
+ server.tool("ink.crawl", "\u89E6\u53D1\u722C\u866B\u6293\u53D6\uFF08\u7D20\u6750\u5B58\u5165\u672C\u5730\u77E5\u8BC6\u5E93\uFF09", sourceCrawlSchema.shape, async (input) => {
25527
25630
  const result = await sourceCrawl(input);
25528
25631
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
25529
25632
  });
@@ -25531,6 +25634,9 @@ server.tool("ink.subscribe", "\u7BA1\u7406\u8BA2\u9605\u6E90\uFF08\u6DFB\u52A0/\
25531
25634
  const result = await sourceSubscribe(input);
25532
25635
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
25533
25636
  });
25637
+ process.on("unhandledRejection", (err) => {
25638
+ console.error(`[ClaudeInk] Unhandled rejection: ${err}`);
25639
+ });
25534
25640
  async function main() {
25535
25641
  try {
25536
25642
  const state = await readState();
@@ -25542,11 +25648,8 @@ async function main() {
25542
25648
  }
25543
25649
  const transport = new StdioServerTransport();
25544
25650
  await server.connect(transport);
25545
- await log("INFO", "server", "started (8 tools)");
25651
+ await log("INFO", "server", "started (7 tools)");
25546
25652
  }
25547
- process.on("unhandledRejection", (err) => {
25548
- console.error(`[ClaudeInk] Unhandled rejection: ${err}`);
25549
- });
25550
25653
  main().catch(async (err) => {
25551
25654
  await log("ERROR", "server", `fatal: ${err.message || err}`);
25552
25655
  process.exit(1);