@page.su/ai-blog-cli 0.1.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +145 -0
- package/dist/api-client.d.ts +39 -0
- package/dist/api-client.d.ts.map +1 -0
- package/dist/api-client.js +163 -0
- package/dist/api-client.js.map +1 -0
- package/dist/api-client.test.d.ts +2 -0
- package/dist/api-client.test.d.ts.map +1 -0
- package/dist/api-client.test.js +83 -0
- package/dist/api-client.test.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +74 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/build.d.ts +4 -0
- package/dist/commands/build.d.ts.map +1 -0
- package/dist/commands/build.js +158 -0
- package/dist/commands/build.js.map +1 -0
- package/dist/commands/code-repo.d.ts +4 -0
- package/dist/commands/code-repo.d.ts.map +1 -0
- package/dist/commands/code-repo.js +102 -0
- package/dist/commands/code-repo.js.map +1 -0
- package/dist/commands/comments.d.ts +4 -0
- package/dist/commands/comments.d.ts.map +1 -0
- package/dist/commands/comments.js +53 -0
- package/dist/commands/comments.js.map +1 -0
- package/dist/commands/demo.d.ts +4 -0
- package/dist/commands/demo.d.ts.map +1 -0
- package/dist/commands/demo.js +72 -0
- package/dist/commands/demo.js.map +1 -0
- package/dist/commands/helpers.d.ts +21 -0
- package/dist/commands/helpers.d.ts.map +1 -0
- package/dist/commands/helpers.js +66 -0
- package/dist/commands/helpers.js.map +1 -0
- package/dist/commands/knowledge.d.ts +4 -0
- package/dist/commands/knowledge.d.ts.map +1 -0
- package/dist/commands/knowledge.js +146 -0
- package/dist/commands/knowledge.js.map +1 -0
- package/dist/commands/post.d.ts +4 -0
- package/dist/commands/post.d.ts.map +1 -0
- package/dist/commands/post.js +207 -0
- package/dist/commands/post.js.map +1 -0
- package/dist/commands/scout.d.ts +4 -0
- package/dist/commands/scout.d.ts.map +1 -0
- package/dist/commands/scout.js +48 -0
- package/dist/commands/scout.js.map +1 -0
- package/dist/commands/series.d.ts +4 -0
- package/dist/commands/series.d.ts.map +1 -0
- package/dist/commands/series.js +101 -0
- package/dist/commands/series.js.map +1 -0
- package/dist/commands/skills.d.ts +3 -0
- package/dist/commands/skills.d.ts.map +1 -0
- package/dist/commands/skills.js +79 -0
- package/dist/commands/skills.js.map +1 -0
- package/dist/commands/taxonomy.d.ts +4 -0
- package/dist/commands/taxonomy.d.ts.map +1 -0
- package/dist/commands/taxonomy.js +52 -0
- package/dist/commands/taxonomy.js.map +1 -0
- package/dist/commands/uploads.d.ts +4 -0
- package/dist/commands/uploads.d.ts.map +1 -0
- package/dist/commands/uploads.js +37 -0
- package/dist/commands/uploads.js.map +1 -0
- package/dist/config.d.ts +19 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +84 -0
- package/dist/config.js.map +1 -0
- package/dist/config.test.d.ts +2 -0
- package/dist/config.test.d.ts.map +1 -0
- package/dist/config.test.js +53 -0
- package/dist/config.test.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +100 -0
- package/dist/index.js.map +1 -0
- package/dist/prompts/index.d.ts +9 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +178 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/resources/syntax-spec.d.ts +23 -0
- package/dist/resources/syntax-spec.d.ts.map +1 -0
- package/dist/resources/syntax-spec.js +105 -0
- package/dist/resources/syntax-spec.js.map +1 -0
- package/dist/resources/syntax-spec.test.d.ts +2 -0
- package/dist/resources/syntax-spec.test.d.ts.map +1 -0
- package/dist/resources/syntax-spec.test.js +47 -0
- package/dist/resources/syntax-spec.test.js.map +1 -0
- package/dist/tools/build.d.ts +13 -0
- package/dist/tools/build.d.ts.map +1 -0
- package/dist/tools/build.js +181 -0
- package/dist/tools/build.js.map +1 -0
- package/dist/tools/code-repos.d.ts +5 -0
- package/dist/tools/code-repos.d.ts.map +1 -0
- package/dist/tools/code-repos.js +149 -0
- package/dist/tools/code-repos.js.map +1 -0
- package/dist/tools/comments.d.ts +12 -0
- package/dist/tools/comments.d.ts.map +1 -0
- package/dist/tools/comments.js +87 -0
- package/dist/tools/comments.js.map +1 -0
- package/dist/tools/demos.d.ts +5 -0
- package/dist/tools/demos.d.ts.map +1 -0
- package/dist/tools/demos.js +98 -0
- package/dist/tools/demos.js.map +1 -0
- package/dist/tools/glossary.d.ts +4 -0
- package/dist/tools/glossary.d.ts.map +1 -0
- package/dist/tools/glossary.js +139 -0
- package/dist/tools/glossary.js.map +1 -0
- package/dist/tools/helpers.d.ts +44 -0
- package/dist/tools/helpers.d.ts.map +1 -0
- package/dist/tools/helpers.js +65 -0
- package/dist/tools/helpers.js.map +1 -0
- package/dist/tools/knowledge.d.ts +4 -0
- package/dist/tools/knowledge.d.ts.map +1 -0
- package/dist/tools/knowledge.js +140 -0
- package/dist/tools/knowledge.js.map +1 -0
- package/dist/tools/math-concepts.d.ts +4 -0
- package/dist/tools/math-concepts.d.ts.map +1 -0
- package/dist/tools/math-concepts.js +158 -0
- package/dist/tools/math-concepts.js.map +1 -0
- package/dist/tools/posts.d.ts +35 -0
- package/dist/tools/posts.d.ts.map +1 -0
- package/dist/tools/posts.js +278 -0
- package/dist/tools/posts.js.map +1 -0
- package/dist/tools/scout.d.ts +10 -0
- package/dist/tools/scout.d.ts.map +1 -0
- package/dist/tools/scout.js +111 -0
- package/dist/tools/scout.js.map +1 -0
- package/dist/tools/series.d.ts +12 -0
- package/dist/tools/series.d.ts.map +1 -0
- package/dist/tools/series.js +120 -0
- package/dist/tools/series.js.map +1 -0
- package/dist/tools/syntax-capabilities.d.ts +15 -0
- package/dist/tools/syntax-capabilities.d.ts.map +1 -0
- package/dist/tools/syntax-capabilities.js +859 -0
- package/dist/tools/syntax-capabilities.js.map +1 -0
- package/dist/tools/taxonomy.d.ts +4 -0
- package/dist/tools/taxonomy.d.ts.map +1 -0
- package/dist/tools/taxonomy.js +73 -0
- package/dist/tools/taxonomy.js.map +1 -0
- package/dist/tools/uploads.d.ts +15 -0
- package/dist/tools/uploads.d.ts.map +1 -0
- package/dist/tools/uploads.js +56 -0
- package/dist/tools/uploads.js.map +1 -0
- package/dist/tools/workflows.d.ts +9 -0
- package/dist/tools/workflows.d.ts.map +1 -0
- package/dist/tools/workflows.js +249 -0
- package/dist/tools/workflows.js.map +1 -0
- package/package.json +41 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent skills 速查表 (~500 tokens)。
|
|
3
|
+
*
|
|
4
|
+
* 适合注入 Agent system prompt,让 Agent 快速了解可用命令。
|
|
5
|
+
* Agent 不确定用法时调 `aib <command> --help` 按需获取详情。
|
|
6
|
+
*
|
|
7
|
+
* 三层渐进式发现:
|
|
8
|
+
* L1: 本速查表(aib skills)— 覆盖 90% 场景
|
|
9
|
+
* L2: aib <command> --help — 命令组详情
|
|
10
|
+
* L3: aib <command> <subcommand> --help — 单命令参数
|
|
11
|
+
*/
|
|
12
|
+
const SKILLS_MD = `# ai-blog CLI (aib)
|
|
13
|
+
|
|
14
|
+
Env: AI_BLOG_BASE_URL + AI_BLOG_PAT
|
|
15
|
+
|
|
16
|
+
## Write a post
|
|
17
|
+
1. \`aib taxonomy list --json\` — get category/tag IDs
|
|
18
|
+
2. \`aib post create --title "Title" --content-file draft.md --status draft\` — create
|
|
19
|
+
3. \`aib post validate --content-file draft.md\` — validate (fix errors before publish)
|
|
20
|
+
4. \`aib post publish <id>\` — publish
|
|
21
|
+
|
|
22
|
+
## Long posts (>5000 words, save tokens)
|
|
23
|
+
1. \`aib post outline <id>\` — get section outline
|
|
24
|
+
2. \`aib post section <id> --index 3\` — read section 3
|
|
25
|
+
3. \`aib post replace-section <id> --index 3 --content-file new.md\` — replace section
|
|
26
|
+
4. \`aib post append <id> --content-file chapter.md\` — append new chapter
|
|
27
|
+
5. \`aib post insert-section <id> --after-index 2 --content-file section.md\` — insert
|
|
28
|
+
|
|
29
|
+
## Demos (interactive code sandboxes)
|
|
30
|
+
- \`aib demo list <postId>\` — list demos (slot, title, file sizes)
|
|
31
|
+
- \`aib demo get-file <postId> <slot> <fileName>\` — read single file (preferred)
|
|
32
|
+
- \`aib demo patch-file <postId> <slot> <fileName> --content-file new.js\` — update file
|
|
33
|
+
- \`aib demo upsert <postId> <slot> --files-file files.json\` — create/replace demo
|
|
34
|
+
|
|
35
|
+
## Code repositories
|
|
36
|
+
- \`aib code-repo get <postId>\` — get repo linked to post
|
|
37
|
+
- \`aib code-repo list --search "react"\` — find reusable repos
|
|
38
|
+
- \`aib code-repo link <postId> <repoId>\` — link existing repo (N:1 sharing)
|
|
39
|
+
- \`aib code-repo patch-file <postId> --file-path src/index.ts --content-file new.ts\`
|
|
40
|
+
|
|
41
|
+
## Knowledge base
|
|
42
|
+
- \`aib knowledge find "Promise"\` — find or suggest term entry
|
|
43
|
+
- \`aib knowledge list --domain math\` — list math concepts
|
|
44
|
+
|
|
45
|
+
## Scout (research material)
|
|
46
|
+
- \`aib scout search "LangGraph" --min-score 7 --json\` — search tech items
|
|
47
|
+
- \`aib scout add-refs <postId> --refs-file refs.json\` — add references to post
|
|
48
|
+
|
|
49
|
+
## Series
|
|
50
|
+
- \`aib series list\` — list all series
|
|
51
|
+
- \`aib series create --slug "guide" --title "Guide"\`
|
|
52
|
+
- \`aib series add-post <seriesId> <postId>\`
|
|
53
|
+
|
|
54
|
+
## Build syntax blocks
|
|
55
|
+
- \`aib build frontmatter --title "..." --status draft\`
|
|
56
|
+
- \`aib build image --url "..." --alt "..." --width 50\`
|
|
57
|
+
- \`aib build reference --key "vaswani2017" --title "..." --type paper\`
|
|
58
|
+
- \`aib build live-demo --title "Demo" --files-file files.json\`
|
|
59
|
+
- \`aib build code-group tabs.json\`
|
|
60
|
+
- \`aib build details --title "Details" --content-file body.md\`
|
|
61
|
+
- \`aib build timeline --events-file events.json\`
|
|
62
|
+
|
|
63
|
+
## Upload images
|
|
64
|
+
- \`aib upload image-from-url <url>\` — upload to blog OSS
|
|
65
|
+
|
|
66
|
+
## Conventions
|
|
67
|
+
- Add \`--json\` for structured JSON output
|
|
68
|
+
- Use \`--content-file <path>\` for large content
|
|
69
|
+
- Pipe: \`cat draft.md | aib post validate --stdin\`
|
|
70
|
+
- Call \`aib <command> --help\` for detailed usage`;
|
|
71
|
+
export function registerSkillsCommand(program) {
|
|
72
|
+
program
|
|
73
|
+
.command("skills")
|
|
74
|
+
.description("Output a lightweight skills reference for Agent system prompts (~500 tokens)")
|
|
75
|
+
.action(() => {
|
|
76
|
+
console.log(SKILLS_MD);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=skills.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skills.js","sourceRoot":"","sources":["../../src/commands/skills.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AACH,MAAM,SAAS,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mDA0DiC,CAAC;AAEpD,MAAM,UAAU,qBAAqB,CAAC,OAAgB;IACpD,OAAO;SACJ,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,8EAA8E,CAAC;SAC3F,MAAM,CAAC,GAAG,EAAE;QACX,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACzB,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"taxonomy.d.ts","sourceRoot":"","sources":["../../src/commands/taxonomy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAGlD,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,SAAS,GAAG,IAAI,CAmD/E"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { run, isJson } from "./helpers.js";
|
|
2
|
+
export function registerTaxonomyCommands(program, api) {
|
|
3
|
+
const taxonomy = program.command("taxonomy").description("Manage categories and tags");
|
|
4
|
+
// ── taxonomy list ──
|
|
5
|
+
taxonomy
|
|
6
|
+
.command("list")
|
|
7
|
+
.description("List all categories and tags. Call this before creating/updating posts.")
|
|
8
|
+
.option("--json", "Output as JSON")
|
|
9
|
+
.action(async (opts) => {
|
|
10
|
+
await run(() => api.get("/api/cli/taxonomy"), isJson(opts));
|
|
11
|
+
});
|
|
12
|
+
// ── taxonomy create ──
|
|
13
|
+
taxonomy
|
|
14
|
+
.command("create")
|
|
15
|
+
.description("Create a category or tag")
|
|
16
|
+
.requiredOption("--type <type>", "Type: category or tag")
|
|
17
|
+
.requiredOption("--label <label>", "Display name")
|
|
18
|
+
.option("--slug <slug>", "URL slug (auto-generated if empty)")
|
|
19
|
+
.option("--json", "Output as JSON")
|
|
20
|
+
.action(async (opts) => {
|
|
21
|
+
const data = { label: opts.label };
|
|
22
|
+
if (opts.slug)
|
|
23
|
+
data.slug = opts.slug;
|
|
24
|
+
await run(() => api.post("/api/cli/taxonomy", { action: "create", type: opts.type, data }), isJson(opts));
|
|
25
|
+
});
|
|
26
|
+
// ── taxonomy update ──
|
|
27
|
+
taxonomy
|
|
28
|
+
.command("update")
|
|
29
|
+
.description("Update a category or tag")
|
|
30
|
+
.requiredOption("--type <type>", "Type: category or tag")
|
|
31
|
+
.requiredOption("--id <id>", "Entry id (uuid)")
|
|
32
|
+
.requiredOption("--label <label>", "New display name")
|
|
33
|
+
.option("--slug <slug>", "New URL slug")
|
|
34
|
+
.option("--json", "Output as JSON")
|
|
35
|
+
.action(async (opts) => {
|
|
36
|
+
const data = { id: opts.id, label: opts.label };
|
|
37
|
+
if (opts.slug)
|
|
38
|
+
data.slug = opts.slug;
|
|
39
|
+
await run(() => api.post("/api/cli/taxonomy", { action: "update", type: opts.type, data }), isJson(opts));
|
|
40
|
+
});
|
|
41
|
+
// ── taxonomy delete ──
|
|
42
|
+
taxonomy
|
|
43
|
+
.command("delete")
|
|
44
|
+
.description("Permanently delete a category or tag")
|
|
45
|
+
.requiredOption("--type <type>", "Type: category or tag")
|
|
46
|
+
.requiredOption("--id <id>", "Entry id (uuid)")
|
|
47
|
+
.option("--json", "Output as JSON")
|
|
48
|
+
.action(async (opts) => {
|
|
49
|
+
await run(() => api.post("/api/cli/taxonomy", { action: "delete", type: opts.type, id: opts.id }), isJson(opts));
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=taxonomy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"taxonomy.js","sourceRoot":"","sources":["../../src/commands/taxonomy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uploads.d.ts","sourceRoot":"","sources":["../../src/commands/uploads.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAGlD,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,SAAS,GAAG,IAAI,CAiC7E"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { run, isJson } from "./helpers.js";
|
|
3
|
+
export function registerUploadCommands(program, api) {
|
|
4
|
+
const upload = program.command("upload").description("Upload images to blog OSS");
|
|
5
|
+
// ── upload image-from-url ──
|
|
6
|
+
upload
|
|
7
|
+
.command("image-from-url <url>")
|
|
8
|
+
.description("Upload a remote image to blog OSS, returns stable in-site URL")
|
|
9
|
+
.option("--filename <name>", "Override filename (affects OSS path extension)")
|
|
10
|
+
.option("--json", "Output as JSON")
|
|
11
|
+
.action(async (url, opts) => {
|
|
12
|
+
const body = { source: "url", url };
|
|
13
|
+
if (opts.filename)
|
|
14
|
+
body.filename = opts.filename;
|
|
15
|
+
await run(() => api.post("/api/cli/uploads/image", body), isJson(opts));
|
|
16
|
+
});
|
|
17
|
+
// ── upload image-from-base64 ──
|
|
18
|
+
upload
|
|
19
|
+
.command("image-from-base64")
|
|
20
|
+
.description("Upload a base64-encoded image to blog OSS")
|
|
21
|
+
.requiredOption("--base64-file <path>", "File containing pure base64 (no data: prefix)")
|
|
22
|
+
.requiredOption("--content-type <type>", "Image MIME type (e.g. image/png)")
|
|
23
|
+
.option("--filename <name>", "Override filename")
|
|
24
|
+
.option("--json", "Output as JSON")
|
|
25
|
+
.action(async (opts) => {
|
|
26
|
+
const base64 = readFileSync(opts.base64File, "utf-8").trim();
|
|
27
|
+
const body = {
|
|
28
|
+
source: "base64",
|
|
29
|
+
base64,
|
|
30
|
+
contentType: opts.contentType,
|
|
31
|
+
};
|
|
32
|
+
if (opts.filename)
|
|
33
|
+
body.filename = opts.filename;
|
|
34
|
+
await run(() => api.post("/api/cli/uploads/image", body), isJson(opts));
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=uploads.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uploads.js","sourceRoot":"","sources":["../../src/commands/uploads.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAGvC,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAE3C,MAAM,UAAU,sBAAsB,CAAC,OAAgB,EAAE,GAAc;IACrE,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,WAAW,CAAC,2BAA2B,CAAC,CAAC;IAElF,8BAA8B;IAC9B,MAAM;SACH,OAAO,CAAC,sBAAsB,CAAC;SAC/B,WAAW,CAAC,+DAA+D,CAAC;SAC5E,MAAM,CAAC,mBAAmB,EAAE,gDAAgD,CAAC;SAC7E,MAAM,CAAC,QAAQ,EAAE,gBAAgB,CAAC;SAClC,MAAM,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QAC1B,MAAM,IAAI,GAA4B,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;QAC7D,IAAI,IAAI,CAAC,QAAQ;YAAE,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;QACjD,MAAM,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,wBAAwB,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IAC1E,CAAC,CAAC,CAAC;IAEL,iCAAiC;IACjC,MAAM;SACH,OAAO,CAAC,mBAAmB,CAAC;SAC5B,WAAW,CAAC,2CAA2C,CAAC;SACxD,cAAc,CAAC,sBAAsB,EAAE,+CAA+C,CAAC;SACvF,cAAc,CAAC,uBAAuB,EAAE,kCAAkC,CAAC;SAC3E,MAAM,CAAC,mBAAmB,EAAE,mBAAmB,CAAC;SAChD,MAAM,CAAC,QAAQ,EAAE,gBAAgB,CAAC;SAClC,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QACrB,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;QAC7D,MAAM,IAAI,GAA4B;YACpC,MAAM,EAAE,QAAQ;YAChB,MAAM;YACN,WAAW,EAAE,IAAI,CAAC,WAAW;SAC9B,CAAC;QACF,IAAI,IAAI,CAAC,QAAQ;YAAE,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;QACjD,MAAM,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,wBAAwB,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IAC1E,CAAC,CAAC,CAAC;AACP,CAAC"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ai-blog CLI 的运行时配置。
|
|
3
|
+
*
|
|
4
|
+
* 配置优先级(从高到低):
|
|
5
|
+
* 1. 环境变量(AI_BLOG_BASE_URL / AI_BLOG_PAT / AI_BLOG_REQUEST_TIMEOUT_MS)
|
|
6
|
+
* 2. ~/.aib.env 文件(KEY=VALUE 格式,与 .env 相同)
|
|
7
|
+
* 3. ./.aib.env 文件(当前目录,适合项目级覆盖)
|
|
8
|
+
*
|
|
9
|
+
* 任何缺失关键变量的情况都直接 throw,避免起来一个"半残"的 CLI
|
|
10
|
+
* 让用户在调用命令时才看到错误,定位成本高。
|
|
11
|
+
*/
|
|
12
|
+
export interface CliConfig {
|
|
13
|
+
baseUrl: string;
|
|
14
|
+
pat: string;
|
|
15
|
+
requestTimeoutMs: number;
|
|
16
|
+
}
|
|
17
|
+
/** 读取并校验环境变量;返回归一化后的配置对象 */
|
|
18
|
+
export declare function loadCliConfig(env?: NodeJS.ProcessEnv): CliConfig;
|
|
19
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAuDD,4BAA4B;AAC5B,wBAAgB,aAAa,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,GAAG,SAAS,CAqChE"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
5
|
+
/**
|
|
6
|
+
* 解析 .env 格式文件内容为 key-value 对象。
|
|
7
|
+
* 忽略空行和 # 注释行,支持 VALUE 中包含 = 号。
|
|
8
|
+
*/
|
|
9
|
+
function parseEnvFile(content) {
|
|
10
|
+
const result = {};
|
|
11
|
+
for (const line of content.split("\n")) {
|
|
12
|
+
const trimmed = line.trim();
|
|
13
|
+
if (!trimmed || trimmed.startsWith("#")) {
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
const eqIndex = trimmed.indexOf("=");
|
|
17
|
+
if (eqIndex === -1) {
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
const key = trimmed.slice(0, eqIndex).trim();
|
|
21
|
+
let value = trimmed.slice(eqIndex + 1).trim();
|
|
22
|
+
// 去掉首尾引号
|
|
23
|
+
if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
|
|
24
|
+
value = value.slice(1, -1);
|
|
25
|
+
}
|
|
26
|
+
result[key] = value;
|
|
27
|
+
}
|
|
28
|
+
return result;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* 尝试读取 .aib.env 文件并返回解析后的 key-value。
|
|
32
|
+
* 文件不存在时返回空对象,不抛错。
|
|
33
|
+
*/
|
|
34
|
+
function loadEnvFile(filePath) {
|
|
35
|
+
try {
|
|
36
|
+
const content = readFileSync(filePath, "utf-8");
|
|
37
|
+
return parseEnvFile(content);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return {};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* 合并配置源:环境变量优先于 .env 文件。
|
|
45
|
+
* 本地 ./.aib.env 优先于全局 ~/.aib.env。
|
|
46
|
+
*/
|
|
47
|
+
function resolveEnv() {
|
|
48
|
+
const globalEnv = loadEnvFile(join(homedir(), ".aib.env"));
|
|
49
|
+
const localEnv = loadEnvFile(join(process.cwd(), ".aib.env"));
|
|
50
|
+
// 优先级:process.env > local .aib.env > global ~/.aib.env
|
|
51
|
+
return { ...globalEnv, ...localEnv, ...process.env };
|
|
52
|
+
}
|
|
53
|
+
/** 读取并校验环境变量;返回归一化后的配置对象 */
|
|
54
|
+
export function loadCliConfig(env) {
|
|
55
|
+
const resolvedEnv = env ?? resolveEnv();
|
|
56
|
+
const baseUrl = (resolvedEnv.AI_BLOG_BASE_URL ?? "").trim();
|
|
57
|
+
if (!baseUrl) {
|
|
58
|
+
throw new Error("AI_BLOG_BASE_URL is required.\n" +
|
|
59
|
+
"Set it via environment variable or in ~/.aib.env:\n" +
|
|
60
|
+
' AI_BLOG_BASE_URL=https://your-blog.example.com');
|
|
61
|
+
}
|
|
62
|
+
// 去掉末尾的 `/`,避免拼接 URL 时出现 `//` 双斜杠
|
|
63
|
+
const normalizedBaseUrl = baseUrl.replace(/\/+$/, "");
|
|
64
|
+
const pat = (resolvedEnv.AI_BLOG_PAT ?? "").trim();
|
|
65
|
+
if (!pat) {
|
|
66
|
+
throw new Error("AI_BLOG_PAT is required.\n" +
|
|
67
|
+
"Create a token at /admin/settings/tokens, then set it:\n" +
|
|
68
|
+
" export AI_BLOG_PAT=aib_pat_xxx # or put it in ~/.aib.env");
|
|
69
|
+
}
|
|
70
|
+
let requestTimeoutMs = DEFAULT_TIMEOUT_MS;
|
|
71
|
+
const rawTimeout = resolvedEnv.AI_BLOG_REQUEST_TIMEOUT_MS;
|
|
72
|
+
if (rawTimeout) {
|
|
73
|
+
const parsed = Number.parseInt(rawTimeout, 10);
|
|
74
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
75
|
+
requestTimeoutMs = parsed;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
baseUrl: normalizedBaseUrl,
|
|
80
|
+
pat,
|
|
81
|
+
requestTimeoutMs,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.test.d.ts","sourceRoot":"","sources":["../src/config.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { loadCliConfig } from "./config.js";
|
|
3
|
+
/**
|
|
4
|
+
* 验证 loadCliConfig 的边界行为:
|
|
5
|
+
* - 缺关键变量直接抛
|
|
6
|
+
* - URL 末尾斜杠会被归一化
|
|
7
|
+
* - 默认超时存在
|
|
8
|
+
* - AI_BLOG_REQUEST_TIMEOUT_MS 合法时被采纳
|
|
9
|
+
*
|
|
10
|
+
* 注:NodeJS.ProcessEnv 在 @types/node 里被声明为含 NODE_ENV 等必填字段的接口,
|
|
11
|
+
* 但 loadCliConfig 只读取自己关心的几个字段。这里通过 unknown 中转,
|
|
12
|
+
* 让 TypeScript 接受我们构造的"局部 env 对象"。
|
|
13
|
+
*/
|
|
14
|
+
function makeEnv(env) {
|
|
15
|
+
return env;
|
|
16
|
+
}
|
|
17
|
+
describe("loadCliConfig", () => {
|
|
18
|
+
it("缺少 AI_BLOG_BASE_URL 时直接抛错", () => {
|
|
19
|
+
expect(() => loadCliConfig(makeEnv({ AI_BLOG_PAT: "x" }))).toThrow(/AI_BLOG_BASE_URL/);
|
|
20
|
+
});
|
|
21
|
+
it("缺少 AI_BLOG_PAT 时直接抛错", () => {
|
|
22
|
+
expect(() => loadCliConfig(makeEnv({ AI_BLOG_BASE_URL: "https://example.com" }))).toThrow(/AI_BLOG_PAT/);
|
|
23
|
+
});
|
|
24
|
+
it("BASE_URL 末尾的斜杠被归一化", () => {
|
|
25
|
+
const config = loadCliConfig(makeEnv({
|
|
26
|
+
AI_BLOG_BASE_URL: "https://example.com///",
|
|
27
|
+
AI_BLOG_PAT: "aib_pat_xxx",
|
|
28
|
+
}));
|
|
29
|
+
expect(config.baseUrl).toBe("https://example.com");
|
|
30
|
+
});
|
|
31
|
+
it("默认超时为 30s,合法配置覆盖默认值", () => {
|
|
32
|
+
const defaults = loadCliConfig(makeEnv({
|
|
33
|
+
AI_BLOG_BASE_URL: "https://example.com",
|
|
34
|
+
AI_BLOG_PAT: "aib_pat_xxx",
|
|
35
|
+
}));
|
|
36
|
+
expect(defaults.requestTimeoutMs).toBe(30_000);
|
|
37
|
+
const overridden = loadCliConfig(makeEnv({
|
|
38
|
+
AI_BLOG_BASE_URL: "https://example.com",
|
|
39
|
+
AI_BLOG_PAT: "aib_pat_xxx",
|
|
40
|
+
AI_BLOG_REQUEST_TIMEOUT_MS: "5000",
|
|
41
|
+
}));
|
|
42
|
+
expect(overridden.requestTimeoutMs).toBe(5_000);
|
|
43
|
+
});
|
|
44
|
+
it("非法超时值被忽略并回落默认", () => {
|
|
45
|
+
const config = loadCliConfig(makeEnv({
|
|
46
|
+
AI_BLOG_BASE_URL: "https://example.com",
|
|
47
|
+
AI_BLOG_PAT: "aib_pat_xxx",
|
|
48
|
+
AI_BLOG_REQUEST_TIMEOUT_MS: "not-a-number",
|
|
49
|
+
}));
|
|
50
|
+
expect(config.requestTimeoutMs).toBe(30_000);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
//# sourceMappingURL=config.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.test.js","sourceRoot":"","sources":["../src/config.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C;;;;;;;;;;GAUG;AAEH,SAAS,OAAO,CAAC,GAA2B;IAC1C,OAAO,GAAmC,CAAC;AAC7C,CAAC;AAED,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,EAAE,CAAC,2BAA2B,EAAE,GAAG,EAAE;QACnC,MAAM,CAAC,GAAG,EAAE,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACzF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sBAAsB,EAAE,GAAG,EAAE;QAC9B,MAAM,CAAC,GAAG,EAAE,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CACvF,aAAa,CACd,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oBAAoB,EAAE,GAAG,EAAE;QAC5B,MAAM,MAAM,GAAG,aAAa,CAC1B,OAAO,CAAC;YACN,gBAAgB,EAAE,wBAAwB;YAC1C,WAAW,EAAE,aAAa;SAC3B,CAAC,CACH,CAAC;QACF,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qBAAqB,EAAE,GAAG,EAAE;QAC7B,MAAM,QAAQ,GAAG,aAAa,CAC5B,OAAO,CAAC;YACN,gBAAgB,EAAE,qBAAqB;YACvC,WAAW,EAAE,aAAa;SAC3B,CAAC,CACH,CAAC;QACF,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAE/C,MAAM,UAAU,GAAG,aAAa,CAC9B,OAAO,CAAC;YACN,gBAAgB,EAAE,qBAAqB;YACvC,WAAW,EAAE,aAAa;YAC1B,0BAA0B,EAAE,MAAM;SACnC,CAAC,CACH,CAAC;QACF,MAAM,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,eAAe,EAAE,GAAG,EAAE;QACvB,MAAM,MAAM,GAAG,aAAa,CAC1B,OAAO,CAAC;YACN,gBAAgB,EAAE,qBAAqB;YACvC,WAAW,EAAE,aAAa;YAC1B,0BAA0B,EAAE,cAAc;SAC3C,CAAC,CACH,CAAC;QACF,MAAM,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { dirname, resolve } from "node:path";
|
|
5
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
6
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
7
|
+
import { createApiClient } from "./api-client.js";
|
|
8
|
+
import { loadMcpServerConfig } from "./config.js";
|
|
9
|
+
/**
|
|
10
|
+
* 从和入口同级的 package.json 读取版本号(编译后 dist/index.js 位于 dist/,
|
|
11
|
+
* package.json 在其上一级目录)。这样后续发新版时只需改 package.json 一处。
|
|
12
|
+
*/
|
|
13
|
+
function readOwnVersion() {
|
|
14
|
+
const distDir = dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
const pkgPath = resolve(distDir, "..", "package.json");
|
|
16
|
+
try {
|
|
17
|
+
const raw = readFileSync(pkgPath, "utf-8");
|
|
18
|
+
const pkg = JSON.parse(raw);
|
|
19
|
+
return pkg.version ?? "0.0.0";
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return "0.0.0";
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
import { registerBuildTools } from "./tools/build.js";
|
|
26
|
+
import { registerCommentTools } from "./tools/comments.js";
|
|
27
|
+
import { registerDemoTools } from "./tools/demos.js";
|
|
28
|
+
import { registerPostTools } from "./tools/posts.js";
|
|
29
|
+
import { registerTaxonomyTools } from "./tools/taxonomy.js";
|
|
30
|
+
import { registerUploadTools } from "./tools/uploads.js";
|
|
31
|
+
import { registerKnowledgeTools } from "./tools/knowledge.js";
|
|
32
|
+
import { registerScoutTools } from "./tools/scout.js";
|
|
33
|
+
import { registerSeriesTools } from "./tools/series.js";
|
|
34
|
+
import { registerCodeRepoTools } from "./tools/code-repos.js";
|
|
35
|
+
import { registerSyntaxCapabilitiesTools } from "./tools/syntax-capabilities.js";
|
|
36
|
+
import { registerWorkflowTools } from "./tools/workflows.js";
|
|
37
|
+
import { registerSyntaxSpecResources } from "./resources/syntax-spec.js";
|
|
38
|
+
import { registerPrompts } from "./prompts/index.js";
|
|
39
|
+
/**
|
|
40
|
+
* ai-blog MCP server 入口(stdio)。
|
|
41
|
+
*
|
|
42
|
+
* 启动流程:
|
|
43
|
+
* 1. 读取环境变量配置(缺失即直接退出,避免半残运行)
|
|
44
|
+
* 2. 实例化 McpServer,注册 tools / resources
|
|
45
|
+
* 3. 通过 StdioServerTransport 接管 stdin / stdout,进入消息循环
|
|
46
|
+
*
|
|
47
|
+
* 任何运行期错误都会写到 stderr —— 务必避免污染 stdout,
|
|
48
|
+
* 否则会破坏 MCP JSON-RPC 协议帧。
|
|
49
|
+
*/
|
|
50
|
+
async function main() {
|
|
51
|
+
const config = loadMcpServerConfig();
|
|
52
|
+
const api = createApiClient(config);
|
|
53
|
+
const serverVersion = readOwnVersion();
|
|
54
|
+
const server = new McpServer({
|
|
55
|
+
name: "ai-blog-mcp-server",
|
|
56
|
+
version: serverVersion,
|
|
57
|
+
}, {
|
|
58
|
+
capabilities: {
|
|
59
|
+
tools: {},
|
|
60
|
+
resources: {},
|
|
61
|
+
prompts: {},
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
// 注册 tools
|
|
65
|
+
registerPostTools(server, api);
|
|
66
|
+
registerDemoTools(server, api);
|
|
67
|
+
registerCommentTools(server, api);
|
|
68
|
+
registerTaxonomyTools(server, api);
|
|
69
|
+
registerKnowledgeTools(server, api);
|
|
70
|
+
registerBuildTools(server, api);
|
|
71
|
+
registerUploadTools(server, api);
|
|
72
|
+
registerScoutTools(server, api);
|
|
73
|
+
registerSeriesTools(server, api);
|
|
74
|
+
registerCodeRepoTools(server, api);
|
|
75
|
+
registerSyntaxCapabilitiesTools(server);
|
|
76
|
+
registerWorkflowTools(server);
|
|
77
|
+
// 注册 resources
|
|
78
|
+
registerSyntaxSpecResources(server, api);
|
|
79
|
+
// 注册 prompts(写作模板)
|
|
80
|
+
registerPrompts(server);
|
|
81
|
+
const transport = new StdioServerTransport();
|
|
82
|
+
await server.connect(transport);
|
|
83
|
+
// 友好的退出处理:收到信号时先 close 再退出
|
|
84
|
+
const shutdown = async (signal) => {
|
|
85
|
+
process.stderr.write(`[ai-blog-mcp] received ${signal}, shutting down...\n`);
|
|
86
|
+
try {
|
|
87
|
+
await server.close();
|
|
88
|
+
}
|
|
89
|
+
finally {
|
|
90
|
+
process.exit(0);
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
process.on("SIGINT", () => void shutdown("SIGINT"));
|
|
94
|
+
process.on("SIGTERM", () => void shutdown("SIGTERM"));
|
|
95
|
+
}
|
|
96
|
+
main().catch((error) => {
|
|
97
|
+
process.stderr.write(`[ai-blog-mcp] fatal: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
98
|
+
process.exit(1);
|
|
99
|
+
});
|
|
100
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
/**
|
|
3
|
+
* 写作辅助类 MCP Prompts。
|
|
4
|
+
*
|
|
5
|
+
* 每个模板在 messages 开头都引导 AI 先调 `describe_workflows` 获取完整工作流路线图,
|
|
6
|
+
* 再按推荐步骤执行。大文章场景引导使用 outline/section 精准读写工作流。
|
|
7
|
+
*/
|
|
8
|
+
export declare function registerPrompts(server: McpServer): void;
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/prompts/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA4LvD"}
|