@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
package/README.md
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# @page.su/ai-blog-cli
|
|
2
|
+
|
|
3
|
+
CLI tool for AI agents and humans to write, manage, and publish posts on ai-blog.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Global install (recommended)
|
|
9
|
+
npm install -g @page.su/ai-blog-cli
|
|
10
|
+
|
|
11
|
+
# Or use with npx (no install needed)
|
|
12
|
+
npx @page.su/ai-blog-cli post list --json
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Configuration
|
|
16
|
+
|
|
17
|
+
Create a token at `/admin/settings/tokens` in your ai-blog admin panel, then configure via one of these methods:
|
|
18
|
+
|
|
19
|
+
### Option A: ~/.aib.env file (recommended)
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# Create ~/.aib.env with your credentials
|
|
23
|
+
cat > ~/.aib.env << 'EOF'
|
|
24
|
+
AI_BLOG_BASE_URL=https://your-blog.example.com
|
|
25
|
+
AI_BLOG_PAT=aib_pat_your_token_here
|
|
26
|
+
EOF
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Option B: Environment variables
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
export AI_BLOG_BASE_URL=https://your-blog.example.com
|
|
33
|
+
export AI_BLOG_PAT=aib_pat_your_token_here
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Option C: Local .aib.env (project-level override)
|
|
37
|
+
|
|
38
|
+
Create `.aib.env` in your project directory. Overrides `~/.aib.env` but not environment variables.
|
|
39
|
+
|
|
40
|
+
### Configuration priority
|
|
41
|
+
|
|
42
|
+
1. Environment variables (highest)
|
|
43
|
+
2. `./.aib.env` (current directory)
|
|
44
|
+
3. `~/.aib.env` (home directory, lowest)
|
|
45
|
+
|
|
46
|
+
### Optional settings
|
|
47
|
+
|
|
48
|
+
| Variable | Default | Description |
|
|
49
|
+
|---|---|---|
|
|
50
|
+
| `AI_BLOG_REQUEST_TIMEOUT_MS` | `30000` | HTTP request timeout in milliseconds |
|
|
51
|
+
|
|
52
|
+
## Quick start
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# View skills reference (~500 tokens, for Agent system prompts)
|
|
56
|
+
aib skills
|
|
57
|
+
|
|
58
|
+
# View all commands
|
|
59
|
+
aib --help
|
|
60
|
+
|
|
61
|
+
# View command group details
|
|
62
|
+
aib post --help
|
|
63
|
+
|
|
64
|
+
# Posts
|
|
65
|
+
aib post list --json
|
|
66
|
+
aib post create --title "Title" --content-file draft.md --status draft
|
|
67
|
+
aib post validate --content-file draft.md
|
|
68
|
+
aib post publish <id>
|
|
69
|
+
|
|
70
|
+
# Large posts (save tokens)
|
|
71
|
+
aib post outline <id>
|
|
72
|
+
aib post section <id> --index 3
|
|
73
|
+
aib post replace-section <id> --index 3 --content-file new.md
|
|
74
|
+
|
|
75
|
+
# Taxonomy
|
|
76
|
+
aib taxonomy list --json
|
|
77
|
+
|
|
78
|
+
# Knowledge
|
|
79
|
+
aib knowledge find "Promise"
|
|
80
|
+
|
|
81
|
+
# Scout research
|
|
82
|
+
aib scout search "LangGraph" --min-score 7 --json
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Commands
|
|
86
|
+
|
|
87
|
+
| Command group | Description |
|
|
88
|
+
|---|---|
|
|
89
|
+
| `aib post` | Create, read, update, delete, validate posts |
|
|
90
|
+
| `aib demo` | Manage interactive live demos |
|
|
91
|
+
| `aib code-repo` | Manage code repositories (N:1 sharing) |
|
|
92
|
+
| `aib build` | Build advanced markdown syntax blocks |
|
|
93
|
+
| `aib knowledge` | Manage knowledge entries (terms & math) |
|
|
94
|
+
| `aib scout` | Search Scout collected tech items |
|
|
95
|
+
| `aib series` | Manage article series |
|
|
96
|
+
| `aib taxonomy` | Manage categories and tags |
|
|
97
|
+
| `aib comment` | Create comments and manage AI bots |
|
|
98
|
+
| `aib upload` | Upload images to blog OSS |
|
|
99
|
+
| `aib skills` | Output skills reference for Agent prompts |
|
|
100
|
+
|
|
101
|
+
## Conventions
|
|
102
|
+
|
|
103
|
+
- Add `--json` to any command for structured JSON output
|
|
104
|
+
- Use `--content-file <path>` for large content (avoids arg length limits)
|
|
105
|
+
- Pipe support: `cat draft.md | aib post validate --stdin`
|
|
106
|
+
- Call `aib <command> --help` for detailed usage
|
|
107
|
+
|
|
108
|
+
## Agent integration
|
|
109
|
+
|
|
110
|
+
### Python (subprocess)
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
import subprocess, json
|
|
114
|
+
|
|
115
|
+
# Get skills reference for system prompt
|
|
116
|
+
skills = subprocess.run(["aib", "skills"], capture_output=True, text=True).stdout
|
|
117
|
+
|
|
118
|
+
# Execute commands and parse JSON
|
|
119
|
+
result = subprocess.run(["aib", "post", "list", "--json"], capture_output=True, text=True)
|
|
120
|
+
posts = json.loads(result.stdout)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Qoder / Cursor / Claude Code
|
|
124
|
+
|
|
125
|
+
Agents with shell access can call `aib` directly. Inject `aib skills` output into the system prompt for command discovery.
|
|
126
|
+
|
|
127
|
+
## Development
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
# Build
|
|
131
|
+
pnpm build
|
|
132
|
+
|
|
133
|
+
# Dev mode (tsx watch)
|
|
134
|
+
pnpm dev
|
|
135
|
+
|
|
136
|
+
# Test
|
|
137
|
+
pnpm test
|
|
138
|
+
|
|
139
|
+
# Publish
|
|
140
|
+
npm publish --access public
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## License
|
|
144
|
+
|
|
145
|
+
MIT
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { CliConfig } from "./config.js";
|
|
2
|
+
/**
|
|
3
|
+
* 对 ai-blog `/api/cli/**` 的薄 HTTP 客户端。
|
|
4
|
+
*
|
|
5
|
+
* 设计要点:
|
|
6
|
+
* - 自动注入 `Authorization: Bearer <PAT>` 与 `Content-Type: application/json`
|
|
7
|
+
* - 自动加 AbortController 超时(避免 stdio 长 hang)
|
|
8
|
+
* - 把非 2xx 响应统一抛 `ApiClientError`,含 status / message / issues 三类信息,
|
|
9
|
+
* 上层 tool 可以直接把 message 透传给 AI 客户端
|
|
10
|
+
*/
|
|
11
|
+
export declare class ApiClientError extends Error {
|
|
12
|
+
readonly status: number;
|
|
13
|
+
readonly issues?: unknown;
|
|
14
|
+
constructor(message: string, status: number, issues?: unknown);
|
|
15
|
+
}
|
|
16
|
+
export interface ApiClient {
|
|
17
|
+
/** 通用 GET,path 以 `/api/cli/...` 开头 */
|
|
18
|
+
get<T>(path: string, query?: Record<string, string | number | undefined>): Promise<T>;
|
|
19
|
+
/** 通用 POST */
|
|
20
|
+
post<T>(path: string, body?: unknown): Promise<T>;
|
|
21
|
+
/** 通用 PATCH */
|
|
22
|
+
patch<T>(path: string, body?: unknown): Promise<T>;
|
|
23
|
+
/** 通用 DELETE */
|
|
24
|
+
delete<T>(path: string): Promise<T>;
|
|
25
|
+
/** 把响应体当成原始文本读取(用于 syntax-spec 资源) */
|
|
26
|
+
getText(path: string): Promise<string>;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* 当前 CLI 对应的 API 版本号。
|
|
30
|
+
*
|
|
31
|
+
* 此版本号对应后端 `CLI_API_CURRENT_VERSION` 常量。
|
|
32
|
+
* CLI 升级时应同步更新此值,确保与后端兼容。
|
|
33
|
+
*
|
|
34
|
+
* 格式:YYYY-MM-DD(日期版本号,类似 Stripe/GitHub 风格)
|
|
35
|
+
*/
|
|
36
|
+
export declare const CLI_CLIENT_API_VERSION = "2026-05-12";
|
|
37
|
+
/** 工厂函数:根据配置创建一个 ApiClient 实例 */
|
|
38
|
+
export declare function createApiClient(config: CliConfig): ApiClient;
|
|
39
|
+
//# sourceMappingURL=api-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C;;;;;;;;GAQG;AAEH,qBAAa,cAAe,SAAQ,KAAK;IACvC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;gBACd,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO;CAM9D;AAED,MAAM,WAAW,SAAS;IACxB,sCAAsC;IACtC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACtF,cAAc;IACd,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAClD,eAAe;IACf,KAAK,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACnD,gBAAgB;IAChB,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACpC,sCAAsC;IACtC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACxC;AA8DD;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,eAAe,CAAC;AAEnD,iCAAiC;AACjC,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,CAoF5D"}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { resolve, dirname } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
/**
|
|
5
|
+
* 对 ai-blog `/api/cli/**` 的薄 HTTP 客户端。
|
|
6
|
+
*
|
|
7
|
+
* 设计要点:
|
|
8
|
+
* - 自动注入 `Authorization: Bearer <PAT>` 与 `Content-Type: application/json`
|
|
9
|
+
* - 自动加 AbortController 超时(避免 stdio 长 hang)
|
|
10
|
+
* - 把非 2xx 响应统一抛 `ApiClientError`,含 status / message / issues 三类信息,
|
|
11
|
+
* 上层 tool 可以直接把 message 透传给 AI 客户端
|
|
12
|
+
*/
|
|
13
|
+
export class ApiClientError extends Error {
|
|
14
|
+
status;
|
|
15
|
+
issues;
|
|
16
|
+
constructor(message, status, issues) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.name = "ApiClientError";
|
|
19
|
+
this.status = status;
|
|
20
|
+
this.issues = issues;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/** 由 baseUrl + path + query 拼出完整 URL */
|
|
24
|
+
function buildUrl(baseUrl, path, query) {
|
|
25
|
+
const url = new URL(path.startsWith("/") ? path : `/${path}`, baseUrl);
|
|
26
|
+
if (query) {
|
|
27
|
+
for (const [key, value] of Object.entries(query)) {
|
|
28
|
+
if (value === undefined || value === null) {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
url.searchParams.set(key, String(value));
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return url.toString();
|
|
35
|
+
}
|
|
36
|
+
/** 从 fetch Response 解析错误,抛 ApiClientError */
|
|
37
|
+
async function throwApiError(response) {
|
|
38
|
+
let message = `Request failed with status ${response.status}`;
|
|
39
|
+
let issues;
|
|
40
|
+
try {
|
|
41
|
+
const data = (await response.json());
|
|
42
|
+
if (typeof data.message === "string" && data.message) {
|
|
43
|
+
message = data.message;
|
|
44
|
+
}
|
|
45
|
+
if (data.issues !== undefined) {
|
|
46
|
+
issues = data.issues;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// 非 JSON 响应,保留默认 message
|
|
51
|
+
}
|
|
52
|
+
throw new ApiClientError(message, response.status, issues);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* 从 package.json 读取版本号。
|
|
56
|
+
*
|
|
57
|
+
* 编译后 dist/api-client.js 位于 dist/ 目录,package.json 在上一级。
|
|
58
|
+
* 首次读取后缓存,避免重复 I/O。
|
|
59
|
+
*/
|
|
60
|
+
let cachedVersion;
|
|
61
|
+
function readPackageVersion() {
|
|
62
|
+
if (cachedVersion === undefined) {
|
|
63
|
+
try {
|
|
64
|
+
// 编译后 dist/ 下运行时 import.meta.url 指向 dist/api-client.js
|
|
65
|
+
const distDir = dirname(fileURLToPath(import.meta.url));
|
|
66
|
+
const pkgPath = resolve(distDir, "..", "package.json");
|
|
67
|
+
const raw = readFileSync(pkgPath, "utf-8");
|
|
68
|
+
const pkg = JSON.parse(raw);
|
|
69
|
+
cachedVersion = pkg.version ?? "0.0.0";
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
cachedVersion = "0.0.0";
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return cachedVersion;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* 当前 CLI 对应的 API 版本号。
|
|
79
|
+
*
|
|
80
|
+
* 此版本号对应后端 `CLI_API_CURRENT_VERSION` 常量。
|
|
81
|
+
* CLI 升级时应同步更新此值,确保与后端兼容。
|
|
82
|
+
*
|
|
83
|
+
* 格式:YYYY-MM-DD(日期版本号,类似 Stripe/GitHub 风格)
|
|
84
|
+
*/
|
|
85
|
+
export const CLI_CLIENT_API_VERSION = "2026-05-12";
|
|
86
|
+
/** 工厂函数:根据配置创建一个 ApiClient 实例 */
|
|
87
|
+
export function createApiClient(config) {
|
|
88
|
+
const serverVersion = readPackageVersion();
|
|
89
|
+
const baseHeaders = {
|
|
90
|
+
Authorization: `Bearer ${config.pat}`,
|
|
91
|
+
"User-Agent": `ai-blog-cli/${serverVersion}`,
|
|
92
|
+
"x-api-version": CLI_CLIENT_API_VERSION,
|
|
93
|
+
};
|
|
94
|
+
/** 统一发起请求的内部实现 */
|
|
95
|
+
async function request(method, path, init = {}) {
|
|
96
|
+
const controller = new AbortController();
|
|
97
|
+
const timer = setTimeout(() => controller.abort(), config.requestTimeoutMs);
|
|
98
|
+
try {
|
|
99
|
+
const headers = { ...baseHeaders };
|
|
100
|
+
let body;
|
|
101
|
+
if (init.body !== undefined) {
|
|
102
|
+
headers["Content-Type"] = "application/json";
|
|
103
|
+
body = JSON.stringify(init.body);
|
|
104
|
+
}
|
|
105
|
+
const response = await fetch(buildUrl(config.baseUrl, path, init.query), {
|
|
106
|
+
method,
|
|
107
|
+
headers,
|
|
108
|
+
body,
|
|
109
|
+
signal: controller.signal,
|
|
110
|
+
});
|
|
111
|
+
if (!response.ok) {
|
|
112
|
+
return throwApiError(response);
|
|
113
|
+
}
|
|
114
|
+
// 204 No Content
|
|
115
|
+
if (response.status === 204) {
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
119
|
+
if (contentType.includes("application/json")) {
|
|
120
|
+
return (await response.json());
|
|
121
|
+
}
|
|
122
|
+
// 非 JSON 响应(如 text/markdown)当字符串返回
|
|
123
|
+
return (await response.text());
|
|
124
|
+
}
|
|
125
|
+
finally {
|
|
126
|
+
clearTimeout(timer);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return {
|
|
130
|
+
get(path, query) {
|
|
131
|
+
return request("GET", path, { query });
|
|
132
|
+
},
|
|
133
|
+
post(path, body) {
|
|
134
|
+
return request("POST", path, { body });
|
|
135
|
+
},
|
|
136
|
+
patch(path, body) {
|
|
137
|
+
return request("PATCH", path, { body });
|
|
138
|
+
},
|
|
139
|
+
delete(path) {
|
|
140
|
+
return request("DELETE", path);
|
|
141
|
+
},
|
|
142
|
+
async getText(path) {
|
|
143
|
+
// 直接绕过 JSON 自动反序列化,单独走一遍以拿到原始文本
|
|
144
|
+
const controller = new AbortController();
|
|
145
|
+
const timer = setTimeout(() => controller.abort(), config.requestTimeoutMs);
|
|
146
|
+
try {
|
|
147
|
+
const response = await fetch(buildUrl(config.baseUrl, path), {
|
|
148
|
+
method: "GET",
|
|
149
|
+
headers: baseHeaders,
|
|
150
|
+
signal: controller.signal,
|
|
151
|
+
});
|
|
152
|
+
if (!response.ok) {
|
|
153
|
+
return throwApiError(response);
|
|
154
|
+
}
|
|
155
|
+
return await response.text();
|
|
156
|
+
}
|
|
157
|
+
finally {
|
|
158
|
+
clearTimeout(timer);
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=api-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-client.js","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-client.test.d.ts","sourceRoot":"","sources":["../src/api-client.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { ApiClientError, createApiClient } from "./api-client.js";
|
|
3
|
+
/**
|
|
4
|
+
* 验证 ApiClient:
|
|
5
|
+
* - 自动注入 Authorization 头
|
|
6
|
+
* - 自动 JSON 序列化与反序列化
|
|
7
|
+
* - 非 2xx 响应抛 ApiClientError,含 message / issues
|
|
8
|
+
* - getText 直接返回原始字符串
|
|
9
|
+
*/
|
|
10
|
+
describe("createApiClient", () => {
|
|
11
|
+
const config = {
|
|
12
|
+
baseUrl: "https://example.com",
|
|
13
|
+
pat: "aib_pat_xxxxxx",
|
|
14
|
+
requestTimeoutMs: 1_000,
|
|
15
|
+
};
|
|
16
|
+
let fetchSpy;
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
fetchSpy = vi.spyOn(globalThis, "fetch");
|
|
19
|
+
});
|
|
20
|
+
afterEach(() => {
|
|
21
|
+
fetchSpy.mockRestore();
|
|
22
|
+
});
|
|
23
|
+
it("get 成功时返回 JSON 数据并自动带 Authorization", async () => {
|
|
24
|
+
fetchSpy.mockResolvedValue(new Response(JSON.stringify({ ok: true, items: [1, 2] }), {
|
|
25
|
+
status: 200,
|
|
26
|
+
headers: { "content-type": "application/json" },
|
|
27
|
+
}));
|
|
28
|
+
const client = createApiClient(config);
|
|
29
|
+
const result = await client.get("/api/cli/posts", { limit: 5 });
|
|
30
|
+
expect(result.items).toEqual([1, 2]);
|
|
31
|
+
const callArgs = fetchSpy.mock.calls[0];
|
|
32
|
+
expect(callArgs[0]).toBe("https://example.com/api/cli/posts?limit=5");
|
|
33
|
+
const init = callArgs[1];
|
|
34
|
+
const headers = init.headers;
|
|
35
|
+
expect(headers.Authorization).toBe(`Bearer ${config.pat}`);
|
|
36
|
+
});
|
|
37
|
+
it("post 自动 JSON 序列化 body 并带 Content-Type", async () => {
|
|
38
|
+
fetchSpy.mockResolvedValue(new Response(JSON.stringify({ ok: true }), {
|
|
39
|
+
status: 200,
|
|
40
|
+
headers: { "content-type": "application/json" },
|
|
41
|
+
}));
|
|
42
|
+
const client = createApiClient(config);
|
|
43
|
+
await client.post("/api/cli/build/image", { url: "https://x", alt: "y" });
|
|
44
|
+
const init = fetchSpy.mock.calls[0][1];
|
|
45
|
+
expect(init.method).toBe("POST");
|
|
46
|
+
expect(init.body).toBe(JSON.stringify({ url: "https://x", alt: "y" }));
|
|
47
|
+
const headers = init.headers;
|
|
48
|
+
expect(headers["Content-Type"]).toBe("application/json");
|
|
49
|
+
});
|
|
50
|
+
it("非 2xx 响应抛 ApiClientError,并保留 message / issues", async () => {
|
|
51
|
+
fetchSpy.mockResolvedValue(new Response(JSON.stringify({ message: "Invalid payload", issues: { fieldErrors: { title: ["required"] } } }), {
|
|
52
|
+
status: 400,
|
|
53
|
+
headers: { "content-type": "application/json" },
|
|
54
|
+
}));
|
|
55
|
+
const client = createApiClient(config);
|
|
56
|
+
await expect(client.post("/api/cli/posts", {})).rejects.toMatchObject({
|
|
57
|
+
name: "ApiClientError",
|
|
58
|
+
status: 400,
|
|
59
|
+
message: "Invalid payload",
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
it("ApiClientError 是 Error 子类,instanceof 检查通过", async () => {
|
|
63
|
+
fetchSpy.mockResolvedValue(new Response("oops", { status: 500 }));
|
|
64
|
+
const client = createApiClient(config);
|
|
65
|
+
try {
|
|
66
|
+
await client.get("/api/cli/posts");
|
|
67
|
+
throw new Error("expected throw");
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
expect(error).toBeInstanceOf(ApiClientError);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
it("getText 直接返回原始文本", async () => {
|
|
74
|
+
fetchSpy.mockResolvedValue(new Response("# Hello", {
|
|
75
|
+
status: 200,
|
|
76
|
+
headers: { "content-type": "text/markdown" },
|
|
77
|
+
}));
|
|
78
|
+
const client = createApiClient(config);
|
|
79
|
+
const text = await client.getText("/api/cli/syntax-spec/overview");
|
|
80
|
+
expect(text).toBe("# Hello");
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
//# sourceMappingURL=api-client.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-client.test.js","sourceRoot":"","sources":["../src/api-client.test.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
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 { Command } from "commander";
|
|
6
|
+
import { createApiClient } from "./api-client.js";
|
|
7
|
+
import { loadCliConfig } from "./config.js";
|
|
8
|
+
import { registerPostCommands } from "./commands/post.js";
|
|
9
|
+
import { registerDemoCommands } from "./commands/demo.js";
|
|
10
|
+
import { registerCodeRepoCommands } from "./commands/code-repo.js";
|
|
11
|
+
import { registerBuildCommands } from "./commands/build.js";
|
|
12
|
+
import { registerKnowledgeCommands } from "./commands/knowledge.js";
|
|
13
|
+
import { registerScoutCommands } from "./commands/scout.js";
|
|
14
|
+
import { registerSeriesCommands } from "./commands/series.js";
|
|
15
|
+
import { registerTaxonomyCommands } from "./commands/taxonomy.js";
|
|
16
|
+
import { registerCommentCommands } from "./commands/comments.js";
|
|
17
|
+
import { registerUploadCommands } from "./commands/uploads.js";
|
|
18
|
+
import { registerSkillsCommand } from "./commands/skills.js";
|
|
19
|
+
function readOwnVersion() {
|
|
20
|
+
const distDir = dirname(fileURLToPath(import.meta.url));
|
|
21
|
+
const pkgPath = resolve(distDir, "..", "package.json");
|
|
22
|
+
try {
|
|
23
|
+
const raw = readFileSync(pkgPath, "utf-8");
|
|
24
|
+
const pkg = JSON.parse(raw);
|
|
25
|
+
return pkg.version ?? "0.0.0";
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return "0.0.0";
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* 创建延迟初始化的 ApiClient 代理。
|
|
33
|
+
*
|
|
34
|
+
* 在实际方法被调用时才加载配置和创建客户端,
|
|
35
|
+
* 这样 --help / --version / skills 等命令不需要环境变量也能运行。
|
|
36
|
+
*/
|
|
37
|
+
function createLazyApi() {
|
|
38
|
+
let cached = null;
|
|
39
|
+
return new Proxy({}, {
|
|
40
|
+
get(_, prop) {
|
|
41
|
+
if (cached === null) {
|
|
42
|
+
const config = loadCliConfig();
|
|
43
|
+
cached = createApiClient(config);
|
|
44
|
+
}
|
|
45
|
+
const value = Reflect.get(cached, prop);
|
|
46
|
+
return typeof value === "function" ? value.bind(cached) : value;
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
async function main() {
|
|
51
|
+
const api = createLazyApi();
|
|
52
|
+
const program = new Command();
|
|
53
|
+
program
|
|
54
|
+
.name("aib")
|
|
55
|
+
.description("AI Blog CLI — write, manage, and publish posts")
|
|
56
|
+
.version(readOwnVersion());
|
|
57
|
+
registerPostCommands(program, api);
|
|
58
|
+
registerDemoCommands(program, api);
|
|
59
|
+
registerCodeRepoCommands(program, api);
|
|
60
|
+
registerBuildCommands(program, api);
|
|
61
|
+
registerKnowledgeCommands(program, api);
|
|
62
|
+
registerScoutCommands(program, api);
|
|
63
|
+
registerSeriesCommands(program, api);
|
|
64
|
+
registerTaxonomyCommands(program, api);
|
|
65
|
+
registerCommentCommands(program, api);
|
|
66
|
+
registerUploadCommands(program, api);
|
|
67
|
+
registerSkillsCommand(program);
|
|
68
|
+
program.parse();
|
|
69
|
+
}
|
|
70
|
+
main().catch((error) => {
|
|
71
|
+
console.error(`aib: ${error instanceof Error ? error.message : String(error)}`);
|
|
72
|
+
process.exit(1);
|
|
73
|
+
});
|
|
74
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/commands/build.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAGlD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,SAAS,GAAG,IAAI,CA6I5E"}
|