@claudeink/mcp-server 2.1.0 → 2.2.1

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 +462 -327
  2. package/dist/index.js +417 -284
  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 (state.credentials?.token) {
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,21 +24820,311 @@ 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
+
24974
25026
  // src/tools/ink.ts
24975
- var inkWriteSchema = external_exports.object({
24976
- id: external_exports.string().optional().describe("\u672C\u5730 ID\uFF08\u63D0\u4F9B\u5219\u66F4\u65B0\u5DF2\u6709\u6761\u76EE\uFF0C\u4E0D\u63D0\u4F9B\u5219\u65B0\u5EFA\uFF09"),
24977
- title: external_exports.string().describe("\u6807\u9898"),
25027
+ import { readFile as readFile3 } from "fs/promises";
25028
+ import { extname } from "path";
25029
+
25030
+ // src/lib/push.ts
25031
+ function fireAndForgetPush(payload) {
25032
+ doPush(payload).catch(() => {
25033
+ });
25034
+ }
25035
+ async function doPush(partial2) {
25036
+ const creds = await getCredentials();
25037
+ if (!creds?.token) return;
25038
+ const config2 = await getConfig();
25039
+ const payload = {
25040
+ knowledge: partial2.knowledge || [],
25041
+ crawlerSources: partial2.crawlerSources || []
25042
+ };
25043
+ await log("DEBUG", "push", `payload: ${partial2.knowledge?.length || 0} knowledge, ${partial2.crawlerSources?.length || 0} sources`);
25044
+ try {
25045
+ await fetch(`${config2.apiBaseUrl}/api/sync/batch`, {
25046
+ method: "POST",
25047
+ headers: {
25048
+ "Content-Type": "application/json",
25049
+ Authorization: `Bearer ${creds.token}`
25050
+ },
25051
+ body: JSON.stringify(payload)
25052
+ });
25053
+ } catch (err) {
25054
+ await log("WARN", "push", `failed: ${err instanceof Error ? err.message : err}`);
25055
+ }
25056
+ }
25057
+
25058
+ // src/tools/ink.ts
25059
+ var inkSaveSchema = external_exports.object({
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"),
24978
25062
  content: external_exports.string().describe("Markdown \u5168\u6587"),
24979
25063
  tags: external_exports.array(external_exports.string()).optional().default([]).describe("\u6807\u7B7E"),
24980
25064
  category: external_exports.enum(["insight", "decision", "analysis", "idea", "reference", "action"]).optional().default("reference").describe("\u5206\u7C7B"),
24981
25065
  source_type: external_exports.enum(["conversation", "url", "manual"]).optional().default("conversation").describe("\u6765\u6E90\u7C7B\u578B"),
24982
- url: external_exports.string().optional().describe("\u6765\u6E90 URL"),
25066
+ url: external_exports.string().optional().describe("\u6765\u6E90 URL")
25067
+ });
25068
+ async function inkSave(input) {
25069
+ try {
25070
+ let localId;
25071
+ if (input.id) {
25072
+ const result = await updateKnowledgeFile(input.id, {
25073
+ title: input.title,
25074
+ content: input.content,
25075
+ tags: input.tags,
25076
+ category: input.category
25077
+ }, "knowledge");
25078
+ if (!result) {
25079
+ return { success: false, message: `\u672A\u627E\u5230 ID \u4E3A ${input.id} \u7684\u7D20\u6750` };
25080
+ }
25081
+ localId = input.id;
25082
+ } else {
25083
+ const { id } = await saveKnowledge({
25084
+ content: input.content,
25085
+ title: input.title,
25086
+ tags: input.tags,
25087
+ category: input.category,
25088
+ source_type: input.source_type,
25089
+ url: input.url,
25090
+ store: "knowledge"
25091
+ });
25092
+ localId = id;
25093
+ }
25094
+ const creds = await getCredentials();
25095
+ if (creds?.token) {
25096
+ const now = (/* @__PURE__ */ new Date()).toISOString();
25097
+ fireAndForgetPush({
25098
+ knowledge: [{
25099
+ id: localId,
25100
+ title: input.title,
25101
+ tags: input.tags || [],
25102
+ category: input.category || "reference",
25103
+ source_type: input.source_type || "conversation",
25104
+ url: input.url,
25105
+ created_at: now,
25106
+ updated_at: now
25107
+ }],
25108
+ crawlerSources: []
25109
+ });
25110
+ }
25111
+ await log("INFO", "ink.save", `saved source id=${localId} title="${input.title}"`);
25112
+ return {
25113
+ success: true,
25114
+ 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"),
25115
+ data: { id: localId }
25116
+ };
25117
+ } catch (err) {
25118
+ await log("ERROR", "ink.save", `failed: ${err.message}`);
25119
+ return { success: false, message: `\u4FDD\u5B58\u7D20\u6750\u5931\u8D25: ${err.message}` };
25120
+ }
25121
+ }
25122
+ var inkWriteSchema = external_exports.object({
25123
+ 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"),
25124
+ title: external_exports.string().describe("\u6587\u7AE0\u6807\u9898"),
25125
+ content: external_exports.string().describe("Markdown \u5168\u6587"),
25126
+ tags: external_exports.array(external_exports.string()).optional().default([]).describe("\u6807\u7B7E"),
25127
+ category: external_exports.enum(["insight", "decision", "analysis", "idea", "reference", "action"]).optional().default("reference").describe("\u5206\u7C7B"),
24983
25128
  cover: external_exports.string().optional().describe("\u5C01\u9762\u56FE\uFF08\u672C\u5730\u8DEF\u5F84\u6216 URL\uFF0C\u81EA\u52A8\u4E0A\u4F20\uFF09"),
24984
25129
  excerpt: external_exports.string().optional().describe("\u6458\u8981\uFF08\u4E0D\u586B\u81EA\u52A8\u622A\u53D6\u524D 200 \u5B57\uFF09"),
24985
25130
  source_ids: external_exports.array(external_exports.string()).optional().describe("\u5173\u8054\u7684\u7D20\u6750 ID\uFF08\u6EAF\u6E90\uFF09")
@@ -24993,9 +25138,9 @@ async function inkWrite(input) {
24993
25138
  content: input.content,
24994
25139
  tags: input.tags,
24995
25140
  category: input.category
24996
- });
25141
+ }, "drafts");
24997
25142
  if (!result) {
24998
- return { success: false, message: `\u672A\u627E\u5230 ID \u4E3A ${input.id} \u7684\u672C\u5730\u6761\u76EE` };
25143
+ return { success: false, message: `\u672A\u627E\u5230 ID \u4E3A ${input.id} \u7684\u8349\u7A3F` };
24999
25144
  }
25000
25145
  localId = input.id;
25001
25146
  } else {
@@ -25004,17 +25149,17 @@ async function inkWrite(input) {
25004
25149
  title: input.title,
25005
25150
  tags: input.tags,
25006
25151
  category: input.category,
25007
- source_type: input.source_type,
25008
- url: input.url
25152
+ source_type: "conversation",
25153
+ store: "drafts"
25009
25154
  });
25010
25155
  localId = id;
25011
25156
  }
