@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/cli.js CHANGED
@@ -28848,179 +28848,6 @@ var init_state = __esm({
28848
28848
  }
28849
28849
  });
28850
28850
 
28851
- // src/lib/logger.ts
28852
- import { appendFile, mkdir as mkdir2 } from "fs/promises";
28853
- import { join as join2 } from "path";
28854
- function setLogLevel(level) {
28855
- currentLevel = level;
28856
- }
28857
- async function log(level, scope, message) {
28858
- if (LEVELS[level] > LEVELS[currentLevel]) return;
28859
- const dir = join2(getClaudeinkDir(), "logs");
28860
- await mkdir2(dir, { recursive: true });
28861
- const date3 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
28862
- const line = `${(/* @__PURE__ */ new Date()).toISOString()} [${level}] ${scope} | ${message}
28863
- `;
28864
- await appendFile(join2(dir, `mcp-${date3}.log`), line);
28865
- console.error(`[ClaudeInk] ${line.trim()}`);
28866
- }
28867
- var currentLevel, LEVELS;
28868
- var init_logger = __esm({
28869
- "src/lib/logger.ts"() {
28870
- "use strict";
28871
- init_state();
28872
- currentLevel = "INFO";
28873
- LEVELS = { ERROR: 0, WARN: 1, INFO: 2, DEBUG: 3 };
28874
- }
28875
- });
28876
-
28877
- // src/tools/sync.ts
28878
- async function sync2(input) {
28879
- if (input.workDir) setWorkDir(input.workDir);
28880
- const creds = await getCredentials();
28881
- if (!creds?.token) {
28882
- return { success: false, message: "\u672A\u6FC0\u6D3B\uFF0C\u8BF7\u5148\u4F7F\u7528 workflow.init \u6FC0\u6D3B License" };
28883
- }
28884
- return doPull(creds.token);
28885
- }
28886
- async function doPull(token) {
28887
- const config2 = await getConfig();
28888
- try {
28889
- const res = await fetch(`${config2.apiBaseUrl}/api/sync/pull`, {
28890
- headers: { Authorization: `Bearer ${token}` }
28891
- });
28892
- if (!res.ok) {
28893
- if (res.status === 404) {
28894
- return { success: true, message: "\u4E91\u7AEF\u65E0\u6570\u636E" };
28895
- }
28896
- await log("ERROR", "ink.sync", `pull failed: HTTP ${res.status}`);
28897
- return { success: false, message: `\u62C9\u53D6\u5931\u8D25: HTTP ${res.status}` };
28898
- }
28899
- await updateLastSyncAt();
28900
- await log("INFO", "ink.sync", "pull completed");
28901
- return {
28902
- success: true,
28903
- message: "\u2705 \u540C\u6B65\u5B8C\u6210"
28904
- };
28905
- } catch (err) {
28906
- return { success: false, message: `\u540C\u6B65\u5931\u8D25: ${err instanceof Error ? err.message : err}` };
28907
- }
28908
- }
28909
- async function syncPull(input) {
28910
- if (input.workDir) setWorkDir(input.workDir);
28911
- const creds = await getCredentials();
28912
- if (!creds?.token) {
28913
- return { success: false, message: "\u672A\u6FC0\u6D3B" };
28914
- }
28915
- return doPull(creds.token);
28916
- }
28917
- var syncSchema;
28918
- var init_sync = __esm({
28919
- "src/tools/sync.ts"() {
28920
- "use strict";
28921
- init_zod();
28922
- init_state();
28923
- init_logger();
28924
- syncSchema = external_exports.object({
28925
- workDir: external_exports.string().optional().describe("\u5DE5\u4F5C\u76EE\u5F55\uFF08\u9ED8\u8BA4\u4F7F\u7528\u914D\u7F6E\u4E2D\u7684 workflowDir\uFF09")
28926
- });
28927
- }
28928
- });
28929
-
28930
- // src/tools/workflow.ts
28931
- import { mkdir as mkdir3, access, unlink } from "fs/promises";
28932
- import { join as join3 } from "path";
28933
- async function workflowInit(input) {
28934
- const cwd = input.workDir;
28935
- const results = [];
28936
- try {
28937
- setWorkDir(cwd);
28938
- const claudeinkDir = join3(cwd, ".claudeink");
28939
- await mkdir3(join3(claudeinkDir, "knowledge"), { recursive: true });
28940
- results.push("\u2705 \u77E5\u8BC6\u5E93\u76EE\u5F55\u5DF2\u521B\u5EFA");
28941
- const state = await readState();
28942
- state.config.workflowDir = cwd;
28943
- const oldCredsPath = join3(claudeinkDir, "credentials.json");
28944
- try {
28945
- await access(oldCredsPath);
28946
- await unlink(oldCredsPath);
28947
- results.push("\u{1F9F9} \u5DF2\u6E05\u7406\u65E7\u7248 credentials.json");
28948
- } catch {
28949
- }
28950
- let activated = false;
28951
- if (input.licenseKey) {
28952
- try {
28953
- const res = await fetch(`${DEFAULT_API_BASE_URL}/api/auth/activate`, {
28954
- method: "POST",
28955
- headers: { "Content-Type": "application/json" },
28956
- body: JSON.stringify({ key: input.licenseKey })
28957
- });
28958
- const data = await res.json();
28959
- if (data.userId) {
28960
- state.credentials = {
28961
- licenseKey: input.licenseKey,
28962
- token: data.token,
28963
- userId: data.userId,
28964
- plan: data.plan,
28965
- expiresAt: data.expiresAt
28966
- };
28967
- results.push(`\u2705 License \u6FC0\u6D3B\u6210\u529F\uFF08\u5957\u9910: ${data.plan}\uFF09`);
28968
- activated = true;
28969
- } else {
28970
- results.push(`\u26A0\uFE0F License \u6FC0\u6D3B\u5931\u8D25: ${JSON.stringify(data)}`);
28971
- }
28972
- } catch (err) {
28973
- results.push(`\u26A0\uFE0F \u6FC0\u6D3B\u7F51\u7EDC\u9519\u8BEF: ${err instanceof Error ? err.message : err}`);
28974
- }
28975
- }
28976
- await writeState(state);
28977
- results.push("\u2705 state.json");
28978
- if (activated) {
28979
- try {
28980
- const pullResult = await syncPull({ workDir: cwd });
28981
- if (pullResult.success) {
28982
- results.push("\u2705 \u5DF2\u4ECE\u4E91\u7AEF\u540C\u6B65\u6570\u636E");
28983
- } else {
28984
- results.push("\u2139\uFE0F \u4E91\u7AEF\u65E0\u5DF2\u6709\u6570\u636E");
28985
- }
28986
- } catch {
28987
- results.push("\u2139\uFE0F \u4E91\u7AEF\u540C\u6B65\u8DF3\u8FC7");
28988
- }
28989
- }
28990
- return {
28991
- success: true,
28992
- message: [
28993
- "\u{1F389} ClaudeInk \u77E5\u8BC6\u5E93\u521D\u59CB\u5316\u5B8C\u6210\uFF01",
28994
- "",
28995
- ...results,
28996
- "",
28997
- "\u{1F3AF} \u4E0B\u4E00\u6B65\uFF1A",
28998
- "1. \u5728\u5BF9\u8BDD\u4E2D\u8BF4\u300C\u5E2E\u6211\u5B58\u4E00\u4E0B\u300D\u4FDD\u5B58\u6709\u4EF7\u503C\u5185\u5BB9",
28999
- "2. \u7528\u300C\u627E\u4E00\u4E0B\u5173\u4E8E XX \u7684\u300D\u68C0\u7D22\u77E5\u8BC6\u5E93"
29000
- ].join("\n")
29001
- };
29002
- } catch (err) {
29003
- return {
29004
- success: false,
29005
- message: `\u521D\u59CB\u5316\u5931\u8D25: ${err instanceof Error ? err.message : err}`
29006
- };
29007
- }
29008
- }
29009
- var DEFAULT_API_BASE_URL, workflowInitSchema;
29010
- var init_workflow = __esm({
29011
- "src/tools/workflow.ts"() {
29012
- "use strict";
29013
- init_zod();
29014
- init_state();
29015
- init_sync();
29016
- DEFAULT_API_BASE_URL = "https://app.claudeink.com";
29017
- workflowInitSchema = external_exports.object({
29018
- workDir: external_exports.string().describe("\u5DE5\u4F5C\u6D41\u521D\u59CB\u5316\u76EE\u6807\u76EE\u5F55\uFF08\u7EDD\u5BF9\u8DEF\u5F84\uFF09"),
29019
- licenseKey: external_exports.string().optional().describe("License Key\uFF08\u53EF\u9009\uFF0C\u4F20\u5165\u5219\u81EA\u52A8\u6FC0\u6D3B\uFF09")
29020
- });
29021
- }
29022
- });
29023
-
29024
28851
  // ../node_modules/kind-of/index.js
