@claudeink/mcp-server 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +517 -953
- package/package.json +1 -1
- package/workflow/accounts/_template.yaml +1 -0
package/dist/index.js
CHANGED
|
@@ -4,8 +4,10 @@
|
|
|
4
4
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
5
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
6
|
|
|
7
|
-
// src/tools/
|
|
7
|
+
// src/tools/source.ts
|
|
8
8
|
import { z } from "zod";
|
|
9
|
+
import { mkdir as mkdir3 } from "fs/promises";
|
|
10
|
+
import { join as join4 } from "path";
|
|
9
11
|
|
|
10
12
|
// src/lib/config.ts
|
|
11
13
|
import { readFile, writeFile, mkdir, chmod, access } from "fs/promises";
|
|
@@ -28,11 +30,6 @@ var DEFAULT_CONFIG = {
|
|
|
28
30
|
maxTagQueueBatch: 20,
|
|
29
31
|
workflowDir: ""
|
|
30
32
|
};
|
|
31
|
-
var DEFAULT_SYNC_STATE = {
|
|
32
|
-
lastSyncAt: null,
|
|
33
|
-
configVersion: "0.0.0",
|
|
34
|
-
pendingPushCount: 0
|
|
35
|
-
};
|
|
36
33
|
var DEFAULT_TAG_QUEUE = {
|
|
37
34
|
queue: [],
|
|
38
35
|
failed: [],
|
|
@@ -76,23 +73,9 @@ async function getCredentials() {
|
|
|
76
73
|
if (!await fileExists(PATHS.credentials)) return null;
|
|
77
74
|
return readJson(PATHS.credentials, null);
|
|
78
75
|
}
|
|
79
|
-
async function saveCredentials(creds) {
|
|
80
|
-
await writeJson(PATHS.credentials, creds, true);
|
|
81
|
-
}
|
|
82
76
|
async function getConfig() {
|
|
83
77
|
return readJson(PATHS.config, DEFAULT_CONFIG);
|
|
84
78
|
}
|
|
85
|
-
async function saveConfig(config) {
|
|
86
|
-
const current = await getConfig();
|
|
87
|
-
await writeJson(PATHS.config, { ...current, ...config });
|
|
88
|
-
}
|
|
89
|
-
async function getSyncState() {
|
|
90
|
-
return readJson(PATHS.syncState, DEFAULT_SYNC_STATE);
|
|
91
|
-
}
|
|
92
|
-
async function saveSyncState(state) {
|
|
93
|
-
const current = await getSyncState();
|
|
94
|
-
await writeJson(PATHS.syncState, { ...current, ...state });
|
|
95
|
-
}
|
|
96
79
|
async function getTagQueue() {
|
|
97
80
|
return readJson(PATHS.tagQueue, DEFAULT_TAG_QUEUE);
|
|
98
81
|
}
|
|
@@ -102,188 +85,10 @@ async function saveTagQueue(queue) {
|
|
|
102
85
|
async function getCrawlSchedules() {
|
|
103
86
|
return readJson(PATHS.crawlSchedules, DEFAULT_CRAWL_SCHEDULES);
|
|
104
87
|
}
|
|
105
|
-
async function saveCrawlSchedules(schedules) {
|
|
106
|
-
await writeJson(PATHS.crawlSchedules, schedules);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// src/lib/api-client.ts
|
|
110
|
-
async function request(path, options = {}) {
|
|
111
|
-
const config = await getConfig();
|
|
112
|
-
const url = `${config.apiBaseUrl}${path}`;
|
|
113
|
-
try {
|
|
114
|
-
const headers = {
|
|
115
|
-
"Content-Type": "application/json",
|
|
116
|
-
"User-Agent": "ClaudeInk-MCP/0.1.0"
|
|
117
|
-
};
|
|
118
|
-
if (options.token) {
|
|
119
|
-
headers["Authorization"] = `Bearer ${options.token}`;
|
|
120
|
-
} else {
|
|
121
|
-
const creds = await getCredentials();
|
|
122
|
-
if (creds?.token) {
|
|
123
|
-
headers["Authorization"] = `Bearer ${creds.token}`;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
const res = await fetch(url, {
|
|
127
|
-
method: options.method || "GET",
|
|
128
|
-
headers,
|
|
129
|
-
body: options.body ? JSON.stringify(options.body) : void 0
|
|
130
|
-
});
|
|
131
|
-
const data = await res.json().catch(() => null);
|
|
132
|
-
return {
|
|
133
|
-
ok: res.ok,
|
|
134
|
-
status: res.status,
|
|
135
|
-
data: data ?? void 0,
|
|
136
|
-
error: res.ok ? void 0 : `HTTP ${res.status}`
|
|
137
|
-
};
|
|
138
|
-
} catch (err) {
|
|
139
|
-
return {
|
|
140
|
-
ok: false,
|
|
141
|
-
status: 0,
|
|
142
|
-
error: err instanceof Error ? err.message : "Unknown error"
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
async function activateLicense(key) {
|
|
147
|
-
return request("/api/auth/activate", {
|
|
148
|
-
method: "POST",
|
|
149
|
-
body: { key }
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
async function fetchLatestConfig(localVersion) {
|
|
153
|
-
return request(
|
|
154
|
-
`/api/config/latest?version=${localVersion}`
|
|
155
|
-
);
|
|
156
|
-
}
|
|
157
|
-
async function pushSyncBatch(payload) {
|
|
158
|
-
return request("/api/sync/batch", {
|
|
159
|
-
method: "POST",
|
|
160
|
-
body: payload
|
|
161
|
-
});
|
|
162
|
-
}
|
|
163
|
-
async function checkAuthStatus() {
|
|
164
|
-
return request(
|
|
165
|
-
"/api/auth/status"
|
|
166
|
-
);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// src/tools/auth.ts
|
|
170
|
-
import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
171
|
-
import { join as join2 } from "path";
|
|
172
|
-
var activateSchema = z.object({
|
|
173
|
-
key: z.string().describe("License key, e.g. WF-XXXX-XXXX-XXXX"),
|
|
174
|
-
workflowDir: z.string().describe("\u5DE5\u4F5C\u6D41\u6839\u76EE\u5F55\u8DEF\u5F84")
|
|
175
|
-
});
|
|
176
|
-
async function authActivate(input) {
|
|
177
|
-
const res = await activateLicense(input.key);
|
|
178
|
-
if (!res.ok || !res.data) {
|
|
179
|
-
return {
|
|
180
|
-
success: false,
|
|
181
|
-
message: `License key \u9A8C\u8BC1\u5931\u8D25: ${res.error || "\u672A\u77E5\u9519\u8BEF"}`
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
await saveCredentials({
|
|
185
|
-
licenseKey: input.key,
|
|
186
|
-
token: res.data.token,
|
|
187
|
-
userId: res.data.userId,
|
|
188
|
-
plan: res.data.plan,
|
|
189
|
-
expiresAt: res.data.expiresAt
|
|
190
|
-
});
|
|
191
|
-
await saveConfig({ workflowDir: input.workflowDir });
|
|
192
|
-
const configRes = await fetchLatestConfig("0.0.0");
|
|
193
|
-
if (configRes.ok && configRes.data) {
|
|
194
|
-
for (const [filePath, content] of Object.entries(configRes.data.files)) {
|
|
195
|
-
const fullPath = join2(input.workflowDir, filePath);
|
|
196
|
-
const dir = fullPath.substring(0, fullPath.lastIndexOf("/"));
|
|
197
|
-
await mkdir2(dir, { recursive: true });
|
|
198
|
-
await writeFile2(fullPath, content, "utf-8");
|
|
199
|
-
}
|
|
200
|
-
await saveSyncState({ configVersion: configRes.data.version });
|
|
201
|
-
}
|
|
202
|
-
return {
|
|
203
|
-
success: true,
|
|
204
|
-
message: `\u6FC0\u6D3B\u6210\u529F\uFF01\u5957\u9910: ${res.data.plan}\uFF0C\u5230\u671F: ${res.data.expiresAt}`,
|
|
205
|
-
data: {
|
|
206
|
-
plan: res.data.plan,
|
|
207
|
-
expiresAt: res.data.expiresAt,
|
|
208
|
-
configVersion: configRes.data?.version || "unknown"
|
|
209
|
-
}
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
async function authStatus() {
|
|
213
|
-
const creds = await getCredentials();
|
|
214
|
-
if (!creds) {
|
|
215
|
-
return {
|
|
216
|
-
success: false,
|
|
217
|
-
message: "\u672A\u6FC0\u6D3B\u3002\u8BF7\u5148\u4F7F\u7528 auth.activate \u6FC0\u6D3B license key\u3002"
|
|
218
|
-
};
|
|
219
|
-
}
|
|
220
|
-
const res = await checkAuthStatus();
|
|
221
|
-
if (res.ok && res.data) {
|
|
222
|
-
return {
|
|
223
|
-
success: true,
|
|
224
|
-
message: `\u5DF2\u6FC0\u6D3B\u3002\u5957\u9910: ${res.data.plan}\uFF0C\u5230\u671F: ${res.data.expiresAt}\uFF0C\u72B6\u6001: ${res.data.valid ? "\u6709\u6548" : "\u5DF2\u8FC7\u671F"}`,
|
|
225
|
-
data: res.data
|
|
226
|
-
};
|
|
227
|
-
}
|
|
228
|
-
const expired = new Date(creds.expiresAt) < /* @__PURE__ */ new Date();
|
|
229
|
-
return {
|
|
230
|
-
success: true,
|
|
231
|
-
message: `\u5DF2\u6FC0\u6D3B\uFF08\u79BB\u7EBF\u6A21\u5F0F\uFF09\u3002\u5957\u9910: ${creds.plan}\uFF0C\u5230\u671F: ${creds.expiresAt}${expired ? "\uFF08\u5DF2\u8FC7\u671F\uFF09" : ""}`,
|
|
232
|
-
data: {
|
|
233
|
-
plan: creds.plan,
|
|
234
|
-
expiresAt: creds.expiresAt,
|
|
235
|
-
valid: !expired,
|
|
236
|
-
offline: true
|
|
237
|
-
}
|
|
238
|
-
};
|
|
239
|
-
}
|
|
240
|
-
async function configSync() {
|
|
241
|
-
const syncState = await getSyncState();
|
|
242
|
-
const res = await fetchLatestConfig(syncState.configVersion);
|
|
243
|
-
if (!res.ok || !res.data) {
|
|
244
|
-
return {
|
|
245
|
-
success: false,
|
|
246
|
-
message: `\u914D\u7F6E\u540C\u6B65\u5931\u8D25: ${res.error || "\u65E0\u6CD5\u8FDE\u63A5\u4E91\u7AEF"}`
|
|
247
|
-
};
|
|
248
|
-
}
|
|
249
|
-
if (res.data.version === syncState.configVersion) {
|
|
250
|
-
return {
|
|
251
|
-
success: true,
|
|
252
|
-
message: `\u914D\u7F6E\u5DF2\u662F\u6700\u65B0\u7248\u672C (${syncState.configVersion})`
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
const config = await getConfig();
|
|
256
|
-
for (const [filePath, content] of Object.entries(res.data.files)) {
|
|
257
|
-
const fullPath = join2(config.workflowDir, filePath);
|
|
258
|
-
const dir = fullPath.substring(0, fullPath.lastIndexOf("/"));
|
|
259
|
-
await mkdir2(dir, { recursive: true });
|
|
260
|
-
await writeFile2(fullPath, content, "utf-8");
|
|
261
|
-
}
|
|
262
|
-
await saveSyncState({ configVersion: res.data.version });
|
|
263
|
-
return {
|
|
264
|
-
success: true,
|
|
265
|
-
message: `\u914D\u7F6E\u5DF2\u66F4\u65B0: ${syncState.configVersion} \u2192 ${res.data.version}
|
|
266
|
-
\u53D8\u66F4: ${res.data.changelog}`,
|
|
267
|
-
data: { version: res.data.version, changelog: res.data.changelog }
|
|
268
|
-
};
|
|
269
|
-
}
|
|
270
|
-
async function configVersion() {
|
|
271
|
-
const syncState = await getSyncState();
|
|
272
|
-
return {
|
|
273
|
-
success: true,
|
|
274
|
-
message: `\u5F53\u524D\u672C\u5730\u914D\u7F6E\u7248\u672C: ${syncState.configVersion}`,
|
|
275
|
-
data: { version: syncState.configVersion }
|
|
276
|
-
};
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// src/tools/source.ts
|
|
280
|
-
import { z as z2 } from "zod";
|
|
281
|
-
import { mkdir as mkdir3 } from "fs/promises";
|
|
282
|
-
import { join as join4 } from "path";
|
|
283
88
|
|
|
284
89
|
// src/lib/sources.ts
|
|
285
|
-
import { readFile as readFile2, writeFile as
|
|
286
|
-
import { join as
|
|
90
|
+
import { readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
91
|
+
import { join as join2, relative } from "path";
|
|
287
92
|
import matter from "gray-matter";
|
|
288
93
|
import { glob } from "glob";
|
|
289
94
|
async function readSourceFile(filePath) {
|
|
@@ -299,7 +104,7 @@ async function readSourceFile(filePath) {
|
|
|
299
104
|
}
|
|
300
105
|
async function writeSourceFile(filePath, meta, content) {
|
|
301
106
|
const output = matter.stringify(content, meta);
|
|
302
|
-
await
|
|
107
|
+
await writeFile2(filePath, output, "utf-8");
|
|
303
108
|
}
|
|
304
109
|
async function updateSourceTags(filePath, tags) {
|
|
305
110
|
const source = await readSourceFile(filePath);
|
|
@@ -308,64 +113,6 @@ async function updateSourceTags(filePath, tags) {
|
|
|
308
113
|
source.meta.taggedBy = "local-claude";
|
|
309
114
|
await writeSourceFile(filePath, source.meta, source.content);
|
|
310
115
|
}
|
|
311
|
-
async function listSources(options) {
|
|
312
|
-
const config = await getConfig();
|
|
313
|
-
const baseDir = options.folder ? join3(config.workflowDir, "sources", options.folder) : join3(config.workflowDir, "sources");
|
|
314
|
-
const files = await glob("**/*.md", { cwd: baseDir, absolute: true });
|
|
315
|
-
const sources = [];
|
|
316
|
-
for (const file of files) {
|
|
317
|
-
try {
|
|
318
|
-
const source = await readSourceFile(file);
|
|
319
|
-
if (options.tagged === true && (!source.meta.tags || source.meta.tags.length === 0)) {
|
|
320
|
-
continue;
|
|
321
|
-
}
|
|
322
|
-
if (options.tagged === false && source.meta.tags && source.meta.tags.length > 0) {
|
|
323
|
-
continue;
|
|
324
|
-
}
|
|
325
|
-
sources.push(source);
|
|
326
|
-
} catch {
|
|
327
|
-
continue;
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
sources.sort(
|
|
331
|
-
(a, b) => new Date(b.meta.published || 0).getTime() - new Date(a.meta.published || 0).getTime()
|
|
332
|
-
);
|
|
333
|
-
if (options.limit && options.limit > 0) {
|
|
334
|
-
return sources.slice(0, options.limit);
|
|
335
|
-
}
|
|
336
|
-
return sources;
|
|
337
|
-
}
|
|
338
|
-
async function searchSources(options) {
|
|
339
|
-
const all = await listSources({ tagged: true });
|
|
340
|
-
let results = all;
|
|
341
|
-
if (options.tags && options.tags.length > 0) {
|
|
342
|
-
results = results.filter(
|
|
343
|
-
(s) => options.tags.some((tag) => s.meta.tags?.includes(tag))
|
|
344
|
-
);
|
|
345
|
-
}
|
|
346
|
-
if (options.dateFrom) {
|
|
347
|
-
const from = new Date(options.dateFrom).getTime();
|
|
348
|
-
results = results.filter(
|
|
349
|
-
(s) => new Date(s.meta.published || 0).getTime() >= from
|
|
350
|
-
);
|
|
351
|
-
}
|
|
352
|
-
if (options.dateTo) {
|
|
353
|
-
const to = new Date(options.dateTo).getTime();
|
|
354
|
-
results = results.filter(
|
|
355
|
-
(s) => new Date(s.meta.published || 0).getTime() <= to
|
|
356
|
-
);
|
|
357
|
-
}
|
|
358
|
-
if (options.query) {
|
|
359
|
-
const q = options.query.toLowerCase();
|
|
360
|
-
results = results.filter(
|
|
361
|
-
(s) => s.meta.title.toLowerCase().includes(q) || s.content.toLowerCase().includes(q)
|
|
362
|
-
);
|
|
363
|
-
}
|
|
364
|
-
if (options.limit && options.limit > 0) {
|
|
365
|
-
return results.slice(0, options.limit);
|
|
366
|
-
}
|
|
367
|
-
return results;
|
|
368
|
-
}
|
|
369
116
|
async function addToTagQueue(file, title, source) {
|
|
370
117
|
const queue = await getTagQueue();
|
|
371
118
|
if (queue.queue.some((item) => item.file === file)) return;
|
|
@@ -410,13 +157,91 @@ async function markTagFailed(file) {
|
|
|
410
157
|
await saveTagQueue(queue);
|
|
411
158
|
}
|
|
412
159
|
|
|
160
|
+
// src/lib/state.ts
|
|
161
|
+
import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir2 } from "fs/promises";
|
|
162
|
+
import { join as join3 } from "path";
|
|
163
|
+
var DEFAULT_STATE = {
|
|
164
|
+
sources: {},
|
|
165
|
+
drafts: {},
|
|
166
|
+
published: {},
|
|
167
|
+
configs: {},
|
|
168
|
+
crawlerSources: {},
|
|
169
|
+
lastSyncAt: ""
|
|
170
|
+
};
|
|
171
|
+
async function getStatePath() {
|
|
172
|
+
const config = await getConfig();
|
|
173
|
+
const dir = join3(config.workflowDir || process.cwd(), ".claudeink");
|
|
174
|
+
await mkdir2(dir, { recursive: true });
|
|
175
|
+
return join3(dir, "state.json");
|
|
176
|
+
}
|
|
177
|
+
async function readState() {
|
|
178
|
+
try {
|
|
179
|
+
const path = await getStatePath();
|
|
180
|
+
const content = await readFile3(path, "utf-8");
|
|
181
|
+
return { ...DEFAULT_STATE, ...JSON.parse(content) };
|
|
182
|
+
} catch {
|
|
183
|
+
return { ...DEFAULT_STATE };
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
async function writeState(state) {
|
|
187
|
+
const path = await getStatePath();
|
|
188
|
+
await writeFile3(path, JSON.stringify(state, null, 2), "utf-8");
|
|
189
|
+
}
|
|
190
|
+
async function updateSource(id, data) {
|
|
191
|
+
const state = await readState();
|
|
192
|
+
state.sources[id] = data;
|
|
193
|
+
await writeState(state);
|
|
194
|
+
}
|
|
195
|
+
async function updateSourceTags2(id, tags) {
|
|
196
|
+
const state = await readState();
|
|
197
|
+
if (state.sources[id]) {
|
|
198
|
+
state.sources[id].tags = tags;
|
|
199
|
+
state.sources[id].updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
200
|
+
}
|
|
201
|
+
await writeState(state);
|
|
202
|
+
}
|
|
203
|
+
async function updateDraft(id, data) {
|
|
204
|
+
const state = await readState();
|
|
205
|
+
state.drafts[id] = data;
|
|
206
|
+
await writeState(state);
|
|
207
|
+
}
|
|
208
|
+
async function moveDraftToPublished(id, data) {
|
|
209
|
+
const state = await readState();
|
|
210
|
+
delete state.drafts[id];
|
|
211
|
+
state.published[id] = data;
|
|
212
|
+
await writeState(state);
|
|
213
|
+
}
|
|
214
|
+
async function updateConfig(key, data) {
|
|
215
|
+
const state = await readState();
|
|
216
|
+
state.configs[key] = data;
|
|
217
|
+
await writeState(state);
|
|
218
|
+
}
|
|
219
|
+
async function updateCrawlerSource(id, data) {
|
|
220
|
+
const state = await readState();
|
|
221
|
+
state.crawlerSources[id] = data;
|
|
222
|
+
await writeState(state);
|
|
223
|
+
}
|
|
224
|
+
async function removeCrawlerSource(id) {
|
|
225
|
+
const state = await readState();
|
|
226
|
+
delete state.crawlerSources[id];
|
|
227
|
+
await writeState(state);
|
|
228
|
+
}
|
|
229
|
+
async function updateLastSyncAt() {
|
|
230
|
+
const state = await readState();
|
|
231
|
+
state.lastSyncAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
232
|
+
await writeState(state);
|
|
233
|
+
}
|
|
234
|
+
async function replaceState(state) {
|
|
235
|
+
await writeState(state);
|
|
236
|
+
}
|
|
237
|
+
|
|
413
238
|
// src/tools/source.ts
|
|
414
|
-
var sourceAddSchema =
|
|
415
|
-
title:
|
|
416
|
-
content:
|
|
417
|
-
url:
|
|
418
|
-
tags:
|
|
419
|
-
folder:
|
|
239
|
+
var sourceAddSchema = z.object({
|
|
240
|
+
title: z.string().describe("\u7D20\u6750\u6807\u9898"),
|
|
241
|
+
content: z.string().describe("\u7D20\u6750\u6B63\u6587"),
|
|
242
|
+
url: z.string().optional().describe("\u6765\u6E90 URL"),
|
|
243
|
+
tags: z.array(z.string()).optional().describe("\u6807\u7B7E\uFF08\u5982\u4E0D\u63D0\u4F9B\u5219\u52A0\u5165\u6807\u7B7E\u961F\u5217\u7531 Claude \u540E\u7EED\u6253\u6807\u7B7E\uFF09"),
|
|
244
|
+
folder: z.string().optional().describe("\u5B58\u653E\u5B50\u76EE\u5F55\uFF0C\u5982 articles/techcrunch")
|
|
420
245
|
});
|
|
421
246
|
async function sourceAdd(input) {
|
|
422
247
|
const config = await getConfig();
|
|
@@ -438,6 +263,16 @@ async function sourceAdd(input) {
|
|
|
438
263
|
meta.taggedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
439
264
|
meta.taggedBy = "local-claude";
|
|
440
265
|
await writeSourceFile(filePath, meta, input.content);
|
|
266
|
+
await updateSource(slug, {
|
|
267
|
+
title: input.title,
|
|
268
|
+
source: meta.source,
|
|
269
|
+
sourceIcon: "",
|
|
270
|
+
sourceUrl: input.url || "",
|
|
271
|
+
coverUrl: "",
|
|
272
|
+
tags: input.tags,
|
|
273
|
+
publishedAt: date,
|
|
274
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
275
|
+
});
|
|
441
276
|
return {
|
|
442
277
|
success: true,
|
|
443
278
|
message: `\u7D20\u6750\u5DF2\u5165\u5E93: ${filename}\uFF08\u542B ${input.tags.length} \u4E2A\u6807\u7B7E\uFF09`,
|
|
@@ -447,6 +282,16 @@ async function sourceAdd(input) {
|
|
|
447
282
|
await writeSourceFile(filePath, meta, input.content);
|
|
448
283
|
const relativePath = `sources/${folder}/${filename}`;
|
|
449
284
|
await addToTagQueue(relativePath, input.title, "manual");
|
|
285
|
+
await updateSource(slug, {
|
|
286
|
+
title: input.title,
|
|
287
|
+
source: meta.source,
|
|
288
|
+
sourceIcon: "",
|
|
289
|
+
sourceUrl: input.url || "",
|
|
290
|
+
coverUrl: "",
|
|
291
|
+
tags: [],
|
|
292
|
+
publishedAt: date,
|
|
293
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
294
|
+
});
|
|
450
295
|
return {
|
|
451
296
|
success: true,
|
|
452
297
|
message: `\u7D20\u6750\u5DF2\u5165\u5E93: ${filename}\uFF08\u5DF2\u52A0\u5165\u6807\u7B7E\u961F\u5217\uFF0C\u7B49\u5F85 Claude \u6253\u6807\u7B7E\uFF09`,
|
|
@@ -454,64 +299,15 @@ async function sourceAdd(input) {
|
|
|
454
299
|
};
|
|
455
300
|
}
|
|
456
301
|
}
|
|
457
|
-
var
|
|
458
|
-
|
|
459
|
-
tags: z2.array(z2.string()).optional().describe("\u6309\u6807\u7B7E\u7B5B\u9009"),
|
|
460
|
-
dateFrom: z2.string().optional().describe("\u8D77\u59CB\u65E5\u671F YYYY-MM-DD"),
|
|
461
|
-
dateTo: z2.string().optional().describe("\u622A\u6B62\u65E5\u671F YYYY-MM-DD"),
|
|
462
|
-
limit: z2.number().optional().describe("\u8FD4\u56DE\u6570\u91CF\u4E0A\u9650")
|
|
463
|
-
});
|
|
464
|
-
async function sourceSearch(input) {
|
|
465
|
-
const results = await searchSources({
|
|
466
|
-
query: input.query,
|
|
467
|
-
tags: input.tags,
|
|
468
|
-
dateFrom: input.dateFrom,
|
|
469
|
-
dateTo: input.dateTo,
|
|
470
|
-
limit: input.limit
|
|
471
|
-
});
|
|
472
|
-
return {
|
|
473
|
-
success: true,
|
|
474
|
-
message: `\u627E\u5230 ${results.length} \u7BC7\u76F8\u5173\u7D20\u6750`,
|
|
475
|
-
data: results.map((s) => ({
|
|
476
|
-
file: s.relativePath,
|
|
477
|
-
title: s.meta.title,
|
|
478
|
-
tags: s.meta.tags,
|
|
479
|
-
published: s.meta.published,
|
|
480
|
-
source: s.meta.source
|
|
481
|
-
}))
|
|
482
|
-
};
|
|
483
|
-
}
|
|
484
|
-
var sourceListSchema = z2.object({
|
|
485
|
-
folder: z2.string().optional().describe("\u5B50\u76EE\u5F55"),
|
|
486
|
-
tagged: z2.boolean().optional().describe("true=\u5DF2\u6253\u6807\u7B7E, false=\u672A\u6253\u6807\u7B7E, \u4E0D\u4F20=\u5168\u90E8"),
|
|
487
|
-
limit: z2.number().optional().describe("\u8FD4\u56DE\u6570\u91CF\u4E0A\u9650")
|
|
488
|
-
});
|
|
489
|
-
async function sourceList(input) {
|
|
490
|
-
const results = await listSources({
|
|
491
|
-
folder: input.folder,
|
|
492
|
-
tagged: input.tagged,
|
|
493
|
-
limit: input.limit
|
|
494
|
-
});
|
|
495
|
-
return {
|
|
496
|
-
success: true,
|
|
497
|
-
message: `\u5171 ${results.length} \u7BC7\u7D20\u6750`,
|
|
498
|
-
data: results.map((s) => ({
|
|
499
|
-
file: s.relativePath,
|
|
500
|
-
title: s.meta.title,
|
|
501
|
-
tags: s.meta.tags || [],
|
|
502
|
-
published: s.meta.published
|
|
503
|
-
}))
|
|
504
|
-
};
|
|
505
|
-
}
|
|
506
|
-
var sourceCrawlSchema = z2.object({
|
|
507
|
-
sourceId: z2.string().optional().describe("\u6307\u5B9A\u722C\u866B\u6E90\u540D\u79F0\uFF0C\u4E0D\u4F20\u5219\u5168\u91CF")
|
|
302
|
+
var sourceCrawlSchema = z.object({
|
|
303
|
+
sourceId: z.string().optional().describe("\u6307\u5B9A\u722C\u866B\u6E90\u540D\u79F0\uFF0C\u4E0D\u4F20\u5219\u5168\u91CF")
|
|
508
304
|
});
|
|
509
305
|
async function sourceCrawl(input) {
|
|
510
306
|
const schedules = await getCrawlSchedules();
|
|
511
307
|
if (schedules.schedules.length === 0) {
|
|
512
308
|
return {
|
|
513
309
|
success: false,
|
|
514
|
-
message: "\u6682\u65E0\u914D\u7F6E\u722C\u866B\u6E90\u3002\u8BF7\u5148\u7528 source.
|
|
310
|
+
message: "\u6682\u65E0\u914D\u7F6E\u722C\u866B\u6E90\u3002\u8BF7\u5148\u7528 source.subscribe \u6DFB\u52A0\u3002"
|
|
515
311
|
};
|
|
516
312
|
}
|
|
517
313
|
const targets = input.sourceId ? schedules.schedules.filter((s) => s.name === input.sourceId) : schedules.schedules.filter((s) => s.enabled);
|
|
@@ -527,63 +323,9 @@ async function sourceCrawl(input) {
|
|
|
527
323
|
data: { triggered: targets.map((t) => t.name) }
|
|
528
324
|
};
|
|
529
325
|
}
|
|
530
|
-
var
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
url: z2.string().optional().describe("RSS/Sitemap URL"),
|
|
534
|
-
type: z2.enum(["rss", "sitemap", "custom"]).optional().describe("\u6E90\u7C7B\u578B"),
|
|
535
|
-
cron: z2.string().optional().describe("Cron \u8868\u8FBE\u5F0F"),
|
|
536
|
-
targetFolder: z2.string().optional().describe("\u76EE\u6807\u5B58\u653E\u76EE\u5F55")
|
|
537
|
-
});
|
|
538
|
-
async function sourceSchedule(input) {
|
|
539
|
-
const schedules = await getCrawlSchedules();
|
|
540
|
-
if (input.action === "list") {
|
|
541
|
-
return {
|
|
542
|
-
success: true,
|
|
543
|
-
message: `\u5171 ${schedules.schedules.length} \u4E2A\u722C\u866B\u6E90`,
|
|
544
|
-
data: schedules.schedules
|
|
545
|
-
};
|
|
546
|
-
}
|
|
547
|
-
if (input.action === "add") {
|
|
548
|
-
if (!input.name || !input.url || !input.cron) {
|
|
549
|
-
return {
|
|
550
|
-
success: false,
|
|
551
|
-
message: "\u6DFB\u52A0\u722C\u866B\u6E90\u9700\u8981 name, url, cron \u53C2\u6570"
|
|
552
|
-
};
|
|
553
|
-
}
|
|
554
|
-
if (schedules.schedules.some((s) => s.name === input.name)) {
|
|
555
|
-
return { success: false, message: `\u722C\u866B\u6E90 ${input.name} \u5DF2\u5B58\u5728` };
|
|
556
|
-
}
|
|
557
|
-
schedules.schedules.push({
|
|
558
|
-
name: input.name,
|
|
559
|
-
url: input.url,
|
|
560
|
-
type: input.type || "rss",
|
|
561
|
-
cron: input.cron,
|
|
562
|
-
targetFolder: input.targetFolder || `sources/articles/${input.name}/`,
|
|
563
|
-
enabled: true,
|
|
564
|
-
lastRun: null
|
|
565
|
-
});
|
|
566
|
-
await saveCrawlSchedules(schedules);
|
|
567
|
-
return {
|
|
568
|
-
success: true,
|
|
569
|
-
message: `\u722C\u866B\u6E90 ${input.name} \u5DF2\u6DFB\u52A0\uFF0CCron: ${input.cron}`
|
|
570
|
-
};
|
|
571
|
-
}
|
|
572
|
-
if (input.action === "remove") {
|
|
573
|
-
if (!input.name) {
|
|
574
|
-
return { success: false, message: "\u5220\u9664\u722C\u866B\u6E90\u9700\u8981 name \u53C2\u6570" };
|
|
575
|
-
}
|
|
576
|
-
schedules.schedules = schedules.schedules.filter(
|
|
577
|
-
(s) => s.name !== input.name
|
|
578
|
-
);
|
|
579
|
-
await saveCrawlSchedules(schedules);
|
|
580
|
-
return { success: true, message: `\u722C\u866B\u6E90 ${input.name} \u5DF2\u5220\u9664` };
|
|
581
|
-
}
|
|
582
|
-
return { success: false, message: "\u672A\u77E5\u64CD\u4F5C" };
|
|
583
|
-
}
|
|
584
|
-
var sourceTagSchema = z2.object({
|
|
585
|
-
mode: z2.enum(["queue", "single"]).describe("queue=\u5904\u7406\u6807\u7B7E\u961F\u5217, single=\u5355\u4E2A\u6587\u4EF6\u6253\u6807\u7B7E"),
|
|
586
|
-
file: z2.string().optional().describe("single \u6A21\u5F0F\u9700\u8981\u6307\u5B9A\u6587\u4EF6\u8DEF\u5F84")
|
|
326
|
+
var sourceTagSchema = z.object({
|
|
327
|
+
mode: z.enum(["queue", "single"]).describe("queue=\u5904\u7406\u6807\u7B7E\u961F\u5217, single=\u5355\u4E2A\u6587\u4EF6\u6253\u6807\u7B7E"),
|
|
328
|
+
file: z.string().optional().describe("single \u6A21\u5F0F\u9700\u8981\u6307\u5B9A\u6587\u4EF6\u8DEF\u5F84")
|
|
587
329
|
});
|
|
588
330
|
async function sourceTag(input) {
|
|
589
331
|
if (input.mode === "single") {
|
|
@@ -651,23 +393,9 @@ async function sourceTag(input) {
|
|
|
651
393
|
}
|
|
652
394
|
};
|
|
653
395
|
}
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
success: true,
|
|
658
|
-
message: `\u5F85\u5904\u7406: ${queue.queue.length} \u7BC7 | \u5931\u8D25: ${queue.failed.length} \u7BC7 | \u7D2F\u8BA1\u5DF2\u5904\u7406: ${queue.history.totalProcessed} \u7BC7 | \u5E73\u5747\u6807\u7B7E\u6570: ${queue.history.avgTagsPerItem.toFixed(1)}`,
|
|
659
|
-
data: {
|
|
660
|
-
pending: queue.queue.length,
|
|
661
|
-
failed: queue.failed.length,
|
|
662
|
-
totalProcessed: queue.history.totalProcessed,
|
|
663
|
-
avgTagsPerItem: queue.history.avgTagsPerItem,
|
|
664
|
-
lastProcessed: queue.history.lastProcessed
|
|
665
|
-
}
|
|
666
|
-
};
|
|
667
|
-
}
|
|
668
|
-
var sourceTagApplySchema = z2.object({
|
|
669
|
-
file: z2.string().describe("\u7D20\u6750\u6587\u4EF6\u8DEF\u5F84"),
|
|
670
|
-
tags: z2.array(z2.string()).describe("Claude \u751F\u6210\u7684\u6807\u7B7E")
|
|
396
|
+
var sourceTagApplySchema = z.object({
|
|
397
|
+
file: z.string().describe("\u7D20\u6750\u6587\u4EF6\u8DEF\u5F84"),
|
|
398
|
+
tags: z.array(z.string()).describe("Claude \u751F\u6210\u7684\u6807\u7B7E")
|
|
671
399
|
});
|
|
672
400
|
async function sourceTagApply(input) {
|
|
673
401
|
try {
|
|
@@ -675,6 +403,7 @@ async function sourceTagApply(input) {
|
|
|
675
403
|
const fullPath = input.file.startsWith("/") ? input.file : join4(config.workflowDir, input.file);
|
|
676
404
|
await updateSourceTags(fullPath, input.tags);
|
|
677
405
|
await markTagged(input.file, input.tags.length);
|
|
406
|
+
await updateSourceTags2(input.file, input.tags);
|
|
678
407
|
return {
|
|
679
408
|
success: true,
|
|
680
409
|
message: `\u6807\u7B7E\u5DF2\u5199\u5165: ${input.tags.join(", ")}`,
|
|
@@ -689,26 +418,92 @@ async function sourceTagApply(input) {
|
|
|
689
418
|
}
|
|
690
419
|
}
|
|
691
420
|
|
|
421
|
+
// src/tools/subscribe.ts
|
|
422
|
+
import { z as z2 } from "zod";
|
|
423
|
+
import { readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
|
|
424
|
+
import { join as join5 } from "path";
|
|
425
|
+
var sourceSubscribeSchema = z2.object({
|
|
426
|
+
action: z2.enum(["add", "remove", "list"]).describe("\u64CD\u4F5C\u7C7B\u578B"),
|
|
427
|
+
id: z2.string().optional().describe("\u8BA2\u9605\u6E90 ID"),
|
|
428
|
+
name: z2.string().optional().describe("\u8BA2\u9605\u6E90\u540D\u79F0"),
|
|
429
|
+
url: z2.string().optional().describe("RSS/\u535A\u5BA2 URL"),
|
|
430
|
+
type: z2.enum(["rss", "blog", "sitemap"]).optional().describe("\u6E90\u7C7B\u578B"),
|
|
431
|
+
icon: z2.string().optional().describe("\u6765\u6E90 icon URL")
|
|
432
|
+
});
|
|
433
|
+
async function sourceSubscribe(input) {
|
|
434
|
+
const config = await getConfig();
|
|
435
|
+
const configPath = join5(config.workflowDir || process.cwd(), "tools/crawler/config.json");
|
|
436
|
+
let crawlerConfig;
|
|
437
|
+
try {
|
|
438
|
+
crawlerConfig = JSON.parse(await readFile4(configPath, "utf-8"));
|
|
439
|
+
} catch {
|
|
440
|
+
crawlerConfig = { sources: [] };
|
|
441
|
+
}
|
|
442
|
+
if (input.action === "list") {
|
|
443
|
+
return {
|
|
444
|
+
success: true,
|
|
445
|
+
message: `\u5171 ${crawlerConfig.sources.length} \u4E2A\u8BA2\u9605\u6E90`,
|
|
446
|
+
data: crawlerConfig.sources
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
if (input.action === "add") {
|
|
450
|
+
if (!input.id || !input.name || !input.url) {
|
|
451
|
+
return { success: false, message: "\u6DFB\u52A0\u8BA2\u9605\u6E90\u9700\u8981 id, name, url \u53C2\u6570" };
|
|
452
|
+
}
|
|
453
|
+
if (crawlerConfig.sources.some((s) => s.id === input.id)) {
|
|
454
|
+
return { success: false, message: `\u8BA2\u9605\u6E90 ${input.id} \u5DF2\u5B58\u5728` };
|
|
455
|
+
}
|
|
456
|
+
const newSource = {
|
|
457
|
+
id: input.id,
|
|
458
|
+
name: input.name,
|
|
459
|
+
url: input.url,
|
|
460
|
+
type: input.type || "rss",
|
|
461
|
+
icon: input.icon || "",
|
|
462
|
+
enabled: true
|
|
463
|
+
};
|
|
464
|
+
crawlerConfig.sources.push(newSource);
|
|
465
|
+
await writeFile4(configPath, JSON.stringify(crawlerConfig, null, 2));
|
|
466
|
+
await updateCrawlerSource(input.id, {
|
|
467
|
+
name: input.name,
|
|
468
|
+
url: input.url,
|
|
469
|
+
type: input.type || "rss",
|
|
470
|
+
icon: input.icon || "",
|
|
471
|
+
enabled: true
|
|
472
|
+
});
|
|
473
|
+
return { success: true, message: `\u8BA2\u9605\u6E90 ${input.name} \u5DF2\u6DFB\u52A0` };
|
|
474
|
+
}
|
|
475
|
+
if (input.action === "remove") {
|
|
476
|
+
if (!input.id) {
|
|
477
|
+
return { success: false, message: "\u5220\u9664\u8BA2\u9605\u6E90\u9700\u8981 id \u53C2\u6570" };
|
|
478
|
+
}
|
|
479
|
+
crawlerConfig.sources = crawlerConfig.sources.filter((s) => s.id !== input.id);
|
|
480
|
+
await writeFile4(configPath, JSON.stringify(crawlerConfig, null, 2));
|
|
481
|
+
await removeCrawlerSource(input.id);
|
|
482
|
+
return { success: true, message: `\u8BA2\u9605\u6E90 ${input.id} \u5DF2\u5220\u9664` };
|
|
483
|
+
}
|
|
484
|
+
return { success: false, message: "\u672A\u77E5\u64CD\u4F5C" };
|
|
485
|
+
}
|
|
486
|
+
|
|
692
487
|
// src/tools/draft.ts
|
|
693
488
|
import { z as z3 } from "zod";
|
|
694
489
|
|
|
695
490
|
// src/lib/drafts.ts
|
|
696
|
-
import { readFile as
|
|
697
|
-
import { join as
|
|
491
|
+
import { readFile as readFile5, writeFile as writeFile5, mkdir as mkdir4, unlink } from "fs/promises";
|
|
492
|
+
import { join as join6, basename } from "path";
|
|
698
493
|
import matter2 from "gray-matter";
|
|
699
494
|
import { glob as glob2 } from "glob";
|
|
700
495
|
import { randomUUID } from "crypto";
|
|
701
496
|
function getDraftDir(account, workflowDir) {
|
|
702
497
|
if (account.toLowerCase() === "auston") {
|
|
703
|
-
return
|
|
498
|
+
return join6(workflowDir, "drafts");
|
|
704
499
|
}
|
|
705
|
-
return
|
|
500
|
+
return join6(workflowDir, "accounts", account, "drafts");
|
|
706
501
|
}
|
|
707
502
|
function getPublishedDir(account, workflowDir) {
|
|
708
503
|
if (account.toLowerCase() === "auston") {
|
|
709
|
-
return
|
|
504
|
+
return join6(workflowDir, "published");
|
|
710
505
|
}
|
|
711
|
-
return
|
|
506
|
+
return join6(workflowDir, "accounts", account, "published");
|
|
712
507
|
}
|
|
713
508
|
function generateDraftFilename(title) {
|
|
714
509
|
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
@@ -721,7 +516,7 @@ async function saveDraft(options) {
|
|
|
721
516
|
await mkdir4(dir, { recursive: true });
|
|
722
517
|
const id = randomUUID().slice(0, 8);
|
|
723
518
|
const filename = generateDraftFilename(options.title);
|
|
724
|
-
const filePath =
|
|
519
|
+
const filePath = join6(dir, filename);
|
|
725
520
|
const meta = {
|
|
726
521
|
id,
|
|
727
522
|
account: options.account,
|
|
@@ -737,62 +532,8 @@ async function saveDraft(options) {
|
|
|
737
532
|
await writeFile5(filePath, output, "utf-8");
|
|
738
533
|
return { filePath, meta, content: options.content };
|
|
739
534
|
}
|
|
740
|
-
async function listDrafts(options) {
|
|
741
|
-
const config = await getConfig();
|
|
742
|
-
const drafts = [];
|
|
743
|
-
const dirs = [];
|
|
744
|
-
if (options?.account) {
|
|
745
|
-
dirs.push(getDraftDir(options.account, config.workflowDir));
|
|
746
|
-
} else {
|
|
747
|
-
dirs.push(join5(config.workflowDir, "drafts"));
|
|
748
|
-
const accountGlob = await glob2("accounts/*/drafts", {
|
|
749
|
-
cwd: config.workflowDir,
|
|
750
|
-
absolute: true
|
|
751
|
-
});
|
|
752
|
-
dirs.push(...accountGlob);
|
|
753
|
-
}
|
|
754
|
-
for (const dir of dirs) {
|
|
755
|
-
try {
|
|
756
|
-
const files = await glob2("*.md", { cwd: dir, absolute: true });
|
|
757
|
-
for (const file of files) {
|
|
758
|
-
try {
|
|
759
|
-
const raw = await readFile3(file, "utf-8");
|
|
760
|
-
const { data, content } = matter2(raw);
|
|
761
|
-
const meta = data;
|
|
762
|
-
if (options?.status && meta.status !== options.status) continue;
|
|
763
|
-
drafts.push({ filePath: file, meta, content: content.trim() });
|
|
764
|
-
} catch {
|
|
765
|
-
continue;
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
} catch {
|
|
769
|
-
continue;
|
|
770
|
-
}
|
|
771
|
-
}
|
|
772
|
-
drafts.sort(
|
|
773
|
-
(a, b) => new Date(b.meta.updatedAt).getTime() - new Date(a.meta.updatedAt).getTime()
|
|
774
|
-
);
|
|
775
|
-
return drafts;
|
|
776
|
-
}
|
|
777
|
-
async function updateDraft(options) {
|
|
778
|
-
const raw = await readFile3(options.file, "utf-8");
|
|
779
|
-
const { data, content } = matter2(raw);
|
|
780
|
-
const meta = data;
|
|
781
|
-
if (options.title) meta.title = options.title;
|
|
782
|
-
if (options.status) meta.status = options.status;
|
|
783
|
-
if (options.tags) meta.tags = options.tags;
|
|
784
|
-
const newContent = options.content ?? content.trim();
|
|
785
|
-
meta.wordCount = newContent.length;
|
|
786
|
-
meta.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
787
|
-
const output = matter2.stringify(newContent, meta);
|
|
788
|
-
await writeFile5(options.file, output, "utf-8");
|
|
789
|
-
return { filePath: options.file, meta, content: newContent };
|
|
790
|
-
}
|
|
791
|
-
async function deleteDraft(file) {
|
|
792
|
-
await unlink(file);
|
|
793
|
-
}
|
|
794
535
|
async function publishDraft(options) {
|
|
795
|
-
const raw = await
|
|
536
|
+
const raw = await readFile5(options.file, "utf-8");
|
|
796
537
|
const { data, content } = matter2(raw);
|
|
797
538
|
const meta = data;
|
|
798
539
|
meta.status = "published";
|
|
@@ -802,7 +543,7 @@ async function publishDraft(options) {
|
|
|
802
543
|
const config = await getConfig();
|
|
803
544
|
const publishedDir = getPublishedDir(meta.account, config.workflowDir);
|
|
804
545
|
await mkdir4(publishedDir, { recursive: true });
|
|
805
|
-
const newPath =
|
|
546
|
+
const newPath = join6(publishedDir, basename(options.file));
|
|
806
547
|
const output = matter2.stringify(content.trim(), meta);
|
|
807
548
|
await writeFile5(newPath, output, "utf-8");
|
|
808
549
|
await unlink(options.file);
|
|
@@ -827,6 +568,16 @@ async function draftSave(input) {
|
|
|
827
568
|
tags: input.tags,
|
|
828
569
|
status: input.status
|
|
829
570
|
});
|
|
571
|
+
await updateDraft(draft.meta.id, {
|
|
572
|
+
title: draft.meta.title,
|
|
573
|
+
account: input.account,
|
|
574
|
+
platform: input.platform,
|
|
575
|
+
status: draft.meta.status,
|
|
576
|
+
wordCount: draft.meta.wordCount,
|
|
577
|
+
tags: input.tags || [],
|
|
578
|
+
createdAt: draft.meta.createdAt,
|
|
579
|
+
updatedAt: draft.meta.updatedAt
|
|
580
|
+
});
|
|
830
581
|
return {
|
|
831
582
|
success: true,
|
|
832
583
|
message: `\u8349\u7A3F\u5DF2\u4FDD\u5B58: ${draft.meta.title} (${draft.meta.wordCount} \u5B57)`,
|
|
@@ -837,73 +588,21 @@ async function draftSave(input) {
|
|
|
837
588
|
}
|
|
838
589
|
};
|
|
839
590
|
}
|
|
840
|
-
var
|
|
841
|
-
|
|
842
|
-
|
|
591
|
+
var draftPublishSchema = z3.object({
|
|
592
|
+
file: z3.string().describe("\u8349\u7A3F\u6587\u4EF6\u8DEF\u5F84"),
|
|
593
|
+
platformUrl: z3.string().optional().describe("\u5E73\u53F0\u53D1\u5E03\u94FE\u63A5")
|
|
843
594
|
});
|
|
844
|
-
async function
|
|
845
|
-
const
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
});
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
account: d.meta.account,
|
|
856
|
-
platform: d.meta.platform,
|
|
857
|
-
title: d.meta.title,
|
|
858
|
-
status: d.meta.status,
|
|
859
|
-
wordCount: d.meta.wordCount,
|
|
860
|
-
updatedAt: d.meta.updatedAt
|
|
861
|
-
}))
|
|
862
|
-
};
|
|
863
|
-
}
|
|
864
|
-
var draftUpdateSchema = z3.object({
|
|
865
|
-
file: z3.string().describe("\u8349\u7A3F\u6587\u4EF6\u8DEF\u5F84"),
|
|
866
|
-
content: z3.string().optional().describe("\u66F4\u65B0\u7684\u6B63\u6587"),
|
|
867
|
-
title: z3.string().optional().describe("\u66F4\u65B0\u7684\u6807\u9898"),
|
|
868
|
-
status: z3.enum(["draft", "review", "ready"]).optional().describe("\u66F4\u65B0\u72B6\u6001"),
|
|
869
|
-
tags: z3.array(z3.string()).optional().describe("\u66F4\u65B0\u6807\u7B7E")
|
|
870
|
-
});
|
|
871
|
-
async function draftUpdate(input) {
|
|
872
|
-
const draft = await updateDraft({
|
|
873
|
-
file: input.file,
|
|
874
|
-
content: input.content,
|
|
875
|
-
title: input.title,
|
|
876
|
-
status: input.status,
|
|
877
|
-
tags: input.tags
|
|
878
|
-
});
|
|
879
|
-
return {
|
|
880
|
-
success: true,
|
|
881
|
-
message: `\u8349\u7A3F\u5DF2\u66F4\u65B0: ${draft.meta.title}`,
|
|
882
|
-
data: {
|
|
883
|
-
file: draft.filePath,
|
|
884
|
-
status: draft.meta.status,
|
|
885
|
-
wordCount: draft.meta.wordCount
|
|
886
|
-
}
|
|
887
|
-
};
|
|
888
|
-
}
|
|
889
|
-
var draftDeleteSchema = z3.object({
|
|
890
|
-
file: z3.string().describe("\u8349\u7A3F\u6587\u4EF6\u8DEF\u5F84")
|
|
891
|
-
});
|
|
892
|
-
async function draftDelete(input) {
|
|
893
|
-
await deleteDraft(input.file);
|
|
894
|
-
return {
|
|
895
|
-
success: true,
|
|
896
|
-
message: `\u8349\u7A3F\u5DF2\u5220\u9664: ${input.file}`
|
|
897
|
-
};
|
|
898
|
-
}
|
|
899
|
-
var draftPublishSchema = z3.object({
|
|
900
|
-
file: z3.string().describe("\u8349\u7A3F\u6587\u4EF6\u8DEF\u5F84"),
|
|
901
|
-
platformUrl: z3.string().optional().describe("\u5E73\u53F0\u53D1\u5E03\u94FE\u63A5")
|
|
902
|
-
});
|
|
903
|
-
async function draftPublish(input) {
|
|
904
|
-
const draft = await publishDraft({
|
|
905
|
-
file: input.file,
|
|
906
|
-
platformUrl: input.platformUrl
|
|
595
|
+
async function draftPublish(input) {
|
|
596
|
+
const draft = await publishDraft({
|
|
597
|
+
file: input.file,
|
|
598
|
+
platformUrl: input.platformUrl
|
|
599
|
+
});
|
|
600
|
+
await moveDraftToPublished(draft.meta.id, {
|
|
601
|
+
title: draft.meta.title,
|
|
602
|
+
account: draft.meta.account,
|
|
603
|
+
platform: draft.meta.platform,
|
|
604
|
+
platformUrl: draft.meta.platformUrl || "",
|
|
605
|
+
publishedAt: draft.meta.publishedAt || (/* @__PURE__ */ new Date()).toISOString()
|
|
907
606
|
});
|
|
908
607
|
return {
|
|
909
608
|
success: true,
|
|
@@ -918,6 +617,52 @@ async function draftPublish(input) {
|
|
|
918
617
|
|
|
919
618
|
// src/tools/analytics.ts
|
|
920
619
|
import { z as z4 } from "zod";
|
|
620
|
+
|
|
621
|
+
// src/lib/api-client.ts
|
|
622
|
+
async function request(path, options = {}) {
|
|
623
|
+
const config = await getConfig();
|
|
624
|
+
const url = `${config.apiBaseUrl}${path}`;
|
|
625
|
+
try {
|
|
626
|
+
const headers = {
|
|
627
|
+
"Content-Type": "application/json",
|
|
628
|
+
"User-Agent": "ClaudeInk-MCP/0.1.0"
|
|
629
|
+
};
|
|
630
|
+
if (options.token) {
|
|
631
|
+
headers["Authorization"] = `Bearer ${options.token}`;
|
|
632
|
+
} else {
|
|
633
|
+
const creds = await getCredentials();
|
|
634
|
+
if (creds?.token) {
|
|
635
|
+
headers["Authorization"] = `Bearer ${creds.token}`;
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
const res = await fetch(url, {
|
|
639
|
+
method: options.method || "GET",
|
|
640
|
+
headers,
|
|
641
|
+
body: options.body ? JSON.stringify(options.body) : void 0
|
|
642
|
+
});
|
|
643
|
+
const data = await res.json().catch(() => null);
|
|
644
|
+
return {
|
|
645
|
+
ok: res.ok,
|
|
646
|
+
status: res.status,
|
|
647
|
+
data: data ?? void 0,
|
|
648
|
+
error: res.ok ? void 0 : `HTTP ${res.status}`
|
|
649
|
+
};
|
|
650
|
+
} catch (err) {
|
|
651
|
+
return {
|
|
652
|
+
ok: false,
|
|
653
|
+
status: 0,
|
|
654
|
+
error: err instanceof Error ? err.message : "Unknown error"
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
async function pushSyncBatch(payload) {
|
|
659
|
+
return request("/api/sync/batch", {
|
|
660
|
+
method: "POST",
|
|
661
|
+
body: payload
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
// src/tools/analytics.ts
|
|
921
666
|
var analyticsPushSchema = z4.object({
|
|
922
667
|
articleId: z4.string().describe("\u6587\u7AE0 ID"),
|
|
923
668
|
metrics: z4.object({
|
|
@@ -970,108 +715,51 @@ async function analyticsReport(input) {
|
|
|
970
715
|
|
|
971
716
|
// src/tools/account.ts
|
|
972
717
|
import { z as z5 } from "zod";
|
|
973
|
-
import { readFile as
|
|
974
|
-
import { join as
|
|
975
|
-
import { glob as glob3 } from "glob";
|
|
976
|
-
async function accountList() {
|
|
977
|
-
const config = await getConfig();
|
|
978
|
-
const yamlFiles = await glob3("accounts/*.yaml", {
|
|
979
|
-
cwd: config.workflowDir,
|
|
980
|
-
absolute: true
|
|
981
|
-
});
|
|
982
|
-
const accounts = [];
|
|
983
|
-
for (const file of yamlFiles) {
|
|
984
|
-
if (file.endsWith("_template.yaml")) continue;
|
|
985
|
-
try {
|
|
986
|
-
const content = await readFile4(file, "utf-8");
|
|
987
|
-
const name = extractYamlField(content, "name");
|
|
988
|
-
const platform = extractYamlField(content, "platform");
|
|
989
|
-
const desc = extractYamlField(content, "description");
|
|
990
|
-
accounts.push({ name, platform, description: desc, file });
|
|
991
|
-
} catch {
|
|
992
|
-
continue;
|
|
993
|
-
}
|
|
994
|
-
}
|
|
995
|
-
return {
|
|
996
|
-
success: true,
|
|
997
|
-
message: `\u5171 ${accounts.length} \u4E2A\u8D26\u53F7`,
|
|
998
|
-
data: accounts
|
|
999
|
-
};
|
|
1000
|
-
}
|
|
1001
|
-
var accountSwitchSchema = z5.object({
|
|
1002
|
-
name: z5.string().describe("\u8981\u5207\u6362\u5230\u7684\u8D26\u53F7\u540D\u79F0")
|
|
1003
|
-
});
|
|
1004
|
-
async function accountSwitch(input) {
|
|
1005
|
-
const config = await getConfig();
|
|
1006
|
-
const yamlPath = join6(config.workflowDir, "accounts", `${input.name}.yaml`);
|
|
1007
|
-
try {
|
|
1008
|
-
const content = await readFile4(yamlPath, "utf-8");
|
|
1009
|
-
const platform = extractYamlField(content, "platform");
|
|
1010
|
-
let platformRules = "";
|
|
1011
|
-
if (platform) {
|
|
1012
|
-
try {
|
|
1013
|
-
platformRules = await readFile4(
|
|
1014
|
-
join6(config.workflowDir, "platforms", `${platform}.md`),
|
|
1015
|
-
"utf-8"
|
|
1016
|
-
);
|
|
1017
|
-
} catch {
|
|
1018
|
-
platformRules = "[\u5E73\u53F0\u89C4\u5219\u6587\u4EF6\u4E0D\u5B58\u5728]";
|
|
1019
|
-
}
|
|
1020
|
-
}
|
|
1021
|
-
return {
|
|
1022
|
-
success: true,
|
|
1023
|
-
message: `\u5DF2\u5207\u6362\u5230\u8D26\u53F7: ${input.name} (${platform || "\u672A\u77E5\u5E73\u53F0"})\u3002\u8BF7\u6309\u987A\u5E8F\u52A0\u8F7D\uFF1A
|
|
1024
|
-
1. base-rules.md
|
|
1025
|
-
2. platforms/${platform}.md
|
|
1026
|
-
3. accounts/${input.name}.yaml`,
|
|
1027
|
-
data: {
|
|
1028
|
-
account: input.name,
|
|
1029
|
-
platform,
|
|
1030
|
-
configFile: yamlPath,
|
|
1031
|
-
platformRulesLoaded: platformRules.length > 0
|
|
1032
|
-
}
|
|
1033
|
-
};
|
|
1034
|
-
} catch {
|
|
1035
|
-
return {
|
|
1036
|
-
success: false,
|
|
1037
|
-
message: `\u8D26\u53F7 ${input.name} \u4E0D\u5B58\u5728\u3002\u8BF7\u68C0\u67E5 accounts/ \u76EE\u5F55\u3002`
|
|
1038
|
-
};
|
|
1039
|
-
}
|
|
1040
|
-
}
|
|
718
|
+
import { readFile as readFile6, writeFile as writeFile6, mkdir as mkdir5 } from "fs/promises";
|
|
719
|
+
import { join as join7 } from "path";
|
|
1041
720
|
var accountCreateSchema = z5.object({
|
|
1042
721
|
name: z5.string().describe("\u8D26\u53F7\u540D\u79F0\uFF08\u82F1\u6587\u5C0F\u5199\uFF0C\u7528\u4F5C\u6587\u4EF6\u540D\uFF09"),
|
|
1043
722
|
platform: z5.string().describe("\u5E73\u53F0\u540D\u79F0\uFF0C\u5982 wechat, xiaohongshu"),
|
|
1044
723
|
displayName: z5.string().optional().describe("\u663E\u793A\u540D\u79F0"),
|
|
1045
724
|
domains: z5.array(z5.string()).describe("\u5185\u5BB9\u9886\u57DF"),
|
|
1046
|
-
description: z5.string().optional().describe("\u8D26\u53F7\u5B9A\u4F4D\u63CF\u8FF0")
|
|
725
|
+
description: z5.string().optional().describe("\u8D26\u53F7\u5B9A\u4F4D\u63CF\u8FF0"),
|
|
726
|
+
profileUrl: z5.string().optional().describe("\u8D26\u53F7\u5728\u5E73\u53F0\u4E0A\u7684\u4E3B\u9875 URL")
|
|
1047
727
|
});
|
|
1048
728
|
async function accountCreate(input) {
|
|
1049
729
|
const config = await getConfig();
|
|
1050
|
-
const yamlPath =
|
|
730
|
+
const yamlPath = join7(config.workflowDir, "accounts", `${input.name}.yaml`);
|
|
1051
731
|
try {
|
|
1052
|
-
await
|
|
732
|
+
await readFile6(yamlPath);
|
|
1053
733
|
return { success: false, message: `\u8D26\u53F7 ${input.name} \u5DF2\u5B58\u5728` };
|
|
1054
734
|
} catch {
|
|
1055
735
|
}
|
|
1056
736
|
let template = "";
|
|
1057
737
|
try {
|
|
1058
|
-
template = await
|
|
1059
|
-
|
|
738
|
+
template = await readFile6(
|
|
739
|
+
join7(config.workflowDir, "accounts", "_template.yaml"),
|
|
1060
740
|
"utf-8"
|
|
1061
741
|
);
|
|
1062
742
|
} catch {
|
|
1063
743
|
template = defaultTemplate();
|
|
1064
744
|
}
|
|
1065
|
-
const yamlContent = template.replace(/name:\s*""/, `name: "${input.displayName || input.name}"`).replace(/platform:\s*""/, `platform: "${input.platform}"`).replace(/description:\s*""/, `description: "${input.description || ""}"`).replace(
|
|
1066
|
-
/domains:\s*\
|
|
745
|
+
const yamlContent = template.replace(/name:\s*""/, `name: "${input.displayName || input.name}"`).replace(/id:\s*""/, `id: "${input.name}"`).replace(/platform:\s*""/, `platform: "${input.platform}"`).replace(/profile_url:\s*""/, `profile_url: "${input.profileUrl || ""}"`).replace(/description:\s*""/, `description: "${input.description || ""}"`).replace(
|
|
746
|
+
/domains:\s*\n\s*- ""/,
|
|
1067
747
|
`domains:
|
|
1068
748
|
${input.domains.map((d) => ` - "${d}"`).join("\n")}`
|
|
1069
749
|
);
|
|
1070
750
|
await writeFile6(yamlPath, yamlContent, "utf-8");
|
|
1071
|
-
const accountDir =
|
|
1072
|
-
await mkdir5(
|
|
1073
|
-
await mkdir5(
|
|
1074
|
-
await mkdir5(
|
|
751
|
+
const accountDir = join7(config.workflowDir, "accounts", input.name);
|
|
752
|
+
await mkdir5(join7(accountDir, "drafts"), { recursive: true });
|
|
753
|
+
await mkdir5(join7(accountDir, "published"), { recursive: true });
|
|
754
|
+
await mkdir5(join7(accountDir, "assets"), { recursive: true });
|
|
755
|
+
await updateConfig(`account:${input.name}`, {
|
|
756
|
+
type: "account",
|
|
757
|
+
name: input.name,
|
|
758
|
+
displayName: input.displayName || input.name,
|
|
759
|
+
content: yamlContent,
|
|
760
|
+
metadata: { platform: input.platform, profileUrl: input.profileUrl || "" },
|
|
761
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
762
|
+
});
|
|
1075
763
|
return {
|
|
1076
764
|
success: true,
|
|
1077
765
|
message: `\u8D26\u53F7 ${input.name} \u5DF2\u521B\u5EFA
|
|
@@ -1087,22 +775,16 @@ ${input.domains.map((d) => ` - "${d}"`).join("\n")}`
|
|
|
1087
775
|
}
|
|
1088
776
|
};
|
|
1089
777
|
}
|
|
1090
|
-
function extractYamlField(yaml, field) {
|
|
1091
|
-
const regex = new RegExp(`^${field}:\\s*["']?([^"'\\n]+)["']?`, "m");
|
|
1092
|
-
const match = yaml.match(regex);
|
|
1093
|
-
return match ? match[1].trim() : "";
|
|
1094
|
-
}
|
|
1095
778
|
function defaultTemplate() {
|
|
1096
779
|
return `# ClaudeInk \u8D26\u53F7\u914D\u7F6E
|
|
1097
780
|
name: ""
|
|
781
|
+
id: ""
|
|
1098
782
|
platform: ""
|
|
783
|
+
profile_url: ""
|
|
1099
784
|
description: ""
|
|
1100
785
|
|
|
1101
|
-
domains:
|
|
1102
|
-
|
|
1103
|
-
audience:
|
|
1104
|
-
age_range: ""
|
|
1105
|
-
interests: []
|
|
786
|
+
domains:
|
|
787
|
+
- ""
|
|
1106
788
|
|
|
1107
789
|
style:
|
|
1108
790
|
tone: "\u5E73\u5B9E"
|
|
@@ -1112,33 +794,185 @@ style:
|
|
|
1112
794
|
humor: "\u5076\u5C14"
|
|
1113
795
|
language: "zh-CN"
|
|
1114
796
|
|
|
1115
|
-
persona:
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
max_words: null
|
|
1120
|
-
paragraph_style: null
|
|
1121
|
-
|
|
1122
|
-
templates: []
|
|
797
|
+
persona:
|
|
798
|
+
role: ""
|
|
799
|
+
background: ""
|
|
800
|
+
first_person: "\u6211"
|
|
1123
801
|
|
|
1124
802
|
paths:
|
|
1125
|
-
drafts:
|
|
1126
|
-
published:
|
|
1127
|
-
assets:
|
|
803
|
+
drafts: "accounts/{id}/drafts/"
|
|
804
|
+
published: "accounts/{id}/published/"
|
|
805
|
+
assets: "accounts/{id}/assets/"
|
|
1128
806
|
`;
|
|
1129
807
|
}
|
|
1130
808
|
|
|
1131
|
-
// src/tools/
|
|
809
|
+
// src/tools/sync.ts
|
|
1132
810
|
import { z as z6 } from "zod";
|
|
1133
|
-
import {
|
|
1134
|
-
import { join as
|
|
811
|
+
import { writeFile as writeFile7 } from "fs/promises";
|
|
812
|
+
import { join as join8 } from "path";
|
|
813
|
+
var syncPushSchema = z6.object({
|
|
814
|
+
workDir: z6.string().optional().describe("\u5DE5\u4F5C\u76EE\u5F55\uFF08\u9ED8\u8BA4\u4F7F\u7528\u914D\u7F6E\u4E2D\u7684 workflowDir\uFF09")
|
|
815
|
+
});
|
|
816
|
+
async function syncPush(input) {
|
|
817
|
+
const creds = await getCredentials();
|
|
818
|
+
if (!creds?.token) {
|
|
819
|
+
return { success: false, message: "\u672A\u6FC0\u6D3B\uFF0C\u8BF7\u5148\u4F7F\u7528 workflow.init \u6FC0\u6D3B License" };
|
|
820
|
+
}
|
|
821
|
+
const config = await getConfig();
|
|
822
|
+
const state = await readState();
|
|
823
|
+
const payload = {
|
|
824
|
+
sources: Object.entries(state.sources).map(([id, s]) => ({
|
|
825
|
+
id,
|
|
826
|
+
title: s.title,
|
|
827
|
+
source: s.source,
|
|
828
|
+
tags: s.tags,
|
|
829
|
+
sourceUrl: s.sourceUrl,
|
|
830
|
+
coverUrl: s.coverUrl,
|
|
831
|
+
sourceIcon: s.sourceIcon,
|
|
832
|
+
createdAt: s.publishedAt
|
|
833
|
+
})),
|
|
834
|
+
drafts: Object.entries(state.drafts).map(([id, d]) => ({
|
|
835
|
+
id,
|
|
836
|
+
account: d.account,
|
|
837
|
+
platform: d.platform,
|
|
838
|
+
title: d.title,
|
|
839
|
+
status: d.status,
|
|
840
|
+
wordCount: d.wordCount,
|
|
841
|
+
tags: d.tags,
|
|
842
|
+
createdAt: d.createdAt,
|
|
843
|
+
updatedAt: d.updatedAt
|
|
844
|
+
})),
|
|
845
|
+
published: Object.entries(state.published).map(([id, p]) => ({
|
|
846
|
+
id,
|
|
847
|
+
account: p.account,
|
|
848
|
+
platform: p.platform,
|
|
849
|
+
title: p.title,
|
|
850
|
+
platformUrl: p.platformUrl,
|
|
851
|
+
publishedAt: p.publishedAt
|
|
852
|
+
})),
|
|
853
|
+
configs: Object.entries(state.configs).map(([key, c]) => ({
|
|
854
|
+
type: c.type,
|
|
855
|
+
name: c.name,
|
|
856
|
+
displayName: c.displayName,
|
|
857
|
+
content: c.content,
|
|
858
|
+
metadata: c.metadata
|
|
859
|
+
})),
|
|
860
|
+
crawlerSources: Object.entries(state.crawlerSources).map(([id, cs]) => ({
|
|
861
|
+
id,
|
|
862
|
+
name: cs.name,
|
|
863
|
+
url: cs.url,
|
|
864
|
+
type: cs.type,
|
|
865
|
+
icon: cs.icon,
|
|
866
|
+
enabled: cs.enabled
|
|
867
|
+
})),
|
|
868
|
+
analytics: []
|
|
869
|
+
};
|
|
870
|
+
try {
|
|
871
|
+
const res = await fetch(`${config.apiBaseUrl}/api/sync/batch`, {
|
|
872
|
+
method: "POST",
|
|
873
|
+
headers: {
|
|
874
|
+
"Content-Type": "application/json",
|
|
875
|
+
"Authorization": `Bearer ${creds.token}`
|
|
876
|
+
},
|
|
877
|
+
body: JSON.stringify(payload)
|
|
878
|
+
});
|
|
879
|
+
const result = await res.json();
|
|
880
|
+
if (res.ok) {
|
|
881
|
+
await updateLastSyncAt();
|
|
882
|
+
return {
|
|
883
|
+
success: true,
|
|
884
|
+
message: [
|
|
885
|
+
"\u2705 \u540C\u6B65\u5B8C\u6210",
|
|
886
|
+
` \u7D20\u6750: ${payload.sources.length} \u7BC7`,
|
|
887
|
+
` \u8349\u7A3F: ${payload.drafts.length} \u7BC7`,
|
|
888
|
+
` \u5DF2\u53D1\u5E03: ${payload.published.length} \u7BC7`,
|
|
889
|
+
` \u914D\u7F6E: ${payload.configs.length} \u4E2A`,
|
|
890
|
+
` \u8BA2\u9605\u6E90: ${payload.crawlerSources.length} \u4E2A`,
|
|
891
|
+
` \u4E91\u7AEF\u63A5\u53D7: ${result.accepted || 0} \u6761`
|
|
892
|
+
].join("\n"),
|
|
893
|
+
data: { accepted: result.accepted || 0 }
|
|
894
|
+
};
|
|
895
|
+
} else {
|
|
896
|
+
return { success: false, message: `\u540C\u6B65\u5931\u8D25: HTTP ${res.status}` };
|
|
897
|
+
}
|
|
898
|
+
} catch (err) {
|
|
899
|
+
return { success: false, message: `\u540C\u6B65\u7F51\u7EDC\u9519\u8BEF: ${err instanceof Error ? err.message : err}` };
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
var syncPullSchema = z6.object({
|
|
903
|
+
workDir: z6.string().optional().describe("\u5DE5\u4F5C\u76EE\u5F55")
|
|
904
|
+
});
|
|
905
|
+
async function syncPull(input) {
|
|
906
|
+
const creds = await getCredentials();
|
|
907
|
+
if (!creds?.token) {
|
|
908
|
+
return { success: false, message: "\u672A\u6FC0\u6D3B\uFF0C\u8BF7\u5148\u4F7F\u7528 workflow.init \u6FC0\u6D3B License" };
|
|
909
|
+
}
|
|
910
|
+
const config = await getConfig();
|
|
911
|
+
const workDir = input.workDir || config.workflowDir;
|
|
912
|
+
try {
|
|
913
|
+
const res = await fetch(`${config.apiBaseUrl}/api/sync/pull`, {
|
|
914
|
+
headers: { "Authorization": `Bearer ${creds.token}` }
|
|
915
|
+
});
|
|
916
|
+
if (!res.ok) {
|
|
917
|
+
if (res.status === 404) {
|
|
918
|
+
return { success: true, message: "\u4E91\u7AEF\u65E0\u5DF2\u6709\u914D\u7F6E\uFF0C\u4F7F\u7528\u672C\u5730\u9ED8\u8BA4" };
|
|
919
|
+
}
|
|
920
|
+
return { success: false, message: `\u62C9\u53D6\u5931\u8D25: HTTP ${res.status}` };
|
|
921
|
+
}
|
|
922
|
+
const cloudData = await res.json();
|
|
923
|
+
if (!cloudData.configs || cloudData.configs.length === 0) {
|
|
924
|
+
return { success: true, message: "\u4E91\u7AEF\u65E0\u914D\u7F6E\u6570\u636E" };
|
|
925
|
+
}
|
|
926
|
+
let updated = 0;
|
|
927
|
+
const state = await readState();
|
|
928
|
+
for (const config2 of cloudData.configs) {
|
|
929
|
+
const stateKey = `${config2.type}:${config2.name}`;
|
|
930
|
+
const localUpdatedAt = state.configs[stateKey]?.updatedAt || "";
|
|
931
|
+
if (!localUpdatedAt || config2.updatedAt > localUpdatedAt) {
|
|
932
|
+
let filePath = "";
|
|
933
|
+
if (config2.type === "base_rules") {
|
|
934
|
+
filePath = join8(workDir, "base-rules.md");
|
|
935
|
+
} else if (config2.type === "platform") {
|
|
936
|
+
filePath = join8(workDir, "platforms", `${config2.name}.md`);
|
|
937
|
+
} else if (config2.type === "account") {
|
|
938
|
+
filePath = join8(workDir, "accounts", `${config2.name}.yaml`);
|
|
939
|
+
}
|
|
940
|
+
if (filePath && config2.content) {
|
|
941
|
+
await writeFile7(filePath, config2.content, "utf-8");
|
|
942
|
+
state.configs[stateKey] = {
|
|
943
|
+
type: config2.type,
|
|
944
|
+
name: config2.name,
|
|
945
|
+
displayName: config2.displayName,
|
|
946
|
+
content: config2.content,
|
|
947
|
+
metadata: config2.metadata || {},
|
|
948
|
+
updatedAt: config2.updatedAt
|
|
949
|
+
};
|
|
950
|
+
updated++;
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
await replaceState(state);
|
|
955
|
+
return {
|
|
956
|
+
success: true,
|
|
957
|
+
message: updated > 0 ? `\u2705 \u4ECE\u4E91\u7AEF\u540C\u6B65\u4E86 ${updated} \u4E2A\u914D\u7F6E\u6587\u4EF6` : "\u672C\u5730\u914D\u7F6E\u5DF2\u662F\u6700\u65B0\uFF0C\u65E0\u9700\u66F4\u65B0",
|
|
958
|
+
data: { updated }
|
|
959
|
+
};
|
|
960
|
+
} catch (err) {
|
|
961
|
+
return { success: false, message: `\u62C9\u53D6\u9519\u8BEF: ${err instanceof Error ? err.message : err}` };
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
// src/tools/workflow.ts
|
|
966
|
+
import { z as z7 } from "zod";
|
|
967
|
+
import { cp, mkdir as mkdir6, access as access2, writeFile as writeFile8 } from "fs/promises";
|
|
968
|
+
import { join as join9, dirname } from "path";
|
|
1135
969
|
import { fileURLToPath } from "url";
|
|
1136
970
|
var __filename = fileURLToPath(import.meta.url);
|
|
1137
|
-
var __dirname =
|
|
1138
|
-
var WORKFLOW_SRC =
|
|
1139
|
-
var workflowInitSchema =
|
|
1140
|
-
workDir:
|
|
1141
|
-
licenseKey:
|
|
971
|
+
var __dirname = dirname(__filename);
|
|
972
|
+
var WORKFLOW_SRC = join9(__dirname, "..", "workflow");
|
|
973
|
+
var workflowInitSchema = z7.object({
|
|
974
|
+
workDir: z7.string().describe("\u5DE5\u4F5C\u6D41\u521D\u59CB\u5316\u76EE\u6807\u76EE\u5F55\uFF08\u7EDD\u5BF9\u8DEF\u5F84\uFF09"),
|
|
975
|
+
licenseKey: z7.string().optional().describe("License Key\uFF08\u53EF\u9009\uFF0C\u4F20\u5165\u5219\u81EA\u52A8\u6FC0\u6D3B\uFF09")
|
|
1142
976
|
});
|
|
1143
977
|
async function workflowInit(input) {
|
|
1144
978
|
const cwd = input.workDir;
|
|
@@ -1146,8 +980,8 @@ async function workflowInit(input) {
|
|
|
1146
980
|
try {
|
|
1147
981
|
const items = ["CLAUDE.md", "base-rules.md", "platforms", "accounts", "tools"];
|
|
1148
982
|
for (const item of items) {
|
|
1149
|
-
const src =
|
|
1150
|
-
const dest =
|
|
983
|
+
const src = join9(WORKFLOW_SRC, item);
|
|
984
|
+
const dest = join9(cwd, item);
|
|
1151
985
|
try {
|
|
1152
986
|
await access2(dest);
|
|
1153
987
|
results.push(`\u23ED\uFE0F ${item} \u5DF2\u5B58\u5728\uFF0C\u8DF3\u8FC7`);
|
|
@@ -1166,9 +1000,20 @@ async function workflowInit(input) {
|
|
|
1166
1000
|
".claudeink"
|
|
1167
1001
|
];
|
|
1168
1002
|
for (const dir of dirs) {
|
|
1169
|
-
await mkdir6(
|
|
1003
|
+
await mkdir6(join9(cwd, dir), { recursive: true });
|
|
1170
1004
|
}
|
|
1171
|
-
results.push("\u2705 \u8FD0\u884C\u65F6\u76EE\u5F55\u5DF2\u521B\u5EFA
|
|
1005
|
+
results.push("\u2705 \u8FD0\u884C\u65F6\u76EE\u5F55\u5DF2\u521B\u5EFA");
|
|
1006
|
+
const emptyState = {
|
|
1007
|
+
sources: {},
|
|
1008
|
+
drafts: {},
|
|
1009
|
+
published: {},
|
|
1010
|
+
configs: {},
|
|
1011
|
+
crawlerSources: {},
|
|
1012
|
+
lastSyncAt: ""
|
|
1013
|
+
};
|
|
1014
|
+
await replaceState(emptyState);
|
|
1015
|
+
results.push("\u2705 \u672C\u5730\u72B6\u6001\u8868\u5DF2\u521D\u59CB\u5316");
|
|
1016
|
+
let activated = false;
|
|
1172
1017
|
if (input.licenseKey) {
|
|
1173
1018
|
try {
|
|
1174
1019
|
const res = await fetch("https://app.claudeink.com/api/auth/activate", {
|
|
@@ -1178,13 +1023,13 @@ async function workflowInit(input) {
|
|
|
1178
1023
|
});
|
|
1179
1024
|
const data = await res.json();
|
|
1180
1025
|
if (data.userId) {
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
join7(cwd, ".claudeink", "credentials.json"),
|
|
1026
|
+
await writeFile8(
|
|
1027
|
+
join9(cwd, ".claudeink", "credentials.json"),
|
|
1184
1028
|
JSON.stringify(data, null, 2),
|
|
1185
1029
|
{ mode: 384 }
|
|
1186
1030
|
);
|
|
1187
|
-
results.push(`\u2705 License \u6FC0\u6D3B\u6210\u529F\uFF08\u5957\u9910: ${data.plan}\
|
|
1031
|
+
results.push(`\u2705 License \u6FC0\u6D3B\u6210\u529F\uFF08\u5957\u9910: ${data.plan}\uFF09`);
|
|
1032
|
+
activated = true;
|
|
1188
1033
|
} else {
|
|
1189
1034
|
results.push(`\u26A0\uFE0F License \u6FC0\u6D3B\u5931\u8D25: ${JSON.stringify(data)}`);
|
|
1190
1035
|
}
|
|
@@ -1192,6 +1037,28 @@ async function workflowInit(input) {
|
|
|
1192
1037
|
results.push(`\u26A0\uFE0F \u6FC0\u6D3B\u7F51\u7EDC\u9519\u8BEF: ${err instanceof Error ? err.message : err}`);
|
|
1193
1038
|
}
|
|
1194
1039
|
}
|
|
1040
|
+
if (activated) {
|
|
1041
|
+
try {
|
|
1042
|
+
const pullResult = await syncPull({ workDir: cwd });
|
|
1043
|
+
if (pullResult.success && pullResult.data && pullResult.data.updated > 0) {
|
|
1044
|
+
results.push("\u2705 \u5DF2\u4ECE\u4E91\u7AEF\u540C\u6B65\u914D\u7F6E\uFF08\u8DE8\u8BBE\u5907\u6062\u590D\uFF09");
|
|
1045
|
+
} else {
|
|
1046
|
+
results.push("\u2139\uFE0F \u4E91\u7AEF\u65E0\u5DF2\u6709\u914D\u7F6E\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u6A21\u677F");
|
|
1047
|
+
}
|
|
1048
|
+
} catch {
|
|
1049
|
+
results.push("\u2139\uFE0F \u4E91\u7AEF\u65E0\u5DF2\u6709\u914D\u7F6E\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u6A21\u677F");
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
try {
|
|
1053
|
+
await access2(join9(cwd, "tools", "crawler", "package.json"));
|
|
1054
|
+
const { execSync } = await import("child_process");
|
|
1055
|
+
execSync("npm install --silent", {
|
|
1056
|
+
cwd: join9(cwd, "tools", "crawler"),
|
|
1057
|
+
stdio: "pipe"
|
|
1058
|
+
});
|
|
1059
|
+
results.push("\u2705 \u722C\u866B\u4F9D\u8D56\u5DF2\u5B89\u88C5");
|
|
1060
|
+
} catch {
|
|
1061
|
+
}
|
|
1195
1062
|
return {
|
|
1196
1063
|
success: true,
|
|
1197
1064
|
message: [
|
|
@@ -1199,14 +1066,6 @@ async function workflowInit(input) {
|
|
|
1199
1066
|
"",
|
|
1200
1067
|
...results,
|
|
1201
1068
|
"",
|
|
1202
|
-
"\u{1F4C2} \u5DF2\u91CA\u653E\u6587\u4EF6\uFF1A",
|
|
1203
|
-
"\u251C\u2500\u2500 CLAUDE.md \uFF08\u7CFB\u7EDF\u7D22\u5F15\uFF0C\u8BF7\u5148\u8BFB\u53D6\uFF09",
|
|
1204
|
-
"\u251C\u2500\u2500 base-rules.md \uFF08\u901A\u7528\u5199\u4F5C\u5E95\u5EA7\uFF09",
|
|
1205
|
-
"\u251C\u2500\u2500 platforms/ \uFF085 \u4E2A\u5E73\u53F0\u89C4\u5219\uFF09",
|
|
1206
|
-
"\u251C\u2500\u2500 accounts/ \uFF08\u8D26\u53F7\u914D\u7F6E\u6A21\u677F\uFF09",
|
|
1207
|
-
"\u251C\u2500\u2500 sources/ \uFF08\u5171\u4EAB\u7D20\u6750\u5E93\uFF09",
|
|
1208
|
-
"\u2514\u2500\u2500 tools/ \uFF08\u722C\u866B\u7B49\u5DE5\u5177\uFF09",
|
|
1209
|
-
"",
|
|
1210
1069
|
"\u{1F3AF} \u4E0B\u4E00\u6B65\uFF1A",
|
|
1211
1070
|
"1. \u8BFB\u53D6 CLAUDE.md \u4E86\u89E3\u4E09\u5C42\u914D\u7F6E\u67B6\u6784",
|
|
1212
1071
|
"2. \u4F7F\u7528 /\u65B0\u5EFA\u8D26\u53F7 \u521B\u5EFA\u7B2C\u4E00\u4E2A\u81EA\u5A92\u4F53\u8D26\u53F7",
|
|
@@ -1220,323 +1079,48 @@ async function workflowInit(input) {
|
|
|
1220
1079
|
};
|
|
1221
1080
|
}
|
|
1222
1081
|
}
|
|
1223
|
-
async function workflowStatus() {
|
|
1224
|
-
const cwd = process.cwd();
|
|
1225
|
-
const checks = [];
|
|
1226
|
-
const requiredFiles = [
|
|
1227
|
-
{ path: "CLAUDE.md", label: "\u7CFB\u7EDF\u7D22\u5F15" },
|
|
1228
|
-
{ path: "base-rules.md", label: "\u901A\u7528\u5199\u4F5C\u5E95\u5EA7" },
|
|
1229
|
-
{ path: "platforms/wechat.md", label: "\u5FAE\u4FE1\u516C\u4F17\u53F7\u89C4\u5219" },
|
|
1230
|
-
{ path: "accounts/_template.yaml", label: "\u8D26\u53F7\u6A21\u677F" }
|
|
1231
|
-
];
|
|
1232
|
-
let allOk = true;
|
|
1233
|
-
for (const file of requiredFiles) {
|
|
1234
|
-
try {
|
|
1235
|
-
await access2(join7(cwd, file.path));
|
|
1236
|
-
checks.push(`\u2705 ${file.label}\uFF08${file.path}\uFF09`);
|
|
1237
|
-
} catch {
|
|
1238
|
-
checks.push(`\u274C ${file.label}\uFF08${file.path}\uFF09\u2014 \u7F3A\u5931`);
|
|
1239
|
-
allOk = false;
|
|
1240
|
-
}
|
|
1241
|
-
}
|
|
1242
|
-
try {
|
|
1243
|
-
const creds = await readFile5(join7(cwd, ".claudeink/credentials.json"), "utf-8");
|
|
1244
|
-
const data = JSON.parse(creds);
|
|
1245
|
-
if (data.userId) {
|
|
1246
|
-
checks.push(`\u2705 License \u5DF2\u6FC0\u6D3B\uFF08\u5957\u9910: ${data.plan}\uFF09`);
|
|
1247
|
-
} else {
|
|
1248
|
-
checks.push("\u26A0\uFE0F \u51ED\u8BC1\u6587\u4EF6\u5B58\u5728\u4F46\u672A\u6FC0\u6D3B");
|
|
1249
|
-
}
|
|
1250
|
-
} catch {
|
|
1251
|
-
checks.push("\u274C \u672A\u627E\u5230\u51ED\u8BC1\u6587\u4EF6\uFF08.claudeink/credentials.json\uFF09");
|
|
1252
|
-
allOk = false;
|
|
1253
|
-
}
|
|
1254
|
-
try {
|
|
1255
|
-
const { glob: glob5 } = await import("glob");
|
|
1256
|
-
const yamlFiles = await glob5("accounts/*.yaml", { cwd, ignore: "accounts/_template.yaml" });
|
|
1257
|
-
if (yamlFiles.length > 0) {
|
|
1258
|
-
checks.push(`\u2705 ${yamlFiles.length} \u4E2A\u8D26\u53F7\u914D\u7F6E`);
|
|
1259
|
-
} else {
|
|
1260
|
-
checks.push("\u2139\uFE0F \u6682\u65E0\u8D26\u53F7\uFF0C\u4F7F\u7528 /\u65B0\u5EFA\u8D26\u53F7 \u521B\u5EFA");
|
|
1261
|
-
}
|
|
1262
|
-
} catch {
|
|
1263
|
-
checks.push("\u2139\uFE0F \u6682\u65E0\u8D26\u53F7");
|
|
1264
|
-
}
|
|
1265
|
-
return {
|
|
1266
|
-
success: true,
|
|
1267
|
-
message: [
|
|
1268
|
-
allOk ? "\u2705 \u5DE5\u4F5C\u6D41\u72B6\u6001\uFF1A\u5C31\u7EEA" : "\u26A0\uFE0F \u5DE5\u4F5C\u6D41\u72B6\u6001\uFF1A\u9700\u8981\u521D\u59CB\u5316",
|
|
1269
|
-
"",
|
|
1270
|
-
...checks,
|
|
1271
|
-
"",
|
|
1272
|
-
allOk ? "" : "\u8BF7\u8C03\u7528 workflow.init \u5B8C\u6210\u521D\u59CB\u5316\u3002"
|
|
1273
|
-
].join("\n")
|
|
1274
|
-
};
|
|
1275
|
-
}
|
|
1276
|
-
|
|
1277
|
-
// src/tools/sync.ts
|
|
1278
|
-
import { z as z7 } from "zod";
|
|
1279
|
-
import { readFile as readFile6, stat as stat2 } from "fs/promises";
|
|
1280
|
-
import { join as join8, basename as basename2, extname as extname2 } from "path";
|
|
1281
|
-
import matter3 from "gray-matter";
|
|
1282
|
-
import { glob as glob4 } from "glob";
|
|
1283
|
-
var syncPushSchema = z7.object({
|
|
1284
|
-
workDir: z7.string().optional().describe("\u5DE5\u4F5C\u76EE\u5F55\uFF08\u9ED8\u8BA4\u4F7F\u7528\u914D\u7F6E\u4E2D\u7684 workflowDir\uFF09")
|
|
1285
|
-
});
|
|
1286
|
-
async function syncPush(input) {
|
|
1287
|
-
const config = await getConfig();
|
|
1288
|
-
const creds = await getCredentials();
|
|
1289
|
-
if (!creds?.token) {
|
|
1290
|
-
return { success: false, message: "\u672A\u6FC0\u6D3B\uFF0C\u8BF7\u5148\u4F7F\u7528 auth.activate \u6FC0\u6D3B License" };
|
|
1291
|
-
}
|
|
1292
|
-
const workDir = input.workDir || config.workflowDir;
|
|
1293
|
-
if (!workDir) {
|
|
1294
|
-
return { success: false, message: "\u672A\u8BBE\u7F6E\u5DE5\u4F5C\u76EE\u5F55\uFF0C\u8BF7\u5728 config.json \u4E2D\u914D\u7F6E workflowDir" };
|
|
1295
|
-
}
|
|
1296
|
-
const sources = [];
|
|
1297
|
-
const drafts = [];
|
|
1298
|
-
const published = [];
|
|
1299
|
-
try {
|
|
1300
|
-
const sourceFiles = await glob4("sources/**/*.md", { cwd: workDir });
|
|
1301
|
-
for (const file of sourceFiles) {
|
|
1302
|
-
try {
|
|
1303
|
-
const raw = await readFile6(join8(workDir, file), "utf-8");
|
|
1304
|
-
const { data } = matter3(raw);
|
|
1305
|
-
const id = basename2(file, extname2(file));
|
|
1306
|
-
sources.push({
|
|
1307
|
-
id,
|
|
1308
|
-
title: data.title || id,
|
|
1309
|
-
source: data.source || "unknown",
|
|
1310
|
-
tags: Array.isArray(data.tags) ? data.tags : [],
|
|
1311
|
-
sourceUrl: data.url || null,
|
|
1312
|
-
createdAt: data.published || (/* @__PURE__ */ new Date()).toISOString()
|
|
1313
|
-
});
|
|
1314
|
-
} catch {
|
|
1315
|
-
}
|
|
1316
|
-
}
|
|
1317
|
-
} catch {
|
|
1318
|
-
}
|
|
1319
|
-
const accounts = {};
|
|
1320
|
-
try {
|
|
1321
|
-
const yamlFiles = await glob4("accounts/*.yaml", { cwd: workDir, ignore: "accounts/_template.yaml" });
|
|
1322
|
-
for (const file of yamlFiles) {
|
|
1323
|
-
try {
|
|
1324
|
-
const content = await readFile6(join8(workDir, file), "utf-8");
|
|
1325
|
-
const nameMatch = content.match(/^name:\s*"?([^"\n]+)"?/m);
|
|
1326
|
-
const idMatch = content.match(/^id:\s*"?([^"\n]+)"?/m);
|
|
1327
|
-
const platformMatch = content.match(/^platform:\s*"?([^"\n]+)"?/m);
|
|
1328
|
-
const draftsMatch = content.match(/drafts:\s*"?([^"\n]+)"?/m);
|
|
1329
|
-
const publishedMatch = content.match(/published:\s*"?([^"\n]+)"?/m);
|
|
1330
|
-
if (idMatch && nameMatch) {
|
|
1331
|
-
const id = idMatch[1].trim();
|
|
1332
|
-
const name = nameMatch[1].trim();
|
|
1333
|
-
const platform = platformMatch?.[1]?.trim() || "unknown";
|
|
1334
|
-
const dp = (draftsMatch?.[1] || `accounts/${id}/drafts/`).replace("{id}", id).trim();
|
|
1335
|
-
const pp = (publishedMatch?.[1] || `accounts/${id}/published/`).replace("{id}", id).trim();
|
|
1336
|
-
accounts[id] = { name, platform, draftsPath: dp, publishedPath: pp };
|
|
1337
|
-
}
|
|
1338
|
-
} catch {
|
|
1339
|
-
}
|
|
1340
|
-
}
|
|
1341
|
-
} catch {
|
|
1342
|
-
}
|
|
1343
|
-
for (const [accId, acc] of Object.entries(accounts)) {
|
|
1344
|
-
try {
|
|
1345
|
-
const draftFiles = await glob4("**/*.md", { cwd: join8(workDir, acc.draftsPath) });
|
|
1346
|
-
for (const file of draftFiles) {
|
|
1347
|
-
try {
|
|
1348
|
-
const fullPath = join8(workDir, acc.draftsPath, file);
|
|
1349
|
-
const raw = await readFile6(fullPath, "utf-8");
|
|
1350
|
-
const { data, content } = matter3(raw);
|
|
1351
|
-
const fileStat = await stat2(fullPath);
|
|
1352
|
-
const id = basename2(file, extname2(file));
|
|
1353
|
-
drafts.push({
|
|
1354
|
-
id,
|
|
1355
|
-
account: acc.name,
|
|
1356
|
-
platform: acc.platform,
|
|
1357
|
-
title: data.title || id,
|
|
1358
|
-
status: data.status || "draft",
|
|
1359
|
-
wordCount: content.trim().length,
|
|
1360
|
-
tags: Array.isArray(data.tags) ? data.tags : [],
|
|
1361
|
-
createdAt: data.created || fileStat.birthtime.toISOString(),
|
|
1362
|
-
updatedAt: fileStat.mtime.toISOString()
|
|
1363
|
-
});
|
|
1364
|
-
} catch {
|
|
1365
|
-
}
|
|
1366
|
-
}
|
|
1367
|
-
} catch {
|
|
1368
|
-
}
|
|
1369
|
-
}
|
|
1370
|
-
for (const [accId, acc] of Object.entries(accounts)) {
|
|
1371
|
-
try {
|
|
1372
|
-
const pubFiles = await glob4("**/*.md", { cwd: join8(workDir, acc.publishedPath) });
|
|
1373
|
-
for (const file of pubFiles) {
|
|
1374
|
-
try {
|
|
1375
|
-
const raw = await readFile6(join8(workDir, acc.publishedPath, file), "utf-8");
|
|
1376
|
-
const { data } = matter3(raw);
|
|
1377
|
-
const id = basename2(file, extname2(file));
|
|
1378
|
-
published.push({
|
|
1379
|
-
id,
|
|
1380
|
-
account: acc.name,
|
|
1381
|
-
platform: acc.platform,
|
|
1382
|
-
title: data.title || id,
|
|
1383
|
-
platformUrl: data.url || data.platform_url || null,
|
|
1384
|
-
publishedAt: data.published_at || data.publishedAt || (/* @__PURE__ */ new Date()).toISOString()
|
|
1385
|
-
});
|
|
1386
|
-
} catch {
|
|
1387
|
-
}
|
|
1388
|
-
}
|
|
1389
|
-
} catch {
|
|
1390
|
-
}
|
|
1391
|
-
}
|
|
1392
|
-
const configs = [];
|
|
1393
|
-
try {
|
|
1394
|
-
const baseRules = await readFile6(join8(workDir, "base-rules.md"), "utf-8");
|
|
1395
|
-
configs.push({ type: "base_rules", name: "base", displayName: "\u901A\u7528\u5E95\u5EA7", content: baseRules });
|
|
1396
|
-
} catch {
|
|
1397
|
-
}
|
|
1398
|
-
try {
|
|
1399
|
-
const platformFiles = await glob4("platforms/*.md", { cwd: workDir });
|
|
1400
|
-
for (const file of platformFiles) {
|
|
1401
|
-
const content = await readFile6(join8(workDir, file), "utf-8");
|
|
1402
|
-
const name = basename2(file, ".md");
|
|
1403
|
-
const displayNames = {
|
|
1404
|
-
wechat: "\u5FAE\u4FE1\u516C\u4F17\u53F7",
|
|
1405
|
-
xiaohongshu: "\u5C0F\u7EA2\u4E66",
|
|
1406
|
-
"x-twitter": "X (Twitter)",
|
|
1407
|
-
toutiao: "\u5934\u6761\u53F7",
|
|
1408
|
-
blog: "\u4E2A\u4EBA\u535A\u5BA2"
|
|
1409
|
-
};
|
|
1410
|
-
configs.push({ type: "platform", name, displayName: displayNames[name] || name, content });
|
|
1411
|
-
}
|
|
1412
|
-
} catch {
|
|
1413
|
-
}
|
|
1414
|
-
for (const [accId, acc] of Object.entries(accounts)) {
|
|
1415
|
-
try {
|
|
1416
|
-
const content = await readFile6(join8(workDir, `accounts/${accId}.yaml`), "utf-8");
|
|
1417
|
-
configs.push({
|
|
1418
|
-
type: "account",
|
|
1419
|
-
name: accId,
|
|
1420
|
-
displayName: acc.name,
|
|
1421
|
-
content,
|
|
1422
|
-
metadata: { platform: acc.platform }
|
|
1423
|
-
});
|
|
1424
|
-
} catch {
|
|
1425
|
-
}
|
|
1426
|
-
}
|
|
1427
|
-
const payload = { sources, drafts, published, analytics: [], configs };
|
|
1428
|
-
try {
|
|
1429
|
-
const res = await fetch(`${config.apiBaseUrl}/api/sync/batch`, {
|
|
1430
|
-
method: "POST",
|
|
1431
|
-
headers: {
|
|
1432
|
-
"Content-Type": "application/json",
|
|
1433
|
-
"Authorization": `Bearer ${creds.token}`
|
|
1434
|
-
},
|
|
1435
|
-
body: JSON.stringify(payload)
|
|
1436
|
-
});
|
|
1437
|
-
const result = await res.json();
|
|
1438
|
-
if (res.ok) {
|
|
1439
|
-
return {
|
|
1440
|
-
success: true,
|
|
1441
|
-
message: [
|
|
1442
|
-
`\u2705 \u540C\u6B65\u5B8C\u6210`,
|
|
1443
|
-
` \u7D20\u6750: ${sources.length} \u7BC7`,
|
|
1444
|
-
` \u8349\u7A3F: ${drafts.length} \u7BC7`,
|
|
1445
|
-
` \u5DF2\u53D1\u5E03: ${published.length} \u7BC7`,
|
|
1446
|
-
` \u914D\u7F6E: ${configs.length} \u4E2A\uFF08\u901A\u7528\u5E95\u5EA7 + \u5E73\u53F0\u89C4\u5219 + \u8D26\u53F7\u914D\u7F6E\uFF09`,
|
|
1447
|
-
` \u4E91\u7AEF\u63A5\u53D7: ${result.accepted || 0} \u6761`
|
|
1448
|
-
].join("\n"),
|
|
1449
|
-
data: {
|
|
1450
|
-
sources: sources.length,
|
|
1451
|
-
drafts: drafts.length,
|
|
1452
|
-
published: published.length,
|
|
1453
|
-
configs: configs.length,
|
|
1454
|
-
accepted: result.accepted || 0
|
|
1455
|
-
}
|
|
1456
|
-
};
|
|
1457
|
-
} else {
|
|
1458
|
-
return {
|
|
1459
|
-
success: false,
|
|
1460
|
-
message: `\u540C\u6B65\u5931\u8D25: HTTP ${res.status} \u2014 ${JSON.stringify(result)}`
|
|
1461
|
-
};
|
|
1462
|
-
}
|
|
1463
|
-
} catch (err) {
|
|
1464
|
-
return {
|
|
1465
|
-
success: false,
|
|
1466
|
-
message: `\u540C\u6B65\u7F51\u7EDC\u9519\u8BEF: ${err instanceof Error ? err.message : err}`
|
|
1467
|
-
};
|
|
1468
|
-
}
|
|
1469
|
-
}
|
|
1470
1082
|
|
|
1471
1083
|
// src/index.ts
|
|
1472
1084
|
var server = new McpServer({
|
|
1473
1085
|
name: "ClaudeInk",
|
|
1474
|
-
version: "0.
|
|
1475
|
-
});
|
|
1476
|
-
server.tool("auth.activate", "\u9A8C\u8BC1 license key \u5E76\u6FC0\u6D3B ClaudeInk", activateSchema.shape, async (input) => {
|
|
1477
|
-
const result = await authActivate(input);
|
|
1478
|
-
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1086
|
+
version: "0.5.0"
|
|
1479
1087
|
});
|
|
1480
|
-
server.tool("
|
|
1481
|
-
const result = await
|
|
1088
|
+
server.tool("workflow.init", "\u521D\u59CB\u5316\u5199\u4F5C\u5DE5\u4F5C\u6D41\uFF08\u91CA\u653E\u4E09\u5C42\u914D\u7F6E + \u6FC0\u6D3B License + \u81EA\u52A8\u540C\u6B65\u4E91\u7AEF\u914D\u7F6E\uFF09", workflowInitSchema.shape, async (input) => {
|
|
1089
|
+
const result = await workflowInit(input);
|
|
1482
1090
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1483
1091
|
});
|
|
1484
|
-
server.tool("
|
|
1485
|
-
const result = await
|
|
1092
|
+
server.tool("sync.push", "\u5C06\u672C\u5730\u6570\u636E\u540C\u6B65\u5230 ClaudeInk \u4E91\u7AEF\uFF08\u8BFB\u53D6\u672C\u5730\u72B6\u6001\u8868\u76F4\u63A5\u63A8\u9001\uFF09", syncPushSchema.shape, async (input) => {
|
|
1093
|
+
const result = await syncPush(input);
|
|
1486
1094
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1487
1095
|
});
|
|
1488
|
-
server.tool("
|
|
1489
|
-
const result = await
|
|
1096
|
+
server.tool("sync.pull", "\u4ECE\u4E91\u7AEF\u62C9\u53D6\u914D\u7F6E\u5230\u672C\u5730\uFF08\u65F6\u95F4\u6233\u6BD4\u8F83\uFF0C\u65B0\u7684\u8986\u76D6\u65E7\u7684\uFF09", syncPullSchema.shape, async (input) => {
|
|
1097
|
+
const result = await syncPull(input);
|
|
1490
1098
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1491
1099
|
});
|
|
1492
1100
|
server.tool("source.add", "\u6DFB\u52A0\u7D20\u6750\u5230\u672C\u5730\u7D20\u6750\u5E93", sourceAddSchema.shape, async (input) => {
|
|
1493
1101
|
const result = await sourceAdd(input);
|
|
1494
1102
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1495
1103
|
});
|
|
1496
|
-
server.tool("source.search", "\u641C\u7D22\u672C\u5730\u7D20\u6750", sourceSearchSchema.shape, async (input) => {
|
|
1497
|
-
const result = await sourceSearch(input);
|
|
1498
|
-
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1499
|
-
});
|
|
1500
|
-
server.tool("source.list", "\u5217\u51FA\u7D20\u6750\u76EE\u5F55", sourceListSchema.shape, async (input) => {
|
|
1501
|
-
const result = await sourceList(input);
|
|
1502
|
-
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1503
|
-
});
|
|
1504
1104
|
server.tool("source.crawl", "\u89E6\u53D1\u722C\u866B\u6293\u53D6", sourceCrawlSchema.shape, async (input) => {
|
|
1505
1105
|
const result = await sourceCrawl(input);
|
|
1506
1106
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1507
1107
|
});
|
|
1508
|
-
server.tool("source.
|
|
1509
|
-
const result = await sourceSchedule(input);
|
|
1510
|
-
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1511
|
-
});
|
|
1512
|
-
server.tool("source.tag", "\u5904\u7406\u7D20\u6750\u6807\u7B7E\uFF08\u961F\u5217\u6216\u5355\u4E2A\uFF09", sourceTagSchema.shape, async (input) => {
|
|
1108
|
+
server.tool("source.tag", "\u83B7\u53D6\u5F85\u6807\u7B7E\u7D20\u6750\u5185\u5BB9\u4F9B AI \u5206\u6790", sourceTagSchema.shape, async (input) => {
|
|
1513
1109
|
const result = await sourceTag(input);
|
|
1514
1110
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1515
1111
|
});
|
|
1516
|
-
server.tool("source.
|
|
1517
|
-
const result = await
|
|
1112
|
+
server.tool("source.tag_apply", "\u5199\u5165 AI \u751F\u6210\u7684\u6807\u7B7E\u5230\u7D20\u6750\u6587\u4EF6", sourceTagApplySchema.shape, async (input) => {
|
|
1113
|
+
const result = await sourceTagApply(input);
|
|
1518
1114
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1519
1115
|
});
|
|
1520
|
-
server.tool("source.
|
|
1521
|
-
const result = await
|
|
1116
|
+
server.tool("source.subscribe", "\u7BA1\u7406\u8BA2\u9605\u6E90\uFF08\u6DFB\u52A0/\u5220\u9664/\u5217\u51FA\u722C\u866B\u6E90\uFF09", sourceSubscribeSchema.shape, async (input) => {
|
|
1117
|
+
const result = await sourceSubscribe(input);
|
|
1522
1118
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1523
1119
|
});
|
|
1524
1120
|
server.tool("draft.save", "\u4FDD\u5B58\u8349\u7A3F", draftSaveSchema.shape, async (input) => {
|
|
1525
1121
|
const result = await draftSave(input);
|
|
1526
1122
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1527
1123
|
});
|
|
1528
|
-
server.tool("draft.list", "\u5217\u51FA\u8349\u7A3F", draftListSchema.shape, async (input) => {
|
|
1529
|
-
const result = await draftList(input);
|
|
1530
|
-
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1531
|
-
});
|
|
1532
|
-
server.tool("draft.update", "\u66F4\u65B0\u8349\u7A3F", draftUpdateSchema.shape, async (input) => {
|
|
1533
|
-
const result = await draftUpdate(input);
|
|
1534
|
-
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1535
|
-
});
|
|
1536
|
-
server.tool("draft.delete", "\u5220\u9664\u8349\u7A3F", draftDeleteSchema.shape, async (input) => {
|
|
1537
|
-
const result = await draftDelete(input);
|
|
1538
|
-
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1539
|
-
});
|
|
1540
1124
|
server.tool("draft.publish", "\u6807\u8BB0\u8349\u7A3F\u4E3A\u5DF2\u53D1\u5E03", draftPublishSchema.shape, async (input) => {
|
|
1541
1125
|
const result = await draftPublish(input);
|
|
1542
1126
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
@@ -1549,34 +1133,14 @@ server.tool("analytics.report", "\u83B7\u53D6\u6570\u636E\u5206\u6790\u62A5\u544
|
|
|
1549
1133
|
const result = await analyticsReport(input);
|
|
1550
1134
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1551
1135
|
});
|
|
1552
|
-
server.tool("account.
|
|
1553
|
-
const result = await accountList();
|
|
1554
|
-
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1555
|
-
});
|
|
1556
|
-
server.tool("account.switch", "\u5207\u6362\u5F53\u524D\u8D26\u53F7", accountSwitchSchema.shape, async (input) => {
|
|
1557
|
-
const result = await accountSwitch(input);
|
|
1558
|
-
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1559
|
-
});
|
|
1560
|
-
server.tool("account.create", "\u521B\u5EFA\u65B0\u8D26\u53F7", accountCreateSchema.shape, async (input) => {
|
|
1136
|
+
server.tool("account.create", "\u521B\u5EFA\u65B0\u81EA\u5A92\u4F53\u8D26\u53F7", accountCreateSchema.shape, async (input) => {
|
|
1561
1137
|
const result = await accountCreate(input);
|
|
1562
1138
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1563
1139
|
});
|
|
1564
|
-
server.tool("sync.push", "\u5C06\u672C\u5730\u7D20\u6750\u3001\u8349\u7A3F\u3001\u5DF2\u53D1\u5E03\u6587\u7AE0\u7684\u5143\u6570\u636E\u540C\u6B65\u5230 ClaudeInk \u4E91\u7AEF\u63A7\u5236\u53F0", syncPushSchema.shape, async (input) => {
|
|
1565
|
-
const result = await syncPush(input);
|
|
1566
|
-
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1567
|
-
});
|
|
1568
|
-
server.tool("workflow.init", "\u521D\u59CB\u5316\u5199\u4F5C\u5DE5\u4F5C\u6D41\uFF08\u91CA\u653E\u4E09\u5C42\u914D\u7F6E + \u5E73\u53F0\u89C4\u5219 + \u8D26\u53F7\u6A21\u677F + \u722C\u866B\u5DE5\u5177\u5230\u5DE5\u4F5C\u76EE\u5F55\uFF09", workflowInitSchema.shape, async (input) => {
|
|
1569
|
-
const result = await workflowInit(input);
|
|
1570
|
-
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1571
|
-
});
|
|
1572
|
-
server.tool("workflow.status", "\u68C0\u67E5\u5199\u4F5C\u5DE5\u4F5C\u6D41\u72B6\u6001\uFF08\u914D\u7F6E\u6587\u4EF6\u3001\u51ED\u8BC1\u3001\u8D26\u53F7\u662F\u5426\u5C31\u7EEA\uFF09", {}, async () => {
|
|
1573
|
-
const result = await workflowStatus();
|
|
1574
|
-
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
1575
|
-
});
|
|
1576
1140
|
async function main() {
|
|
1577
1141
|
const transport = new StdioServerTransport();
|
|
1578
1142
|
await server.connect(transport);
|
|
1579
|
-
console.error("[ClaudeInk MCP] Server started on stdio");
|
|
1143
|
+
console.error("[ClaudeInk MCP] Server started on stdio (13 tools)");
|
|
1580
1144
|
}
|
|
1581
1145
|
main().catch((err) => {
|
|
1582
1146
|
console.error("[ClaudeInk MCP] Fatal error:", err);
|