25012
25157
  const creds = await getCredentials();
25013
25158
  if (!creds?.token) {
25014
- await log("INFO", "ink.write", `saved locally id=${localId} title="${input.title}" (no cloud auth)`);
25159
+ await log("INFO", "ink.write", `saved draft locally id=${localId} title="${input.title}" (no cloud auth)`);
25015
25160
  return {
25016
25161
  success: true,
25017
- message: `\u5DF2\u4FDD\u5B58\u300C${input.title}\u300D\u5230\u672C\u5730\uFF08\u672A\u767B\u5F55\uFF0C\u8DF3\u8FC7\u4E91\u7AEF\u540C\u6B65\uFF09`,
25162
+ 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`,
25018
25163
  data: { id: localId }
25019
25164
  };
25020
25165
  }
@@ -25036,55 +25181,69 @@ async function inkWrite(input) {
25036
25181
  }
25037
25182
  const slug = `${Date.now().toString(36)}-${input.title.toLowerCase().replace(/[^a-z0-9\u4e00-\u9fff]+/g, "-").replace(/^-|-$/g, "").slice(0, 60)}`;
25038
25183
  const excerpt = input.excerpt || input.content.replace(/^#+\s.+$/gm, "").replace(/\n+/g, " ").trim().slice(0, 200);
25039
- const res = await fetch(`${config2.apiBaseUrl}/api/blog/manage`, {
25040
- method: "POST",
25041
- headers: {
25042
- "Content-Type": "application/json",
25043
- Authorization: `Bearer ${creds.token}`
25044
- },
25045
- body: JSON.stringify({
25046
- title: input.title,
25047
- slug,
25048
- content: input.content,
25049
- cover_url: coverUrl,
25050
- tags: input.tags,
25051
- category: input.category,
25052
- excerpt,
25053
- source_type: input.source_type,
25054
- url: input.url,
25055
- source_ids: input.source_ids,
25056
- local_id: localId
25057
- })
25058
- });
25059
- if (!res.ok) {
25060
- const err = await res.text();
25061
- await log("ERROR", "ink.write", `cloud save failed: ${err}`);
25184
+ try {
25185
+ const controller = new AbortController();
25186
+ const timeout = setTimeout(() => controller.abort(), 1e4);
25187
+ const res = await fetch(`${config2.apiBaseUrl}/api/blog/manage`, {
25188
+ method: "POST",
25189
+ signal: controller.signal,
25190
+ headers: {
25191
+ "Content-Type": "application/json",
25192
+ Authorization: `Bearer ${creds.token}`
25193
+ },
25194
+ body: JSON.stringify({
25195
+ title: input.title,
25196
+ slug,
25197
+ content: input.content,
25198
+ cover_url: coverUrl,
25199
+ tags: input.tags,
25200
+ category: input.category,
25201
+ excerpt,
25202
+ source_type: "conversation",
25203
+ source_ids: input.source_ids,
25204
+ local_id: localId
25205
+ })
25206
+ });
25207
+ clearTimeout(timeout);
25208
+ if (!res.ok) {
25209
+ const err = await res.text();
25210
+ await log("ERROR", "ink.write", `cloud save failed: ${err}`);
25211
+ return {
25212
+ success: true,
25213
+ message: `\u5DF2\u4FDD\u5B58\u8349\u7A3F\u300C${input.title}\u300D\u5230\u672C\u5730\uFF0C\u4F46\u4E91\u7AEF\u540C\u6B65\u5931\u8D25: ${err}`,
25214
+ data: { id: localId }
25215
+ };
25216
+ }
25217
+ const data = await res.json();
25218
+ await log("INFO", "ink.write", `saved draft id=${localId} cloud_id=${data.id} title="${input.title}"`);
25062
25219
  return {
25063
25220
  success: true,
25064
- message: `\u5DF2\u4FDD\u5B58\u300C${input.title}\u300D\u5230\u672C\u5730\uFF0C\u4F46\u4E91\u7AEF\u540C\u6B65\u5931\u8D25: ${err}`,
25221
+ message: `\u5DF2\u4FDD\u5B58\u8349\u7A3F\u300C${input.title}\u300D\uFF08\u672C\u5730 + \u4E91\u7AEF\uFF09`,
25222
+ data: { id: localId, cloud_id: data.id, slug: data.slug, status: data.status }
25223
+ };
25224
+ } catch (cloudErr) {
25225
+ await log("WARN", "ink.write", `cloud upload failed (local saved): ${cloudErr.message}`);
25226
+ return {
25227
+ success: true,
25228
+ message: `\u5DF2\u4FDD\u5B58\u8349\u7A3F\u300C${input.title}\u300D\u5230\u672C\u5730\uFF0C\u4F46\u4E91\u7AEF\u540C\u6B65\u5931\u8D25: ${cloudErr.message}`,
25065
25229
  data: { id: localId }
25066
25230
  };
25067
25231
  }
25068
- const data = await res.json();
25069
- await log("INFO", "ink.write", `saved id=${localId} cloud_id=${data.id} title="${input.title}"`);
25070
- return {
25071
- success: true,
25072
- message: `\u5DF2\u4FDD\u5B58\u300C${input.title}\u300D\uFF08\u672C\u5730 + \u4E91\u7AEF\uFF09`,
25073
- data: { id: localId, cloud_id: data.id, slug: data.slug, status: data.status }
25074
- };
25075
25232
  } catch (err) {
25076
25233
  await log("ERROR", "ink.write", `failed: ${err.message}`);
25077
- return { success: false, message: `\u4FDD\u5B58\u5931\u8D25: ${err.message}` };
25234
+ return { success: false, message: `\u4FDD\u5B58\u8349\u7A3F\u5931\u8D25: ${err.message}` };
25078
25235
  }
25079
25236
  }
25080
25237
  var inkDeleteSchema = external_exports.object({
25081
25238
  id: external_exports.string().describe("\u6761\u76EE ID"),
25239
+ 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"),
25082
25240
  confirm: external_exports.boolean().optional().default(false).describe("\u786E\u8BA4\u5220\u9664\uFF08\u5FC5\u987B\u4E3A true \u624D\u4F1A\u6267\u884C\u5220\u9664\uFF09")
25083
25241
  });
25084
25242
  async function inkDelete(input) {
25085
25243
  try {
25244
+ const store = input.store;
25086
25245
  if (!input.confirm) {
25087
- const index = await readIndex();
25246
+ const index = await readIndex(store);
25088
25247
  const entry = index.entries[input.id];
25089
25248
  if (!entry) {
25090
25249
  return { success: false, message: `\u672A\u627E\u5230 ID \u4E3A ${input.id} \u7684\u6761\u76EE` };
@@ -25092,10 +25251,10 @@ async function inkDelete(input) {
25092
25251
  return {
25093
25252
  success: false,
25094
25253
  message: `\u786E\u8BA4\u5220\u9664\u300C${entry.title}\u300D\uFF1F\u8BF7\u5C06 confirm \u8BBE\u4E3A true \u4EE5\u786E\u8BA4\u5220\u9664\u3002`,
25095
- data: { id: entry.id, title: entry.title, category: entry.category, tags: entry.tags }
25254
+ data: { id: entry.id, title: entry.title, store, category: entry.category, tags: entry.tags }
25096
25255
  };
25097
25256
  }
25098
- const deleted = await deleteKnowledgeFile(input.id);
25257
+ const deleted = await deleteKnowledgeFile(input.id, store);
25099
25258
  if (!deleted) {
25100
25259
  return { success: false, message: `\u672A\u627E\u5230 ID \u4E3A ${input.id} \u7684\u6761\u76EE` };
25101
25260
  }
@@ -25114,7 +25273,7 @@ async function inkDelete(input) {
25114
25273
  }
25115
25274
  } catch {
25116
25275
  }
25117
- await log("INFO", "ink.delete", `deleted id=${input.id}`);
25276
+ await log("INFO", "ink.delete", `deleted id=${input.id} store=${store}`);
25118
25277
  return { success: true, message: `\u5DF2\u5220\u9664 ${input.id}`, data: { id: input.id } };
25119
25278
  } catch (err) {
25120
25279
  await log("ERROR", "ink.delete", `failed: ${err.message}`);
@@ -25129,7 +25288,7 @@ var inkTagsSchema = external_exports.object({
25129
25288
  });
25130
25289
  async function inkTags(input) {
25131
25290
  try {
25132
- const index = await readIndex();
25291
+ const index = await readIndex("knowledge");
25133
25292
  switch (input.action) {
25134
25293
  case "list": {
25135
25294
  const tags = getAllTags(index);
@@ -25143,7 +25302,7 @@ async function inkTags(input) {
25143
25302
  if (!input.tag || !input.new_tag) {
25144
25303
  return { success: false, message: "rename \u9700\u8981 tag\uFF08\u539F\u6807\u7B7E\uFF09\u548C new_tag\uFF08\u65B0\u6807\u7B7E\u540D\uFF09" };
25145
25304
  }
25146
- const count = await renameTag(input.tag, input.new_tag);
25305
+ const count = await renameTag(input.tag, input.new_tag, "knowledge");
25147
25306
  if (count === 0) {
25148
25307
  return { success: false, message: `\u672A\u627E\u5230\u4F7F\u7528\u6807\u7B7E\u300C${input.tag}\u300D\u7684\u6761\u76EE` };
25149
25308
  }
@@ -25158,7 +25317,7 @@ async function inkTags(input) {
25158
25317
  if (!input.source_tags || input.source_tags.length === 0 || !input.new_tag) {
25159
25318
  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" };
25160
25319
  }
25161
- const count = await mergeTags(input.source_tags, input.new_tag);
25320
+ const count = await mergeTags(input.source_tags, input.new_tag, "knowledge");
25162
25321
  if (count === 0) {
25163
25322
  return { success: false, message: "\u672A\u627E\u5230\u4F7F\u7528\u8FD9\u4E9B\u6807\u7B7E\u7684\u6761\u76EE" };
25164
25323
  }
@@ -25234,46 +25393,16 @@ async function uploadImageFromUrl(imageUrl, type, token, apiBase) {
25234
25393
  // src/tools/source.ts
25235
25394
  var import_gray_matter2 = __toESM(require_gray_matter(), 1);
25236
25395
  import { readFile as readFile4 } from "fs/promises";
25237
- import { join as join5 } from "path";
25396
+ import { join as join6 } from "path";
25238
25397
  import { readdirSync } from "fs";
25239
25398
  import { execSync } from "child_process";
25240
-
25241
- // src/lib/push.ts
25242
- function fireAndForgetPush(payload) {
25243
- doPush(payload).catch(() => {
25244
- });
25245
- }
25246
- async function doPush(partial2) {
25247
- const creds = await getCredentials();
25248
- if (!creds?.token) return;
25249
- const config2 = await getConfig();
25250
- const payload = {
25251
- knowledge: partial2.knowledge || [],
25252
- crawlerSources: partial2.crawlerSources || []
25253
- };
25254
- await log("DEBUG", "push", `payload: ${partial2.knowledge?.length || 0} knowledge, ${partial2.crawlerSources?.length || 0} sources`);
25255
- try {
25256
- await fetch(`${config2.apiBaseUrl}/api/sync/batch`, {
25257
- method: "POST",
25258
- headers: {
25259
- "Content-Type": "application/json",
25260
- Authorization: `Bearer ${creds.token}`
25261
- },
25262
- body: JSON.stringify(payload)
25263
- });
25264
- } catch (err) {
25265
- await log("WARN", "push", `failed: ${err instanceof Error ? err.message : err}`);
25266
- }
25267
- }
25268
-
25269
- // src/tools/source.ts
25270
25399
  var sourceCrawlSchema = external_exports.object({
25271
25400
  sourceId: external_exports.string().optional().describe("\u6307\u5B9A\u722C\u866B\u6E90\u540D\u79F0\uFF0C\u4E0D\u4F20\u5219\u5168\u91CF")
25272
25401
  });
25273
25402
  async function sourceCrawl(input) {
25274
25403
  const config2 = await getConfig();
25275
- const crawlerDir = join5(config2.workflowDir || process.cwd(), "tools", "crawler");
25276
- const configPath = join5(crawlerDir, "config.json");
25404
+ const crawlerDir = join6(config2.workflowDir || process.cwd(), "tools", "crawler");
25405
+ const configPath = join6(crawlerDir, "config.json");
25277
25406
  let crawlerConfig;
25278
25407
  try {
25279
25408
  crawlerConfig = JSON.parse(await readFile4(configPath, "utf-8"));
@@ -25290,7 +25419,7 @@ async function sourceCrawl(input) {
25290
25419
  message: input.sourceId ? `\u672A\u627E\u5230\u722C\u866B\u6E90: ${input.sourceId}` : "\u6CA1\u6709\u5DF2\u542F\u7528\u7684\u722C\u866B\u6E90"
25291
25420
  };
25292
25421
  }
25293
- const crawlScript = join5(crawlerDir, "crawl.mjs");
25422
+ const crawlScript = join6(crawlerDir, "crawl.mjs");
25294
25423
  const args = input.sourceId ? `--source ${input.sourceId}` : "";
25295
25424
  try {
25296
25425
  execSync(`node "${crawlScript}" ${args}`, {
@@ -25302,7 +25431,7 @@ async function sourceCrawl(input) {
25302
25431
  let saved = 0;
25303
25432
  let queued = 0;
25304
25433
  for (const target of targets) {
25305
- const sourceDir = join5(config2.workflowDir, "sources", "articles", target.id);
25434
+ const sourceDir = join6(config2.workflowDir, "sources", "articles", target.id);
25306
25435
  let files;
25307
25436
  try {
25308
25437
  files = readdirSync(sourceDir).filter((f) => f.endsWith(".md"));
@@ -25310,7 +25439,7 @@ async function sourceCrawl(input) {
25310
25439
  continue;
25311
25440
  }
25312
25441
  for (const f of files) {
25313
- const filePath = join5(sourceDir, f);
25442
+ const filePath = join6(sourceDir, f);
25314
25443
  try {
25315
25444
  const raw = await readFile4(filePath, "utf-8");
25316
25445
  const { data, content } = (0, import_gray_matter2.default)(raw);
@@ -25371,8 +25500,8 @@ async function sourceCrawl(input) {
25371
25500
  }
25372
25501
 
25373
25502
  // src/tools/subscribe.ts
25374
- import { readFile as readFile5, writeFile as writeFile3, mkdir as mkdir5 } from "fs/promises";
25375
- import { join as join6 } from "path";
25503
+ import { readFile as readFile5, writeFile as writeFile4, mkdir as mkdir6 } from "fs/promises";
25504
+ import { join as join7 } from "path";
25376
25505
  var sourceSubscribeSchema = external_exports.object({
25377
25506
  action: external_exports.enum(["add", "remove", "list"]).describe("\u64CD\u4F5C\u7C7B\u578B"),
25378
25507
  id: external_exports.string().optional().describe("\u8BA2\u9605\u6E90 ID"),
@@ -25401,9 +25530,9 @@ async function readCrawlerConfig(configPath) {
25401
25530
  }
25402
25531
  async function sourceSubscribe(input) {
25403
25532
  const config2 = await getConfig();
25404
- const crawlerDir = join6(config2.workflowDir || process.cwd(), "tools", "crawler");
25405
- await mkdir5(crawlerDir, { recursive: true });
25406
- const configPath = join6(crawlerDir, "config.json");
25533
+ const crawlerDir = join7(config2.workflowDir || process.cwd(), "tools", "crawler");
25534
+ await mkdir6(crawlerDir, { recursive: true });
25535
+ const configPath = join7(crawlerDir, "config.json");
25407
25536
  const crawlerConfig = await readCrawlerConfig(configPath);
25408
25537
  if (input.action === "list") {
25409
25538
  return {
@@ -25435,7 +25564,7 @@ async function sourceSubscribe(input) {
25435
25564
  if (input.crawlConfig.maxArticles) newSource.maxArticles = input.crawlConfig.maxArticles;
25436
25565
  }
25437
25566
  crawlerConfig.sources.push(newSource);
25438
- await writeFile3(configPath, JSON.stringify(crawlerConfig, null, 2));
25567
+ await writeFile4(configPath, JSON.stringify(crawlerConfig, null, 2));
25439
25568
  fireAndForgetPush({
25440
25569
  crawlerSources: crawlerConfig.sources.map((s) => ({
25441
25570
  id: s.id,
@@ -25453,7 +25582,7 @@ async function sourceSubscribe(input) {
25453
25582
  return { success: false, message: "\u5220\u9664\u8BA2\u9605\u6E90\u9700\u8981 id \u53C2\u6570" };
25454
25583
  }
25455
25584
  crawlerConfig.sources = crawlerConfig.sources.filter((s) => s.id !== input.id);
25456
- await writeFile3(configPath, JSON.stringify(crawlerConfig, null, 2));
25585
+ await writeFile4(configPath, JSON.stringify(crawlerConfig, null, 2));
25457
25586
  fireAndForgetPush({
25458
25587
  crawlerSources: crawlerConfig.sources.map((s) => ({
25459
25588
  id: s.id,
@@ -25472,7 +25601,7 @@ async function sourceSubscribe(input) {
25472
25601
  // src/index.ts
25473
25602
  var server = new McpServer({
25474
25603
  name: "ClaudeInk",
25475
- version: "2.1.0"
25604
+ version: "2.2.0"
25476
25605
  });
25477
25606
  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) => {
25478
25607
  const result = await workflowInit(input);
@@ -25482,7 +25611,11 @@ server.tool("ink.sync", "\u4ECE\u4E91\u7AEF\u540C\u6B65\u77E5\u8BC6\u5143\u6570\
25482
25611
  const result = await sync(input);
25483
25612
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
25484
25613
  });
25485
- server.tool("ink.write", "\u4FDD\u5B58\u5185\u5BB9\uFF08\u672C\u5730 .md + \u4E0A\u4F20\u4E91\u7AEF\uFF09\u3002Claude \u751F\u6210\u5185\u5BB9\u540E\u8C03\u7528\u6B64\u5DE5\u5177\u4FDD\u5B58\u3002", inkWriteSchema.shape, async (input) => {
25614
+ 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) => {
25615
+ const result = await inkSave(input);
25616
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
25617
+ });
25618
+ 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) => {
25486
25619
  const result = await inkWrite(input);
25487
25620
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
25488
25621
  });