29025
28852
  var require_kind_of = __commonJS({
29026
28853
  "../node_modules/kind-of/index.js"(exports2, module2) {
@@ -32506,8 +32333,8 @@ var require_gray_matter = __commonJS({
32506
32333
  });
32507
32334
 
32508
32335
  // src/lib/knowledge.ts
32509
- import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir4, unlink as unlink2 } from "fs/promises";
32510
- import { join as join4 } from "path";
32336
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, unlink } from "fs/promises";
32337
+ import { join as join2 } from "path";
32511
32338
  function withIndexLock(fn) {
32512
32339
  const prev = _lockPromise;
32513
32340
  let resolve;
@@ -32516,11 +32343,11 @@ function withIndexLock(fn) {
32516
32343
  });
32517
32344
  return prev.then(fn).finally(() => resolve());
32518
32345
  }
32519
- function getKnowledgeDir() {
32520
- return join4(getWorkDir(), ".claudeink", "knowledge");
32346
+ function getStoreDir(store = "knowledge") {
32347
+ return join2(getWorkDir(), ".claudeink", store);
32521
32348
  }
32522
- function getIndexPath() {
32523
- return join4(getWorkDir(), ".claudeink", "knowledge", "index.json");
32349
+ function getIndexPath(store = "knowledge") {
32350
+ return join2(getWorkDir(), ".claudeink", store, "index.json");
32524
32351
  }
32525
32352
  async function generateIdInternal(index) {
32526
32353
  const now = /* @__PURE__ */ new Date();
@@ -32542,28 +32369,28 @@ function toSlug(title) {
32542
32369
  function generatePreview(content, maxLen = 200) {
32543
32370
  return content.replace(/^#+\s.+$/gm, "").replace(/\n+/g, " ").trim().slice(0, maxLen);
32544
32371
  }
32545
- async function readIndex() {
32372
+ async function readIndex(store = "knowledge") {
32546
32373
  try {
32547
- const raw = await readFile2(getIndexPath(), "utf-8");
32374
+ const raw = await readFile2(getIndexPath(store), "utf-8");
32548
32375
  return JSON.parse(raw);
32549
32376
  } catch {
32550
32377
  return { entries: {}, updated_at: (/* @__PURE__ */ new Date()).toISOString() };
32551
32378
  }
32552
32379
  }
32553
- async function saveIndex(index) {
32380
+ async function saveIndex(index, store = "knowledge") {
32554
32381
  index.updated_at = (/* @__PURE__ */ new Date()).toISOString();
32555
- const dir = getKnowledgeDir();
32556
- await mkdir4(dir, { recursive: true });
32557
- await writeFile2(getIndexPath(), JSON.stringify(index, null, 2), "utf-8");
32382
+ const dir = getStoreDir(store);
32383
+ await mkdir2(dir, { recursive: true });
32384
+ await writeFile2(getIndexPath(store), JSON.stringify(index, null, 2), "utf-8");
32558
32385
  }
32559
- async function writeKnowledgeFile(meta, content) {
32386
+ async function writeKnowledgeFile(meta, content, store = "knowledge") {
32560
32387
  const now = /* @__PURE__ */ new Date();
32561
32388
  const monthDir = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}`;
32562
- const dir = join4(getKnowledgeDir(), monthDir);
32563
- await mkdir4(dir, { recursive: true });
32389
+ const dir = join2(getStoreDir(store), monthDir);
32390
+ await mkdir2(dir, { recursive: true });
32564
32391
  const slug = toSlug(meta.title) || meta.id;
32565
32392
  const filename = `${now.toISOString().slice(0, 10)}-${slug}.md`;
32566
- const filePath = join4(dir, filename);
32393
+ const filePath = join2(dir, filename);
32567
32394
  const frontmatter = {
32568
32395
  id: meta.id,
32569
32396
  title: meta.title,
@@ -32576,13 +32403,13 @@ async function writeKnowledgeFile(meta, content) {
32576
32403
  if (meta.url) frontmatter.url = meta.url;
32577
32404
  const output = import_gray_matter.default.stringify(content, frontmatter);
32578
32405
  await writeFile2(filePath, output, "utf-8");
32579
- return join4(monthDir, filename);
32406
+ return join2(monthDir, filename);
32580
32407
  }
32581
- async function readKnowledgeFile(id) {
32582
- const index = await readIndex();
32408
+ async function readKnowledgeFile(id, store = "knowledge") {
32409
+ const index = await readIndex(store);
32583
32410
  const entry = index.entries[id];
32584
32411
  if (!entry) return null;
32585
- const filePath = join4(getKnowledgeDir(), entry.file_path);
32412
+ const filePath = join2(getStoreDir(store), entry.file_path);
32586
32413
  try {
32587
32414
  const raw = await readFile2(filePath, "utf-8");
32588
32415
  const { data, content } = (0, import_gray_matter.default)(raw);
@@ -32596,8 +32423,9 @@ async function readKnowledgeFile(id) {
32596
32423
  }
32597
32424
  }
32598
32425
  async function saveKnowledge(params) {
32426
+ const store = params.store || "knowledge";
32599
32427
  return withIndexLock(async () => {
32600
- const index = await readIndex();
32428
+ const index = await readIndex(store);
32601
32429
  const id = await generateIdInternal(index);
32602
32430
  const now = (/* @__PURE__ */ new Date()).toISOString();
32603
32431
  const meta = {
@@ -32610,7 +32438,7 @@ async function saveKnowledge(params) {
32610
32438
  created_at: now,
32611
32439
  updated_at: now
32612
32440
  };
32613
- const relativePath = await writeKnowledgeFile(meta, params.content);
32441
+ const relativePath = await writeKnowledgeFile(meta, params.content, store);
32614
32442
  index.entries[id] = {
32615
32443
  id,
32616
32444
  title: params.title,
@@ -32622,13 +32450,13 @@ async function saveKnowledge(params) {
32622
32450
  created_at: now,
32623
32451
  updated_at: now
32624
32452
  };
32625
- await saveIndex(index);
32453
+ await saveIndex(index, store);
32626
32454
  return { id, filePath: relativePath };
32627
32455
  });
32628
32456
  }
32629
- async function updateKnowledgeFile(id, updates) {
32457
+ async function updateKnowledgeFile(id, updates, store = "knowledge") {
32630
32458
  return withIndexLock(async () => {
32631
- const file = await readKnowledgeFile(id);
32459
+ const file = await readKnowledgeFile(id, store);
32632
32460
  if (!file) return null;
32633
32461
  const now = (/* @__PURE__ */ new Date()).toISOString();
32634
32462
  const meta = { ...file.meta };
@@ -32649,7 +32477,7 @@ async function updateKnowledgeFile(id, updates) {
32649
32477
  if (meta.url) frontmatter.url = meta.url;
32650
32478
  const output = import_gray_matter.default.stringify(content, frontmatter);
32651
32479
  await writeFile2(file.filePath, output, "utf-8");
32652
- const index = await readIndex();
32480
+ const index = await readIndex(store);
32653
32481
  const entry = index.entries[id];
32654
32482
  if (entry) {
32655
32483
  entry.title = meta.title;
@@ -32657,23 +32485,23 @@ async function updateKnowledgeFile(id, updates) {
32657
32485
  entry.category = meta.category;
32658
32486
  entry.preview = generatePreview(content);
32659
32487
  entry.updated_at = now;
32660
- await saveIndex(index);
32488
+ await saveIndex(index, store);
32661
32489
  }
32662
32490
  return { meta, content, filePath: file.filePath };
32663
32491
  });
32664
32492
  }
32665
- async function deleteKnowledgeFile(id) {
32493
+ async function deleteKnowledgeFile(id, store = "knowledge") {
32666
32494
  return withIndexLock(async () => {
32667
- const index = await readIndex();
32495
+ const index = await readIndex(store);
32668
32496
  const entry = index.entries[id];
32669
32497
  if (!entry) return false;
32670
- const filePath = join4(getKnowledgeDir(), entry.file_path);
32498
+ const filePath = join2(getStoreDir(store), entry.file_path);
32671
32499
  try {
32672
- await unlink2(filePath);
32500
+ await unlink(filePath);
32673
32501
  } catch {
32674
32502
  }
32675
32503
  delete index.entries[id];
32676
- await saveIndex(index);
32504
+ await saveIndex(index, store);
32677
32505
  return true;
32678
32506
  });
32679
32507
  }
@@ -32686,16 +32514,16 @@ function getAllTags(index) {
32686
32514
  }
32687
32515
  return Object.entries(tagCounts).map(([name, count]) => ({ name, count })).sort((a, b) => b.count - a.count);
32688
32516
  }
32689
- async function renameTag(oldTag, newTag) {
32517
+ async function renameTag(oldTag, newTag, store = "knowledge") {
32690
32518
  return withIndexLock(async () => {
32691
- const index = await readIndex();
32519
+ const index = await readIndex(store);
32692
32520
  let count = 0;
32693
32521
  for (const entry of Object.values(index.entries)) {
32694
32522
  const idx = entry.tags.indexOf(oldTag);
32695
32523
  if (idx !== -1) {
32696
32524
  entry.tags[idx] = newTag;
32697
32525
  count++;
32698
- const filePath = join4(getKnowledgeDir(), entry.file_path);
32526
+ const filePath = join2(getStoreDir(store), entry.file_path);
32699
32527
  try {
32700
32528
  const raw = await readFile2(filePath, "utf-8");
32701
32529
  const { data, content } = (0, import_gray_matter.default)(raw);
@@ -32712,14 +32540,14 @@ async function renameTag(oldTag, newTag) {
32712
32540
  }
32713
32541
  }
32714
32542
  if (count > 0) {
32715
- await saveIndex(index);
32543
+ await saveIndex(index, store);
32716
32544
  }
32717
32545
  return count;
32718
32546
  });
32719
32547
  }
32720
- async function mergeTags(sourceTags, targetTag) {
32548
+ async function mergeTags(sourceTags, targetTag, store = "knowledge") {
32721
32549
  return withIndexLock(async () => {
32722
- const index = await readIndex();
32550
+ const index = await readIndex(store);
32723
32551
  let count = 0;
32724
32552
  for (const entry of Object.values(index.entries)) {
32725
32553
  const hasSource = entry.tags.some((t) => sourceTags.includes(t));
@@ -32730,7 +32558,7 @@ async function mergeTags(sourceTags, targetTag) {
32730
32558
  }
32731
32559
  entry.tags = newTags;
32732
32560
  count++;
32733
- const filePath = join4(getKnowledgeDir(), entry.file_path);
32561
+ const filePath = join2(getStoreDir(store), entry.file_path);
32734
32562
  try {
32735
32563
  const raw = await readFile2(filePath, "utf-8");
32736
32564
  const { data, content } = (0, import_gray_matter.default)(raw);
@@ -32742,7 +32570,7 @@ async function mergeTags(sourceTags, targetTag) {
32742
32570
  }
32743
32571
  }
32744
32572
  if (count > 0) {
32745
- await saveIndex(index);
32573
+ await saveIndex(index, store);
32746
32574
  }
32747
32575
  return count;
32748
32576
  });
@@ -32757,6 +32585,233 @@ var init_knowledge = __esm({
32757
32585
  }
32758
32586
  });
32759
32587
 
32588
+ // src/lib/logger.ts
32589
+ import { appendFile, mkdir as mkdir3 } from "fs/promises";
32590
+ import { join as join3 } from "path";
32591
+ function setLogLevel(level) {
32592
+ currentLevel = level;
32593
+ }
32594
+ async function log(level, scope, message) {
32595
+ if (LEVELS[level] > LEVELS[currentLevel]) return;
32596
+ const dir = join3(getClaudeinkDir(), "logs");
32597
+ await mkdir3(dir, { recursive: true });
32598
+ const date3 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
32599
+ const line = `${(/* @__PURE__ */ new Date()).toISOString()} [${level}] ${scope} | ${message}
32600
+ `;
32601
+ await appendFile(join3(dir, `mcp-${date3}.log`), line);
32602
+ console.error(`[ClaudeInk] ${line.trim()}`);
32603
+ }
32604
+ var currentLevel, LEVELS;
32605
+ var init_logger = __esm({
32606
+ "src/lib/logger.ts"() {
32607
+ "use strict";
32608
+ init_state();
32609
+ currentLevel = "INFO";
32610
+ LEVELS = { ERROR: 0, WARN: 1, INFO: 2, DEBUG: 3 };
32611
+ }
32612
+ });
32613
+
32614
+ // src/tools/sync.ts
32615
+ import { writeFile as writeFile3, mkdir as mkdir4 } from "fs/promises";
32616
+ import { join as join4 } from "path";
32617
+ async function sync2(input) {
32618
+ if (input.workDir) setWorkDir(input.workDir);
32619
+ const creds = await getCredentials();
32620
+ if (!creds?.token) {
32621
+ return { success: false, message: "\u672A\u6FC0\u6D3B\uFF0C\u8BF7\u5148\u4F7F\u7528 ink.init \u6FC0\u6D3B License" };
32622
+ }
32623
+ return doPull(creds.token);
32624
+ }
32625
+ async function doPull(token) {
32626
+ const config2 = await getConfig();
32627
+ try {
32628
+ const res = await fetch(`${config2.apiBaseUrl}/api/sync/pull`, {
32629
+ headers: { Authorization: `Bearer ${token}` }
32630
+ });
32631
+ if (!res.ok) {
32632
+ if (res.status === 404) {
32633
+ return { success: true, message: "\u4E91\u7AEF\u65E0\u6570\u636E" };
32634
+ }
32635
+ await log("ERROR", "ink.sync", `pull failed: HTTP ${res.status}`);
32636
+ return { success: false, message: `\u62C9\u53D6\u5931\u8D25: HTTP ${res.status}` };
32637
+ }
32638
+ const data = await res.json();
32639
+ const results = [];
32640
+ if (data.configs) {
32641
+ const crawlerConfig = data.configs.find((c) => c.type === "crawler" && c.name === "config");
32642
+ if (crawlerConfig?.content) {
32643
+ try {
32644
+ const sources = JSON.parse(crawlerConfig.content);
32645
+ const crawlerDir = join4(config2.workflowDir || getWorkDir(), "tools", "crawler");
32646
+ await mkdir4(crawlerDir, { recursive: true });
32647
+ await writeFile3(join4(crawlerDir, "config.json"), JSON.stringify({ sources }, null, 2));
32648
+ results.push(`\u8BA2\u9605\u6E90: ${sources.length} \u4E2A`);
32649
+ } catch {
32650
+ }
32651
+ }
32652
+ }
32653
+ if (data.sources && Object.keys(data.sources).length > 0) {
32654
+ const index = await readIndex();
32655
+ let added = 0;
32656
+ for (const [localId, meta] of Object.entries(data.sources)) {
32657
+ if (!index.entries[localId]) {
32658
+ const entry = {
32659
+ id: localId,
32660
+ title: meta.title || "",
32661
+ tags: meta.tags || [],
32662
+ category: "reference",
32663
+ source_type: "url",
32664
+ preview: "",
32665
+ file_path: "",
32666
+ created_at: meta.publishedAt || (/* @__PURE__ */ new Date()).toISOString(),
32667
+ updated_at: meta.updatedAt || (/* @__PURE__ */ new Date()).toISOString()
32668
+ };
32669
+ index.entries[localId] = entry;
32670
+ added++;
32671
+ }
32672
+ }
32673
+ if (added > 0) {
32674
+ index.updated_at = (/* @__PURE__ */ new Date()).toISOString();
32675
+ const knowledgeDir = join4(getWorkDir(), ".claudeink", "knowledge");
32676
+ await mkdir4(knowledgeDir, { recursive: true });
32677
+ await writeFile3(join4(knowledgeDir, "index.json"), JSON.stringify(index, null, 2));
32678
+ results.push(`\u7D20\u6750\u7D22\u5F15: +${added} \u6761\uFF08\u5171 ${Object.keys(index.entries).length} \u6761\uFF09`);
32679
+ } else {
32680
+ results.push(`\u7D20\u6750\u7D22\u5F15: ${Object.keys(index.entries).length} \u6761\uFF08\u5DF2\u540C\u6B65\uFF09`);
32681
+ }
32682
+ }
32683
+ const draftCount = data.drafts ? Object.keys(data.drafts).length : 0;
32684
+ const pubCount = data.published ? Object.keys(data.published).length : 0;
32685
+ if (draftCount > 0 || pubCount > 0) {
32686
+ results.push(`\u8349\u7A3F: ${draftCount} \u7BC7, \u5DF2\u53D1\u5E03: ${pubCount} \u7BC7`);
32687
+ }
32688
+ await updateLastSyncAt();
32689
+ await log("INFO", "ink.sync", `pull completed: ${results.join(", ")}`);
32690
+ return {
32691
+ success: true,
32692
+ message: results.length > 0 ? `\u2705 \u540C\u6B65\u5B8C\u6210
32693
+ ${results.join("\n")}` : "\u2705 \u540C\u6B65\u5B8C\u6210\uFF08\u4E91\u7AEF\u65E0\u65B0\u6570\u636E\uFF09"
32694
+ };
32695
+ } catch (err) {
32696
+ return { success: false, message: `\u540C\u6B65\u5931\u8D25: ${err instanceof Error ? err.message : err}` };
32697
+ }
32698
+ }
32699
+ async function syncPull(input) {
32700
+ if (input.workDir) setWorkDir(input.workDir);
32701
+ const creds = await getCredentials();
32702
+ if (!creds?.token) {
32703
+ return { success: false, message: "\u672A\u6FC0\u6D3B" };
32704
+ }
32705
+ return doPull(creds.token);
32706
+ }
32707
+ var syncSchema;
32708
+ var init_sync = __esm({
32709
+ "src/tools/sync.ts"() {
32710
+ "use strict";
32711
+ init_zod();
32712
+ init_state();
32713
+ init_knowledge();
32714
+ init_logger();
32715
+ syncSchema = external_exports.object({
32716
+ workDir: external_exports.string().optional().describe("\u5DE5\u4F5C\u76EE\u5F55\uFF08\u9ED8\u8BA4\u4F7F\u7528\u914D\u7F6E\u4E2D\u7684 workflowDir\uFF09")
32717
+ });
32718
+ }
32719
+ });
32720
+
32721
+ // src/tools/workflow.ts
32722
+ import { mkdir as mkdir5, access, unlink as unlink2 } from "fs/promises";
32723
+ import { join as join5 } from "path";
32724
+ async function workflowInit(input) {
32725
+ const cwd = input.workDir;
32726
+ const results = [];
32727
+ try {
32728
+ setWorkDir(cwd);
32729
+ const claudeinkDir = join5(cwd, ".claudeink");
32730
+ await mkdir5(join5(claudeinkDir, "knowledge"), { recursive: true });
32731
+ results.push("\u2705 \u77E5\u8BC6\u5E93\u76EE\u5F55\u5DF2\u521B\u5EFA");
32732
+ const state = await readState();
32733
+ state.config.workflowDir = cwd;
32734
+ const oldCredsPath = join5(claudeinkDir, "credentials.json");
32735
+ try {
32736
+ await access(oldCredsPath);
32737
+ await unlink2(oldCredsPath);
32738
+ results.push("\u{1F9F9} \u5DF2\u6E05\u7406\u65E7\u7248 credentials.json");
32739
+ } catch {
32740
+ }
32741
+ let activated = false;
32742
+ if (input.licenseKey) {
32743
+ try {
32744
+ const res = await fetch(`${DEFAULT_API_BASE_URL}/api/auth/activate`, {
32745
+ method: "POST",
32746
+ headers: { "Content-Type": "application/json" },
32747
+ body: JSON.stringify({ key: input.licenseKey })
32748
+ });
32749
+ const data = await res.json();
32750
+ if (data.userId) {
32751
+ state.credentials = {
32752
+ licenseKey: input.licenseKey,
32753
+ token: data.token,
32754
+ userId: data.userId,
32755
+ plan: data.plan,
32756
+ expiresAt: data.expiresAt
32757
+ };
32758
+ results.push(`\u2705 License \u6FC0\u6D3B\u6210\u529F\uFF08\u5957\u9910: ${data.plan}\uFF09`);
32759
+ activated = true;
32760
+ } else {
32761
+ results.push(`\u26A0\uFE0F License \u6FC0\u6D3B\u5931\u8D25: ${JSON.stringify(data)}`);
32762
+ }
32763
+ } catch (err) {
32764
+ results.push(`\u26A0\uFE0F \u6FC0\u6D3B\u7F51\u7EDC\u9519\u8BEF: ${err instanceof Error ? err.message : err}`);
32765
+ }
32766
+ }
32767
+ await writeState(state);
32768
+ results.push("\u2705 state.json");
32769
+ if (state.credentials?.token) {
32770
+ try {
32771
+ const pullResult = await syncPull({ workDir: cwd });
32772
+ if (pullResult.success) {
32773
+ results.push("\u2705 \u5DF2\u4ECE\u4E91\u7AEF\u540C\u6B65\u6570\u636E");
32774
+ } else {
32775
+ results.push("\u2139\uFE0F \u4E91\u7AEF\u65E0\u5DF2\u6709\u6570\u636E");
32776
+ }
32777
+ } catch {
32778
+ results.push("\u2139\uFE0F \u4E91\u7AEF\u540C\u6B65\u8DF3\u8FC7");
32779
+ }
32780
+ }
32781
+ return {
32782
+ success: true,
32783
+ message: [
32784
+ "\u{1F389} ClaudeInk \u77E5\u8BC6\u5E93\u521D\u59CB\u5316\u5B8C\u6210\uFF01",
32785
+ "",
32786
+ ...results,
32787
+ "",
32788
+ "\u{1F3AF} \u4E0B\u4E00\u6B65\uFF1A",
32789
+ "1. \u5728\u5BF9\u8BDD\u4E2D\u8BF4\u300C\u5E2E\u6211\u5B58\u4E00\u4E0B\u300D\u4FDD\u5B58\u6709\u4EF7\u503C\u5185\u5BB9",
32790
+ "2. \u7528\u300C\u627E\u4E00\u4E0B\u5173\u4E8E XX \u7684\u300D\u68C0\u7D22\u77E5\u8BC6\u5E93"
32791
+ ].join("\n")
32792
+ };
32793
+ } catch (err) {
32794
+ return {
32795
+ success: false,
32796
+ message: `\u521D\u59CB\u5316\u5931\u8D25: ${err instanceof Error ? err.message : err}`
32797
+ };
32798
+ }
32799
+ }
32800
+ var DEFAULT_API_BASE_URL, workflowInitSchema;
32801
+ var init_workflow = __esm({
32802
+ "src/tools/workflow.ts"() {
32803
+ "use strict";
32804
+ init_zod();
32805
+ init_state();
32806
+ init_sync();
32807
+ DEFAULT_API_BASE_URL = "https://app.claudeink.com";
32808
+ workflowInitSchema = external_exports.object({
32809
+ workDir: external_exports.string().describe("\u5DE5\u4F5C\u6D41\u521D\u59CB\u5316\u76EE\u6807\u76EE\u5F55\uFF08\u7EDD\u5BF9\u8DEF\u5F84\uFF09"),
32810
+ licenseKey: external_exports.string().optional().describe("License Key\uFF08\u53EF\u9009\uFF0C\u4F20\u5165\u5219\u81EA\u52A8\u6FC0\u6D3B\uFF09")
32811
+ });
32812
+ }
32813
+ });
32814
+
32760
32815
  // src/lib/push.ts
32761
32816
  function fireAndForgetPush(payload) {
32762
32817
  doPush(payload).catch(() => {
@@ -32797,107 +32852,92 @@ import { readFile as readFile3 } from "fs/promises";
32797
32852
  import { extname } from "path";
32798
32853
  async function inkSave(input) {
32799
32854
  try {
32855
+ let localId;
32800
32856
  if (input.id) {
32801
32857
  const result = await updateKnowledgeFile(input.id, {
32802
32858
  title: input.title,
32803
32859
  content: input.content,
32804
32860
  tags: input.tags,
32805
32861
  category: input.category
32806
- });
32862
+ }, "knowledge");
32807
32863
  if (!result) {
32808
- return { success: false, message: `\u672A\u627E\u5230 ID \u4E3A ${input.id} \u7684\u77E5\u8BC6\u7247\u6BB5` };
32864
+ return { success: false, message: `\u672A\u627E\u5230 ID \u4E3A ${input.id} \u7684\u7D20\u6750` };
32809
32865
  }
32810
- fireAndForgetPush({
32811
- knowledge: [{
32812
- id: result.meta.id,
32813
- title: result.meta.title,
32814
- tags: result.meta.tags,
32815
- category: result.meta.category,
32816
- source_type: result.meta.source_type,
32817
- url: result.meta.url,
32818
- created_at: result.meta.created_at,
32819
- updated_at: result.meta.updated_at
32820
- }]
32821
- });
32822
- await log("INFO", "ink.save", `updated id=${input.id} title="${input.title}"`);
32823
- return {
32824
- success: true,
32825
- message: `\u5DF2\u66F4\u65B0\u300C${result.meta.title}\u300D`,
32826
- data: { id: result.meta.id }
32827
- };
32828
- }
32829
- const { id } = await saveKnowledge({
32830
- content: input.content,
32831
- title: input.title,
32832
- tags: input.tags,
32833
- category: input.category,
32834
- source_type: input.source_type,
32835
- url: input.url
32836
- });
32837
- fireAndForgetPush({
32838
- knowledge: [{
32839
- id,
32866
+ localId = input.id;
32867
+ } else {
32868
+ const { id } = await saveKnowledge({
32869
+ content: input.content,
32840
32870
  title: input.title,
32841
32871
  tags: input.tags,
32842
32872
  category: input.category,
32843
32873
  source_type: input.source_type,
32844
32874
  url: input.url,
32845
- created_at: (/* @__PURE__ */ new Date()).toISOString(),
32846
- updated_at: (/* @__PURE__ */ new Date()).toISOString()
32847
- }]
32848
- });
32849
- await log("INFO", "ink.save", `created id=${id} title="${input.title}" tags=${JSON.stringify(input.tags)}`);
32875
+ store: "knowledge"
32876
+ });
32877
+ localId = id;
32878
+ }
32879
+ const creds = await getCredentials();
32880
+ if (creds?.token) {
32881
+ const index = await readIndex("knowledge");
32882
+ fireAndForgetPush({
32883
+ knowledge: Object.values(index.entries).map((e) => ({
32884
+ id: e.id,
32885
+ title: e.title,
32886
+ tags: e.tags,
32887
+ category: e.category,
32888
+ source_type: e.source_type,
32889
+ created_at: e.created_at,
32890
+ updated_at: e.updated_at
32891
+ })),
32892
+ crawlerSources: []
32893
+ });
32894
+ }
32895
+ await log("INFO", "ink.save", `saved source id=${localId} title="${input.title}"`);
32850
32896
  return {
32851
32897
  success: true,
32852
- message: `\u5DF2\u4FDD\u5B58\u300C${input.title}\u300D`,
32853
- data: { id }
32898
+ 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"),
32899
+ data: { id: localId }
32854
32900
  };
32855
32901
  } catch (err) {
32856
32902
  await log("ERROR", "ink.save", `failed: ${err.message}`);
32857
- return { success: false, message: `\u4FDD\u5B58\u5931\u8D25\uFF1A${err.message}` };
32903
+ return { success: false, message: `\u4FDD\u5B58\u7D20\u6750\u5931\u8D25: ${err.message}` };
32858
32904
  }
32859
32905
  }
32860
- async function inkDelete(input) {
32906
+ async function inkWrite(input) {
32861
32907
  try {
32862
- if (!input.confirm) {
32863
- const index = await readIndex();
32864
- const entry = index.entries[input.id];
32865
- if (!entry) {
32866
- return { success: false, message: `\u672A\u627E\u5230 ID \u4E3A ${input.id} \u7684\u77E5\u8BC6\u7247\u6BB5` };
32908
+ let localId;
32909
+ if (input.id) {
32910
+ const result = await updateKnowledgeFile(input.id, {
32911
+ title: input.title,
32912
+ content: input.content,
32913
+ tags: input.tags,
32914
+ category: input.category
32915
+ }, "drafts");
32916
+ if (!result) {
32917
+ return { success: false, message: `\u672A\u627E\u5230 ID \u4E3A ${input.id} \u7684\u8349\u7A3F` };
32867
32918
  }
32919
+ localId = input.id;
32920
+ } else {
32921
+ const { id } = await saveKnowledge({
32922
+ content: input.content,
32923
+ title: input.title,
32924
+ tags: input.tags,
32925
+ category: input.category,
32926
+ source_type: "conversation",
32927
+ store: "drafts"
32928
+ });
32929
+ localId = id;
32930
+ }
32931
+ const creds = await getCredentials();
32932
+ if (!creds?.token) {
32933
+ await log("INFO", "ink.write", `saved draft locally id=${localId} title="${input.title}" (no cloud auth)`);
32868
32934
  return {
32869
- success: false,
32870
- message: `\u786E\u8BA4\u5220\u9664\u300C${entry.title}\u300D\uFF1F\u8BF7\u5C06 confirm \u8BBE\u4E3A true \u4EE5\u786E\u8BA4\u5220\u9664\u3002`,
32871
- data: { id: entry.id, title: entry.title, category: entry.category, tags: entry.tags }
32935
+ success: true,
32936
+ 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`,
32937
+ data: { id: localId }
32872
32938
  };
32873
32939
  }
32874
- const deleted = await deleteKnowledgeFile(input.id);
32875
- if (!deleted) {
32876
- return { success: false, message: `\u672A\u627E\u5230 ID \u4E3A ${input.id} \u7684\u77E5\u8BC6\u7247\u6BB5` };
32877
- }
32878
- fireAndForgetPush({
32879
- knowledge: [{
32880
- id: input.id,
32881
- title: "",
32882
- tags: [],
32883
- category: "reference",
32884
- source_type: "conversation",
32885
- created_at: "",
32886
- updated_at: (/* @__PURE__ */ new Date()).toISOString()
32887
- }]
32888
- });
32889
- await log("INFO", "ink.delete", `deleted id=${input.id}`);
32890
- return { success: true, message: `\u5DF2\u5220\u9664\u77E5\u8BC6\u7247\u6BB5 ${input.id}`, data: { id: input.id } };
32891
- } catch (err) {
32892
- await log("ERROR", "ink.delete", `failed: ${err.message}`);
32893
- return { success: false, message: `\u5220\u9664\u5931\u8D25\uFF1A${err.message}` };
32894
- }
32895
- }
32896
- async function inkWrite(input) {
32897
- const creds = await getCredentials();
32898
- if (!creds?.token) return { success: false, message: "\u672A\u8BA4\u8BC1\uFF0C\u8BF7\u5148\u8FD0\u884C ink.init" };
32899
- const config2 = await getConfig();
32900
- try {
32940
+ const config2 = await getConfig();
32901
32941
  let coverUrl = input.cover || null;
32902
32942
  if (coverUrl) {
32903
32943
  if (coverUrl.startsWith("http://") || coverUrl.startsWith("https://")) {
@@ -32915,43 +32955,103 @@ async function inkWrite(input) {
32915
32955
  }
32916
32956
  const slug = `${Date.now().toString(36)}-${input.title.toLowerCase().replace(/[^a-z0-9\u4e00-\u9fff]+/g, "-").replace(/^-|-$/g, "").slice(0, 60)}`;
32917
32957
  const excerpt = input.excerpt || input.content.replace(/^#+\s.+$/gm, "").replace(/\n+/g, " ").trim().slice(0, 200);
32918
- const res = await fetch(`${config2.apiBaseUrl}/api/blog/manage`, {
32919
- method: "POST",
32920
- headers: {
32921
- "Content-Type": "application/json",
32922
- Authorization: `Bearer ${creds.token}`
32923
- },
32924
- body: JSON.stringify({
32925
- title: input.title,
32926
- slug,
32927
- content: input.content,
32928
- cover_url: coverUrl,
32929
- tags: input.tags,
32930
- category: input.category,
32931
- excerpt,
32932
- source_ids: input.source_ids
32933
- })
32934
- });
32935
- if (!res.ok) {
32936
- const err = await res.text();
32937
- await log("ERROR", "ink.write", `save failed: ${err}`);
32938
- return { success: false, message: `\u4FDD\u5B58\u5931\u8D25: ${err}` };
32958
+ try {
32959
+ const controller = new AbortController();
32960
+ const timeout = setTimeout(() => controller.abort(), 1e4);
32961
+ const res = await fetch(`${config2.apiBaseUrl}/api/blog/manage`, {
32962
+ method: "POST",
32963
+ signal: controller.signal,
32964
+ headers: {
32965
+ "Content-Type": "application/json",
32966
+ Authorization: `Bearer ${creds.token}`
32967
+ },
32968
+ body: JSON.stringify({
32969
+ title: input.title,
32970
+ slug,
32971
+ content: input.content,
32972
+ cover_url: coverUrl,
32973
+ tags: input.tags,
32974
+ category: input.category,
32975
+ excerpt,
32976
+ source_type: "conversation",
32977
+ source_ids: input.source_ids,
32978
+ local_id: localId
32979
+ })
32980
+ });
32981
+ clearTimeout(timeout);
32982
+ if (!res.ok) {
32983
+ const err = await res.text();
32984
+ await log("ERROR", "ink.write", `cloud save failed: ${err}`);
32985
+ return {
32986
+ success: true,
32987
+ message: `\u5DF2\u4FDD\u5B58\u8349\u7A3F\u300C${input.title}\u300D\u5230\u672C\u5730\uFF0C\u4F46\u4E91\u7AEF\u540C\u6B65\u5931\u8D25: ${err}`,
32988
+ data: { id: localId }
32989
+ };
32990
+ }
32991
+ const data = await res.json();
32992
+ await log("INFO", "ink.write", `saved draft id=${localId} cloud_id=${data.id} title="${input.title}"`);
32993
+ return {
32994
+ success: true,
32995
+ message: `\u5DF2\u4FDD\u5B58\u8349\u7A3F\u300C${input.title}\u300D\uFF08\u672C\u5730 + \u4E91\u7AEF\uFF09`,
32996
+ data: { id: localId, cloud_id: data.id, slug: data.slug, status: data.status }
32997
+ };
32998
+ } catch (cloudErr) {
32999
+ await log("WARN", "ink.write", `cloud upload failed (local saved): ${cloudErr.message}`);
33000
+ return {
33001
+ success: true,
33002
+ message: `\u5DF2\u4FDD\u5B58\u8349\u7A3F\u300C${input.title}\u300D\u5230\u672C\u5730\uFF0C\u4F46\u4E91\u7AEF\u540C\u6B65\u5931\u8D25: ${cloudErr.message}`,
33003
+ data: { id: localId }
33004
+ };
32939
33005
  }
32940
- const data = await res.json();
32941
- await log("INFO", "ink.write", `saved article="${input.title}" id=${data.id} slug=${data.slug}`);
32942
- return {
32943
- success: true,
32944
- message: `\u6587\u7AE0\u300C${input.title}\u300D\u5DF2\u4FDD\u5B58\u4E3A\u8349\u7A3F\uFF0C\u8BF7\u5230 Dashboard \u53D1\u5E03`,
32945
- data: { id: data.id, slug: data.slug, status: data.status }
32946
- };
32947
33006
  } catch (err) {
32948
33007
  await log("ERROR", "ink.write", `failed: ${err.message}`);
32949
- return { success: false, message: `\u5199\u6587\u7AE0\u5931\u8D25: ${err.message}` };
33008
+ return { success: false, message: `\u4FDD\u5B58\u8349\u7A3F\u5931\u8D25: ${err.message}` };
33009
+ }
33010
+ }
33011
+ async function inkDelete(input) {
33012
+ try {
33013
+ const store = input.store;
33014
+ if (!input.confirm) {
33015
+ const index = await readIndex(store);
33016
+ const entry = index.entries[input.id];
33017
+ if (!entry) {
33018
+ return { success: false, message: `\u672A\u627E\u5230 ID \u4E3A ${input.id} \u7684\u6761\u76EE` };
33019
+ }
33020
+ return {
33021
+ success: false,
33022
+ message: `\u786E\u8BA4\u5220\u9664\u300C${entry.title}\u300D\uFF1F\u8BF7\u5C06 confirm \u8BBE\u4E3A true \u4EE5\u786E\u8BA4\u5220\u9664\u3002`,
33023
+ data: { id: entry.id, title: entry.title, store, category: entry.category, tags: entry.tags }
33024
+ };
33025
+ }
33026
+ const deleted = await deleteKnowledgeFile(input.id, store);
33027
+ if (!deleted) {
33028
+ return { success: false, message: `\u672A\u627E\u5230 ID \u4E3A ${input.id} \u7684\u6761\u76EE` };
33029
+ }
33030
+ try {
33031
+ const creds = await getCredentials();
33032
+ if (creds?.token) {
33033
+ const config2 = await getConfig();
33034
+ await fetch(`${config2.apiBaseUrl}/api/blog/manage`, {
33035
+ method: "DELETE",
33036
+ headers: {
33037
+ "Content-Type": "application/json",
33038
+ Authorization: `Bearer ${creds.token}`
33039
+ },
33040
+ body: JSON.stringify({ local_id: input.id })
33041
+ });
33042
+ }
33043
+ } catch {
33044
+ }
33045
+ await log("INFO", "ink.delete", `deleted id=${input.id} store=${store}`);
33046
+ return { success: true, message: `\u5DF2\u5220\u9664 ${input.id}`, data: { id: input.id } };
33047
+ } catch (err) {
33048
+ await log("ERROR", "ink.delete", `failed: ${err.message}`);
33049
+ return { success: false, message: `\u5220\u9664\u5931\u8D25\uFF1A${err.message}` };
32950
33050
  }
32951
33051
  }
32952
33052
  async function inkTags(input) {
32953
33053
  try {
32954
- const index = await readIndex();
33054
+ const index = await readIndex("knowledge");
32955
33055
  switch (input.action) {
32956
33056
  case "list": {
32957
33057
  const tags = getAllTags(index);
@@ -32965,14 +33065,14 @@ async function inkTags(input) {
32965
33065
  if (!input.tag || !input.new_tag) {
32966
33066
  return { success: false, message: "rename \u9700\u8981 tag\uFF08\u539F\u6807\u7B7E\uFF09\u548C new_tag\uFF08\u65B0\u6807\u7B7E\u540D\uFF09" };
32967
33067
  }
32968
- const count = await renameTag(input.tag, input.new_tag);
33068
+ const count = await renameTag(input.tag, input.new_tag, "knowledge");
32969
33069
  if (count === 0) {
32970
- return { success: false, message: `\u672A\u627E\u5230\u4F7F\u7528\u6807\u7B7E\u300C${input.tag}\u300D\u7684\u77E5\u8BC6\u7247\u6BB5` };
33070
+ return { success: false, message: `\u672A\u627E\u5230\u4F7F\u7528\u6807\u7B7E\u300C${input.tag}\u300D\u7684\u6761\u76EE` };
32971
33071
  }
32972
33072
  await log("INFO", "ink.tags", `renamed "${input.tag}" \u2192 "${input.new_tag}", affected=${count}`);
32973
33073
  return {
32974
33074
  success: true,
32975
- 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`,
33075
+ message: `\u5DF2\u5C06 ${count} \u6761\u7684\u6807\u7B7E\u300C${input.tag}\u300D\u91CD\u547D\u540D\u4E3A\u300C${input.new_tag}\u300D`,
32976
33076
  data: { old_tag: input.tag, new_tag: input.new_tag, affected: count }
32977
33077
  };
32978
33078
  }
@@ -32980,14 +33080,14 @@ async function inkTags(input) {
32980
33080
  if (!input.source_tags || input.source_tags.length === 0 || !input.new_tag) {
32981
33081
  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" };
32982
33082
  }
32983
- const count = await mergeTags(input.source_tags, input.new_tag);
33083
+ const count = await mergeTags(input.source_tags, input.new_tag, "knowledge");
32984
33084
  if (count === 0) {
32985
- return { success: false, message: "\u672A\u627E\u5230\u4F7F\u7528\u8FD9\u4E9B\u6807\u7B7E\u7684\u77E5\u8BC6\u7247\u6BB5" };
33085
+ return { success: false, message: "\u672A\u627E\u5230\u4F7F\u7528\u8FD9\u4E9B\u6807\u7B7E\u7684\u6761\u76EE" };
32986
33086
  }
32987
33087
  await log("INFO", "ink.tags", `merged [${input.source_tags.join(",")}] \u2192 "${input.new_tag}", affected=${count}`);
32988
33088
  return {
32989
33089
  success: true,
32990
- message: `\u5DF2\u5C06 ${count} \u6761\u77E5\u8BC6\u7247\u6BB5\u7684\u6807\u7B7E\u5408\u5E76\u4E3A\u300C${input.new_tag}\u300D`,
33090
+ message: `\u5DF2\u5C06 ${count} \u6761\u7684\u6807\u7B7E\u5408\u5E76\u4E3A\u300C${input.new_tag}\u300D`,
32991
33091
  data: { source_tags: input.source_tags, target_tag: input.new_tag, affected: count }
32992
33092
  };
32993
33093
  }
@@ -33052,36 +33152,38 @@ async function uploadImageFromUrl(imageUrl, type, token, apiBase) {
33052
33152
  return { success: false, error: err.message };
33053
33153
  }
33054
33154
  }
33055
- var inkSaveSchema, inkDeleteSchema, inkWriteSchema, inkTagsSchema;
33155
+ var inkSaveSchema, inkWriteSchema, inkDeleteSchema, inkTagsSchema;
33056
33156
  var init_ink = __esm({
33057
33157
  "src/tools/ink.ts"() {
33058
33158
  "use strict";
33059
33159
  init_zod();
33060
33160
  init_knowledge();
33061
- init_push();
33062
33161
  init_state();
33162
+ init_push();
33063
33163
  init_logger();
33064
33164
  inkSaveSchema = external_exports.object({
33065
- 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"),
33066
- content: external_exports.string().describe("\u8981\u4FDD\u5B58\u7684\u5185\u5BB9\uFF08\u5FC5\u586B\uFF09"),
33067
- title: external_exports.string().describe("\u6807\u9898\uFF08Claude \u81EA\u52A8\u751F\u6210\uFF09"),
33165
+ 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"),
33166
+ title: external_exports.string().describe("\u7D20\u6750\u6807\u9898"),
33167
+ content: external_exports.string().describe("Markdown \u5168\u6587"),
33068
33168
  tags: external_exports.array(external_exports.string()).optional().default([]).describe("\u6807\u7B7E"),
33069
33169
  category: external_exports.enum(["insight", "decision", "analysis", "idea", "reference", "action"]).optional().default("reference").describe("\u5206\u7C7B"),
33070
33170
  source_type: external_exports.enum(["conversation", "url", "manual"]).optional().default("conversation").describe("\u6765\u6E90\u7C7B\u578B"),
33071
33171
  url: external_exports.string().optional().describe("\u6765\u6E90 URL")
33072
33172
  });
33073
- inkDeleteSchema = external_exports.object({
33074
- id: external_exports.string().describe("\u77E5\u8BC6\u7247\u6BB5 ID"),
33075
- confirm: external_exports.boolean().optional().default(false).describe("\u786E\u8BA4\u5220\u9664\uFF08\u5FC5\u987B\u4E3A true \u624D\u4F1A\u6267\u884C\u5220\u9664\uFF09")
33076
- });
33077
33173
  inkWriteSchema = external_exports.object({
33174
+ 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"),
33078
33175
  title: external_exports.string().describe("\u6587\u7AE0\u6807\u9898"),
33079
33176
  content: external_exports.string().describe("Markdown \u5168\u6587"),
33177
+ tags: external_exports.array(external_exports.string()).optional().default([]).describe("\u6807\u7B7E"),
33178
+ category: external_exports.enum(["insight", "decision", "analysis", "idea", "reference", "action"]).optional().default("reference").describe("\u5206\u7C7B"),
33080
33179
  cover: external_exports.string().optional().describe("\u5C01\u9762\u56FE\uFF08\u672C\u5730\u8DEF\u5F84\u6216 URL\uFF0C\u81EA\u52A8\u4E0A\u4F20\uFF09"),
33081
- tags: external_exports.array(external_exports.string()).optional().default([]).describe("\u6587\u7AE0\u6807\u7B7E"),
33082
- category: external_exports.string().optional().describe("\u6587\u7AE0\u5206\u7C7B"),
33083
33180
  excerpt: external_exports.string().optional().describe("\u6458\u8981\uFF08\u4E0D\u586B\u81EA\u52A8\u622A\u53D6\u524D 200 \u5B57\uFF09"),
33084
- source_ids: external_exports.array(external_exports.string()).optional().describe("\u5173\u8054\u7684\u7D20\u6750 ink ID\uFF08\u6EAF\u6E90\uFF09")
33181
+ source_ids: external_exports.array(external_exports.string()).optional().describe("\u5173\u8054\u7684\u7D20\u6750 ID\uFF08\u6EAF\u6E90\uFF09")
33182
+ });
33183
+ inkDeleteSchema = external_exports.object({
33184
+ id: external_exports.string().describe("\u6761\u76EE ID"),
33185
+ 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"),
33186
+ confirm: external_exports.boolean().optional().default(false).describe("\u786E\u8BA4\u5220\u9664\uFF08\u5FC5\u987B\u4E3A true \u624D\u4F1A\u6267\u884C\u5220\u9664\uFF09")
33085
33187
  });
33086
33188
  inkTagsSchema = external_exports.object({
33087
33189
  action: external_exports.enum(["list", "rename", "merge"]).describe("\u64CD\u4F5C\uFF1Alist\uFF08\u5217\u51FA\u6240\u6709\u6807\u7B7E\uFF09/ rename\uFF08\u91CD\u547D\u540D\uFF09/ merge\uFF08\u5408\u5E76\uFF09"),
@@ -33094,13 +33196,13 @@ var init_ink = __esm({
33094
33196
 
33095
33197
  // src/tools/source.ts
33096
33198
  import { readFile as readFile4 } from "fs/promises";
33097
- import { join as join5 } from "path";
33199
+ import { join as join6 } from "path";
33098
33200
  import { readdirSync as readdirSync2 } from "fs";
33099
33201
  import { execSync } from "child_process";
33100
33202
  async function sourceCrawl(input) {
33101
33203
  const config2 = await getConfig();
33102
- const crawlerDir = join5(config2.workflowDir || process.cwd(), "tools", "crawler");
33103
- const configPath = join5(crawlerDir, "config.json");
33204
+ const crawlerDir = join6(config2.workflowDir || process.cwd(), "tools", "crawler");
33205
+ const configPath = join6(crawlerDir, "config.json");
33104
33206
  let crawlerConfig;
33105
33207
  try {
33106
33208
  crawlerConfig = JSON.parse(await readFile4(configPath, "utf-8"));
@@ -33117,7 +33219,7 @@ async function sourceCrawl(input) {
33117
33219
  message: input.sourceId ? `\u672A\u627E\u5230\u722C\u866B\u6E90: ${input.sourceId}` : "\u6CA1\u6709\u5DF2\u542F\u7528\u7684\u722C\u866B\u6E90"
33118
33220
  };
33119
33221
  }
33120
- const crawlScript = join5(crawlerDir, "crawl.mjs");
33222
+ const crawlScript = join6(crawlerDir, "crawl.mjs");
33121
33223
  const args2 = input.sourceId ? `--source ${input.sourceId}` : "";
33122
33224
  try {
33123
33225
  execSync(`node "${crawlScript}" ${args2}`, {
@@ -33129,7 +33231,7 @@ async function sourceCrawl(input) {
33129
33231
  let saved = 0;
33130
33232
  let queued = 0;
33131
33233
  for (const target of targets) {
33132
- const sourceDir = join5(config2.workflowDir, "sources", "articles", target.id);
33234
+ const sourceDir = join6(config2.workflowDir, "sources", "articles", target.id);
33133
33235
  let files;
33134
33236
  try {
33135
33237
  files = readdirSync2(sourceDir).filter((f) => f.endsWith(".md"));
@@ -33137,7 +33239,7 @@ async function sourceCrawl(input) {
33137
33239
  continue;
33138
33240
  }
33139
33241
  for (const f of files) {
33140
- const filePath = join5(sourceDir, f);
33242
+ const filePath = join6(sourceDir, f);
33141
33243
  try {
33142
33244
  const raw = await readFile4(filePath, "utf-8");
33143
33245
  const { data, content } = (0, import_gray_matter2.default)(raw);
@@ -33212,8 +33314,8 @@ var init_source = __esm({
33212
33314
  });
33213
33315
 
33214
33316
  // src/tools/subscribe.ts
33215
- import { readFile as readFile5, writeFile as writeFile3, mkdir as mkdir5 } from "fs/promises";
33216
- import { join as join6 } from "path";
33317
+ import { readFile as readFile5, writeFile as writeFile4, mkdir as mkdir6 } from "fs/promises";
33318
+ import { join as join7 } from "path";
33217
33319
  async function readCrawlerConfig(configPath) {
33218
33320
  try {
33219
33321
  return JSON.parse(await readFile5(configPath, "utf-8"));
@@ -33223,9 +33325,9 @@ async function readCrawlerConfig(configPath) {
33223
33325
  }
33224
33326
  async function sourceSubscribe(input) {
33225
33327
  const config2 = await getConfig();
33226
- const crawlerDir = join6(config2.workflowDir || process.cwd(), "tools", "crawler");
33227
- await mkdir5(crawlerDir, { recursive: true });
33228
- const configPath = join6(crawlerDir, "config.json");
33328
+ const crawlerDir = join7(config2.workflowDir || process.cwd(), "tools", "crawler");
33329
+ await mkdir6(crawlerDir, { recursive: true });
33330
+ const configPath = join7(crawlerDir, "config.json");
33229
33331
  const crawlerConfig = await readCrawlerConfig(configPath);
33230
33332
  if (input.action === "list") {
33231
33333
  return {
@@ -33257,7 +33359,7 @@ async function sourceSubscribe(input) {
33257
33359
  if (input.crawlConfig.maxArticles) newSource.maxArticles = input.crawlConfig.maxArticles;
33258
33360
  }
33259
33361
  crawlerConfig.sources.push(newSource);
33260
- await writeFile3(configPath, JSON.stringify(crawlerConfig, null, 2));
33362
+ await writeFile4(configPath, JSON.stringify(crawlerConfig, null, 2));
33261
33363
  fireAndForgetPush({
33262
33364
  crawlerSources: crawlerConfig.sources.map((s) => ({
33263
33365
  id: s.id,
@@ -33275,7 +33377,7 @@ async function sourceSubscribe(input) {
33275
33377
  return { success: false, message: "\u5220\u9664\u8BA2\u9605\u6E90\u9700\u8981 id \u53C2\u6570" };
33276
33378
  }
33277
33379
  crawlerConfig.sources = crawlerConfig.sources.filter((s) => s.id !== input.id);
33278
- await writeFile3(configPath, JSON.stringify(crawlerConfig, null, 2));
33380
+ await writeFile4(configPath, JSON.stringify(crawlerConfig, null, 2));
33279
33381
  fireAndForgetPush({
33280
33382
  crawlerSources: crawlerConfig.sources.map((s) => ({
33281
33383
  id: s.id,
@@ -33332,7 +33434,7 @@ async function main() {
33332
33434
  }
33333
33435
  const transport = new StdioServerTransport();
33334
33436
  await server.connect(transport);
33335
- await log("INFO", "server", "started (8 tools)");
33437
+ await log("INFO", "server", "started (7 tools)");
33336
33438
  }
33337
33439
  var server;
33338
33440
  var init_index = __esm({
@@ -33349,7 +33451,7 @@ var init_index = __esm({
33349
33451
  init_logger();
33350
33452
  server = new McpServer({
33351
33453
  name: "ClaudeInk",
33352
- version: "2.0.2"
33454
+ version: "2.2.0"
33353
33455
  });
33354
33456
  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) => {
33355
33457
  const result = await workflowInit(input);
@@ -33359,23 +33461,23 @@ var init_index = __esm({
33359
33461
  const result = await sync2(input);
33360
33462
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
33361
33463
  });
33362
- 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) => {
33464
+ 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) => {
33363
33465
  const result = await inkSave(input);
33364
33466
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
33365
33467
  });
33366
- server.tool("ink.delete", "\u5220\u9664\u77E5\u8BC6\u7247\u6BB5\uFF08\u9700\u786E\u8BA4\uFF09", inkDeleteSchema.shape, async (input) => {
33367
- const result = await inkDelete(input);
33468
+ 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) => {
33469
+ const result = await inkWrite(input);
33368
33470
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
33369
33471
  });
33370
- 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) => {
33371
- const result = await inkWrite(input);
33472
+ server.tool("ink.delete", "\u5220\u9664\u6761\u76EE\uFF08\u672C\u5730 + \u4E91\u7AEF\uFF09", inkDeleteSchema.shape, async (input) => {
33473
+ const result = await inkDelete(input);
33372
33474
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
33373
33475
  });
33374
33476
  server.tool("ink.tags", "\u6807\u7B7E\u7BA1\u7406\uFF08\u5217\u51FA / \u91CD\u547D\u540D / \u5408\u5E76\u6807\u7B7E\uFF09", inkTagsSchema.shape, async (input) => {
33375
33477
  const result = await inkTags(input);
33376
33478
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
33377
33479
  });
33378
- 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) => {
33480
+ 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) => {
33379
33481
  const result = await sourceCrawl(input);
33380
33482
  return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
33381
33483
  });
@@ -33394,14 +33496,14 @@ var init_index = __esm({
33394
33496
  });
33395
33497
 
33396
33498
  // src/cli.ts
33397
- import { writeFile as writeFile4, readFile as readFile6, mkdir as mkdir6, cp, access as access2 } from "fs/promises";
33398
- import { join as join7, dirname } from "path";
33499
+ import { writeFile as writeFile5, readFile as readFile6, mkdir as mkdir7, cp, access as access2 } from "fs/promises";
33500
+ import { join as join8, dirname } from "path";
33399
33501
  import { fileURLToPath as fileURLToPath3 } from "url";
33400
33502
  var args = process.argv.slice(2);
33401
33503
  var command = args[0];
33402
33504
  var __filename = fileURLToPath3(import.meta.url);
33403
33505
  var __dirname = dirname(__filename);
33404
- var WORKFLOW_SRC = join7(__dirname, "..", "workflow");
33506
+ var WORKFLOW_SRC = join8(__dirname, "..", "workflow");
33405
33507
  async function init() {
33406
33508
  const keyIdx = args.indexOf("--key");
33407
33509
  const key = keyIdx >= 0 ? args[keyIdx + 1] : void 0;
@@ -33417,8 +33519,8 @@ async function init() {
33417
33519
  console.log("\u{1F4E6} \u91CA\u653E\u5DE5\u4F5C\u6D41\u6A21\u677F...");
33418
33520
  const items = ["CLAUDE.md", "base-rules.md", "platforms", "accounts", "tools"];
33419
33521
  for (const item of items) {
33420
- const src = join7(WORKFLOW_SRC, item);
33421
- const dest = join7(cwd, item);
33522
+ const src = join8(WORKFLOW_SRC, item);
33523
+ const dest = join8(cwd, item);
33422
33524
  try {
33423
33525
  await access2(dest);
33424
33526
  console.log(` \u23ED\uFE0F ${item} \u5DF2\u5B58\u5728\uFF0C\u8DF3\u8FC7`);
@@ -33438,14 +33540,14 @@ async function init() {
33438
33540
  ".claudeink"
33439
33541
  ];
33440
33542
  for (const dir of dirs) {
33441
- await mkdir6(join7(cwd, dir), { recursive: true });
33543
+ await mkdir7(join8(cwd, dir), { recursive: true });
33442
33544
  }
33443
33545
  console.log(" \u2705 sources/, templates/, .claudeink/");
33444
33546
  const { glob: glob2 } = await Promise.resolve().then(() => (init_esm5(), esm_exports));
33445
33547
  const yamlFiles = await glob2("accounts/*.yaml", { cwd, ignore: "accounts/_template.yaml" });
33446
33548
  for (const yamlFile of yamlFiles) {
33447
33549
  try {
33448
- const content = await readFile6(join7(cwd, yamlFile), "utf-8");
33550
+ const content = await readFile6(join8(cwd, yamlFile), "utf-8");
33449
33551
  const idMatch = content.match(/^id:\s*"?([^"\n]+)"?/m);
33450
33552
  if (idMatch) {
33451
33553
  const id = idMatch[1].trim();
@@ -33455,9 +33557,9 @@ async function init() {
33455
33557
  const drafts = (draftsMatch?.[1] || `accounts/${id}/drafts/`).replace("{id}", id).trim();
33456
33558
  const published = (publishedMatch?.[1] || `accounts/${id}/published/`).replace("{id}", id).trim();
33457
33559
  const assets = (assetsMatch?.[1] || `accounts/${id}/assets/`).replace("{id}", id).trim();
33458
- await mkdir6(join7(cwd, drafts), { recursive: true });
33459
- await mkdir6(join7(cwd, published), { recursive: true });
33460
- await mkdir6(join7(cwd, assets), { recursive: true });
33560
+ await mkdir7(join8(cwd, drafts), { recursive: true });
33561
+ await mkdir7(join8(cwd, published), { recursive: true });
33562
+ await mkdir7(join8(cwd, assets), { recursive: true });
33461
33563
  console.log(` \u2705 \u8D26\u53F7 ${id}: ${drafts}, ${published}, ${assets}`);
33462
33564
  }
33463
33565
  } catch {
@@ -33472,8 +33574,8 @@ async function init() {
33472
33574
  });
33473
33575
  const data = await res.json();
33474
33576
  if (data.userId) {
33475
- await writeFile4(
33476
- join7(cwd, ".claudeink", "credentials.json"),
33577
+ await writeFile5(
33578
+ join8(cwd, ".claudeink", "credentials.json"),
33477
33579
  JSON.stringify(data, null, 2),
33478
33580
  { mode: 384 }
33479
33581
  );
@@ -33487,11 +33589,11 @@ async function init() {
33487
33589
  console.log(" \u53EF\u7A0D\u540E\u624B\u52A8\u6FC0\u6D3B\u3002");
33488
33590
  }
33489
33591
  try {
33490
- await access2(join7(cwd, "tools", "crawler", "package.json"));
33592
+ await access2(join8(cwd, "tools", "crawler", "package.json"));
33491
33593
  console.log("\n\u{1F4E6} \u5B89\u88C5\u722C\u866B\u4F9D\u8D56...");
33492
33594
  const { execSync: execSync2 } = await import("child_process");
33493
33595
  execSync2("npm install --silent", {
33494
- cwd: join7(cwd, "tools", "crawler"),
33596
+ cwd: join8(cwd, "tools", "crawler"),
33495
33597
  stdio: "pipe"
33496
33598
  });
33497
33599
  console.log(" \u2705 \u722C\u866B\u4F9D\u8D56\u5DF2\u5B89\u88C5");