aiconductor-cli 1.3.0 → 1.3.2
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/AGENTS.md +47 -56
- package/README.md +33 -2
- package/bin/aiconductor-cli.js +33 -9
- package/package.json +1 -1
- package/src/api.js +69 -14
- package/src/commands/md_publish.js +90 -2
- package/src/commands/xhs.js +0 -2
- package/.codebuddy/skills/aic-web-search/SKILL.md +0 -141
- package/.codebuddy/skills/aic-web-search/references/aiconductor-skills-setup.md +0 -50
- package/.codebuddy/skills/douyin-toolbox/SKILL.md +0 -219
- package/.codebuddy/skills/douyin-toolbox/references/aiconductor-skills-setup.md +0 -50
- package/.codebuddy/skills/npm-cli-publisher/SKILL.md +0 -166
- package/.codebuddy/skills/webpage-publish/SKILL.md +0 -119
- package/.codebuddy/skills/webpage-publish/references/aiconductor-skills-setup.md +0 -50
- package/.codebuddy/skills/xhs-note-publish/SKILL.md +0 -180
- package/.codebuddy/skills/xhs-note-publish/references/aiconductor-skills-setup.md +0 -50
package/AGENTS.md
CHANGED
|
@@ -1,76 +1,67 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Repository Guidelines
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## Project Structure & Module Organization
|
|
4
4
|
|
|
5
|
-
## Commands
|
|
6
|
-
|
|
7
|
-
### 本地运行
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
node bin/aiconductor-cli.js --help # 查看所有命令
|
|
11
|
-
node bin/aiconductor-cli.js <command> --help # 查看子命令选项
|
|
12
|
-
node bin/aiconductor-cli.js search web "关键词" -n 3 -f text # 真实调用示例
|
|
13
5
|
```
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
6
|
+
bin/aiconductor-cli.js CLI entry point (Commander.js)
|
|
7
|
+
src/
|
|
8
|
+
api.js HTTP API client — all platform calls live here
|
|
9
|
+
lib/cli-utils.js Shared helpers: output, error handling, key resolution
|
|
10
|
+
commands/
|
|
11
|
+
html_publish.js `html publish` command
|
|
12
|
+
md_publish.js `webpage publish` command (Markdown/HTML)
|
|
13
|
+
web_search.js `search web` / `search image` commands
|
|
14
|
+
douyin.js All `douyin` subcommands
|
|
15
|
+
xhs.js `xhs note-publish` command
|
|
21
16
|
```
|
|
22
17
|
|
|
23
|
-
|
|
18
|
+
- Commands are thin wrappers: parse options, call `src/api.js`, format output, and exit.
|
|
19
|
+
- Shared logic (key resolution, error formatting, output) goes into `src/lib/cli-utils.js`.
|
|
20
|
+
- Each command file exports an async function; no side effects on load.
|
|
24
21
|
|
|
25
|
-
##
|
|
22
|
+
## Build, Test, and Development Commands
|
|
26
23
|
|
|
27
|
-
|
|
24
|
+
- `node bin/aiconductor-cli.js --help` — run locally without a build step
|
|
25
|
+
- `npm install` — install the single runtime dependency (`commander`)
|
|
26
|
+
- No test suite currently exists
|
|
28
27
|
|
|
29
|
-
|
|
30
|
-
bin/aiconductor-cli.js ← 入口层:commander 注册子命令树,不含业务逻辑
|
|
31
|
-
src/api.js ← API 层:HTTPS 请求封装 + API Key 解析
|
|
32
|
-
src/lib/cli-utils.js ← 共享工具:output/outputError/key/error 统一处理
|
|
33
|
-
src/commands/<topic>.js ← 命令层:参数组装、调用 API、格式化输出
|
|
34
|
-
```
|
|
28
|
+
## Coding Style & Naming Conventions
|
|
35
29
|
|
|
36
|
-
|
|
30
|
+
- 2-space indentation, no tabs
|
|
31
|
+
- CommonJS (`require` / `module.exports`), no ESM
|
|
32
|
+
- camelCase for JS identifiers; snake_case for API request fields
|
|
33
|
+
- Every exported function gets a JSDoc block describing parameters and return type
|
|
34
|
+
- Use `const` by default; `let` only when reassignment is needed
|
|
35
|
+
- No formatter or linter configured — follow surrounding code style
|
|
37
36
|
|
|
38
|
-
|
|
39
|
-
- `resolveApiKey(optionKey)` — 三级优先级:`-k` 参数 > `AICONDUCTOR_API_KEY` 环境变量 > `.env` 文件
|
|
40
|
-
- `httpPost()` — 通用 HTTPS POST(端口 443),统一处理 HTTP 错误和网络错误
|
|
41
|
-
- 每个 API 端点对应一个独立函数,通过 `stripBilling()` 过滤计费信息
|
|
42
|
-
- 新增 API 端点只改此文件
|
|
37
|
+
## Error Handling
|
|
43
38
|
|
|
44
|
-
|
|
45
|
-
-
|
|
46
|
-
-
|
|
47
|
-
- `resolveKeyOrExit(optionKey, format)` — 解析 API Key,失败自动输出错误并 exit(1)
|
|
48
|
-
- `handleApiError(err, format)` — 统一处理 http_error / network_error / 未知错误
|
|
49
|
-
- `safeParseInt(value, defaultValue)` — 安全整数解析,防 NaN
|
|
39
|
+
- Never swallow errors with empty `catch`. At minimum, log and re-throw.
|
|
40
|
+
- API errors carry a `.type` property (`http_error`, `network_error`, `validation_error`, `api_error`)
|
|
41
|
+
- Commands exit with code 0 on success, 1 on failure
|
|
50
42
|
|
|
51
|
-
|
|
52
|
-
- 导入 `cli-utils` 的 4 个共享函数,消除重复代码
|
|
53
|
-
- 业务逻辑函数接收 argument + options,组装参数 → 调 API → `outputResult`
|
|
54
|
-
- 成功 `exit(0)`,失败 `exit(1)`
|
|
43
|
+
## Testing Guidelines
|
|
55
44
|
|
|
56
|
-
|
|
45
|
+
No test framework is wired up yet. If adding tests:
|
|
46
|
+
- Use a standard Node.js test runner or Jest
|
|
47
|
+
- Place test files next to the module they cover (`src/commands/web_search.test.js`)
|
|
57
48
|
|
|
58
|
-
|
|
59
|
-
- **JSON(默认)**:成功 stdout `{ success: true, data: {...} }`,失败 stderr `{ success: false, error: "..." }`
|
|
60
|
-
- **text**:人类可读格式,成功以 `✓` 开头
|
|
49
|
+
## Commit & Pull Request Guidelines
|
|
61
50
|
|
|
62
|
-
|
|
51
|
+
From the existing history:
|
|
63
52
|
|
|
64
|
-
|
|
65
|
-
-
|
|
66
|
-
-
|
|
53
|
+
- Messages are in English, often prefixed with conventional commit tags: `feat:`, `fix:`, `refactor:`, `chore:`, `style:`, `ci:`
|
|
54
|
+
- Agent-authored commits include `[Agent]` before the description
|
|
55
|
+
- PRs should describe the change, note API surface impacts, and keep diffs focused
|
|
67
56
|
|
|
68
|
-
|
|
57
|
+
## Adding a New Command
|
|
69
58
|
|
|
70
|
-
1. `src/
|
|
71
|
-
2. `src/
|
|
72
|
-
3. `bin/aiconductor-cli.js
|
|
59
|
+
1. Create `src/commands/<name>.js` with an async handler that calls `src/api.js`
|
|
60
|
+
2. Add the API function to `src/api.js` if it calls a new endpoint
|
|
61
|
+
3. Register the subcommand in `bin/aiconductor-cli.js`
|
|
62
|
+
4. Update `README.md` — command synopsis, option table, and example
|
|
73
63
|
|
|
74
|
-
|
|
64
|
+
## Security & Configuration
|
|
75
65
|
|
|
76
|
-
|
|
66
|
+
- API Key is read from three sources in order: `-k` CLI argument → `AICONDUCTOR_API_KEY` env var → `.env` file
|
|
67
|
+
- Never commit `.env` files or hardcoded credentials
|
package/README.md
CHANGED
|
@@ -77,6 +77,36 @@ aiconductor-cli webpage publish index.html -t my-page
|
|
|
77
77
|
aiconductor-cli webpage publish "# Hello\n\nThis is **markdown**" -t hello
|
|
78
78
|
```
|
|
79
79
|
|
|
80
|
+
### 管理已发布网页
|
|
81
|
+
|
|
82
|
+
查询当前 API Key 所属用户的网页发布记录:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
aiconductor-cli webpage list [选项]
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
| 选项 | 说明 | 默认值 |
|
|
89
|
+
|------|------|--------|
|
|
90
|
+
| `--page` | 页码,最小为 1 | 1 |
|
|
91
|
+
| `--page-size` | 每页条数,范围为 1 到 100 | 20 |
|
|
92
|
+
| `-k, --api-key` | API Key(可选) | 环境变量 |
|
|
93
|
+
| `-f, --format` | 输出格式:`json` 或 `text` | json |
|
|
94
|
+
|
|
95
|
+
删除指定的已发布网页及其公开 HTML 对象:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
aiconductor-cli webpage delete <webpage_id> [选项]
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
示例:
|
|
102
|
+
```bash
|
|
103
|
+
# 查看第 2 页,每页 50 条
|
|
104
|
+
aiconductor-cli webpage list --page 2 --page-size 50 -f text
|
|
105
|
+
|
|
106
|
+
# 删除 ID 为 123 的网页
|
|
107
|
+
aiconductor-cli webpage delete 123
|
|
108
|
+
```
|
|
109
|
+
|
|
80
110
|
### 网页搜索
|
|
81
111
|
|
|
82
112
|
```bash
|
|
@@ -264,7 +294,6 @@ aiconductor-cli xhs note-publish video \
|
|
|
264
294
|
| `--cover-file <path>` | 本地视频封面路径 | 无 |
|
|
265
295
|
| `--cover-base64 <data>` | 封面 Base64、data URL 或 `@文本文件` | 无 |
|
|
266
296
|
| `--upload-valid-days <days>` | 本地/Base64 素材临时链接有效期:1 或 7 天 | 7 |
|
|
267
|
-
| `--transfer-to-oss` | 让发布接口再次转存全部素材 | 关闭 |
|
|
268
297
|
|
|
269
298
|
本地文件会在 CLI 内转换成 data URL,与 Base64 素材统一通过 JSON 接口免费上传为临时公开 URL,不使用 multipart。图片合计最多 18 张、单张最多 32MB;本地/Base64 视频受上传接口限制为 100MB,URL 视频上限为 200MB。
|
|
270
299
|
|
|
@@ -288,7 +317,7 @@ aiconductor-cli xhs note-publish video \
|
|
|
288
317
|
| 抖音用户搜索 | 0.15 元/页 |
|
|
289
318
|
| 抖音视频搜索 | 0.15 元/页 |
|
|
290
319
|
| 抖音链接转文字 | 0.01 元/次 + 0.0003 元/秒 |
|
|
291
|
-
| 小红书扫码发布 | 0.05
|
|
320
|
+
| 小红书扫码发布 | 0.05 元/次 |
|
|
292
321
|
|
|
293
322
|
> 搜索/调用失败不扣费,仅成功时计费。
|
|
294
323
|
|
|
@@ -300,6 +329,8 @@ cd aiconductor-cli
|
|
|
300
329
|
npm install
|
|
301
330
|
node bin/aiconductor-cli.js --help # 本地运行
|
|
302
331
|
```
|
|
332
|
+
|
|
333
|
+
贡献前请阅读 [AGENTS.md](./AGENTS.md) 了解项目结构、编码风格与提交流程。
|
|
303
334
|
|
|
304
335
|
## License
|
|
305
336
|
|
package/bin/aiconductor-cli.js
CHANGED
|
@@ -2,7 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
const { Command } = require("commander");
|
|
4
4
|
const { publishCommand } = require("../src/commands/html_publish");
|
|
5
|
-
const {
|
|
5
|
+
const {
|
|
6
|
+
publishCommand: webpagePublishCommand,
|
|
7
|
+
listCommand: webpageListCommand,
|
|
8
|
+
deleteCommand: webpageDeleteCommand,
|
|
9
|
+
} = require("../src/commands/md_publish");
|
|
6
10
|
const { searchCommand } = require("../src/commands/web_search");
|
|
7
11
|
const {
|
|
8
12
|
videoInfoCommand,
|
|
@@ -47,8 +51,8 @@ const webpageCmd = program
|
|
|
47
51
|
.command("webpage")
|
|
48
52
|
.description("网页发布(支持 Markdown 和 HTML)");
|
|
49
53
|
|
|
50
|
-
webpageCmd
|
|
51
|
-
.command("publish")
|
|
54
|
+
webpageCmd
|
|
55
|
+
.command("publish")
|
|
52
56
|
.description("将 Markdown 或 HTML 发布为在线网页(自动识别类型)")
|
|
53
57
|
.argument("<input>", "Markdown/HTML 文本字符串或文件路径")
|
|
54
58
|
.option("-k, --api-key <key>", "AIConductor API Key(也可通过 AICONDUCTOR_API_KEY 环境变量或 .env 文件提供)")
|
|
@@ -56,9 +60,30 @@ webpageCmd
|
|
|
56
60
|
.option("--type <type>", "强制指定类型:md 或 html(默认自动识别)")
|
|
57
61
|
.option("--copy-button", "添加代码复制按钮(仅 Markdown 类型生效)")
|
|
58
62
|
.option("-f, --format <format>", "输出格式:json 或 text", "json")
|
|
59
|
-
.action((input, options) => {
|
|
60
|
-
webpagePublishCommand(input, options);
|
|
61
|
-
});
|
|
63
|
+
.action((input, options) => {
|
|
64
|
+
webpagePublishCommand(input, options);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
webpageCmd
|
|
68
|
+
.command("list")
|
|
69
|
+
.description("查询当前用户已发布的网页")
|
|
70
|
+
.option("-k, --api-key <key>", "AIConductor API Key(也可通过 AICONDUCTOR_API_KEY 环境变量或 .env 文件提供)")
|
|
71
|
+
.option("--page <page>", "页码,最小为 1", "1")
|
|
72
|
+
.option("--page-size <size>", "每页条数,范围为 1 到 100", "20")
|
|
73
|
+
.option("-f, --format <format>", "输出格式:json 或 text", "json")
|
|
74
|
+
.action((options) => {
|
|
75
|
+
webpageListCommand(options);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
webpageCmd
|
|
79
|
+
.command("delete")
|
|
80
|
+
.description("删除当前用户指定的已发布网页")
|
|
81
|
+
.argument("<webpage_id>", "网页记录 ID,可通过 webpage list 获取")
|
|
82
|
+
.option("-k, --api-key <key>", "AIConductor API Key(也可通过 AICONDUCTOR_API_KEY 环境变量或 .env 文件提供)")
|
|
83
|
+
.option("-f, --format <format>", "输出格式:json 或 text", "json")
|
|
84
|
+
.action((webpageId, options) => {
|
|
85
|
+
webpageDeleteCommand(webpageId, options);
|
|
86
|
+
});
|
|
62
87
|
|
|
63
88
|
// search 子命令组
|
|
64
89
|
const searchCmd = program
|
|
@@ -176,7 +201,7 @@ const xhsCmd = program
|
|
|
176
201
|
|
|
177
202
|
xhsCmd
|
|
178
203
|
.command("note-publish")
|
|
179
|
-
.description("
|
|
204
|
+
.description("生成小红书扫码发布页与二维码(0.05元/次)")
|
|
180
205
|
.argument("<type>", "笔记类型:normal(图文)或 video(视频)")
|
|
181
206
|
.option("-k, --api-key <key>", "AIConductor API Key")
|
|
182
207
|
.option("-t, --title <title>", "笔记标题(最多20字符)")
|
|
@@ -191,8 +216,7 @@ xhsCmd
|
|
|
191
216
|
.option("--cover-file <path>", "本地视频封面路径(仅video类型有效)")
|
|
192
217
|
.option("--cover-base64 <data>", "视频封面Base64/data URL/@文本文件(仅video类型)")
|
|
193
218
|
.option("--upload-valid-days <days>", "本地/Base64素材临时链接有效期:1或7天", "7")
|
|
194
|
-
.option("--
|
|
195
|
-
.option("-f, --format <format>", "输出格式:json 或 text", "json")
|
|
219
|
+
.option("-f, --format <format>", "输出格式:json 或 text", "json")
|
|
196
220
|
.action((type, options) => { notePublishCommand(type, options); });
|
|
197
221
|
|
|
198
222
|
// 未知命令处理
|
package/package.json
CHANGED
package/src/api.js
CHANGED
|
@@ -3,9 +3,11 @@ const fs = require("fs");
|
|
|
3
3
|
const path = require("path");
|
|
4
4
|
|
|
5
5
|
const API_HOST = "plugin.aiconductor.fun";
|
|
6
|
-
const API_PATH = "/api/html_publish";
|
|
7
|
-
const MD_TO_HTML_API_PATH = "/api/md_to_html";
|
|
8
|
-
const
|
|
6
|
+
const API_PATH = "/api/html_publish";
|
|
7
|
+
const MD_TO_HTML_API_PATH = "/api/md_to_html";
|
|
8
|
+
const PUBLISHED_WEBPAGES_API_PATH = "/api/published_webpages";
|
|
9
|
+
const DELETE_PUBLISHED_WEBPAGE_API_PATH = "/api/delete_published_webpage";
|
|
10
|
+
const SEARCH_API_PATH = "/api/aic_web_search";
|
|
9
11
|
const DOUYIN_VIDEO_INFO_PATH = "/api/douyin_video_info";
|
|
10
12
|
const DOUYIN_VIDEO_COMMENTS_PATH = "/api/douyin_video_comments";
|
|
11
13
|
const DOUYIN_USER_VIDEOS_PATH = "/api/douyin_user_videos";
|
|
@@ -60,7 +62,7 @@ function stripBilling(resp) {
|
|
|
60
62
|
* @param {*} value
|
|
61
63
|
* @param {string} name
|
|
62
64
|
*/
|
|
63
|
-
function validateInt(value, name) {
|
|
65
|
+
function validateInt(value, name) {
|
|
64
66
|
if (value === undefined || value === null) return;
|
|
65
67
|
const n = Number(value);
|
|
66
68
|
if (!Number.isInteger(n) || n < 0) {
|
|
@@ -124,6 +126,22 @@ function httpPost(host, requestPath, body, headers = {}) {
|
|
|
124
126
|
});
|
|
125
127
|
}
|
|
126
128
|
|
|
129
|
+
/**
|
|
130
|
+
* 校验正整数参数
|
|
131
|
+
* @param {*} value
|
|
132
|
+
* @param {string} name
|
|
133
|
+
* @param {number} [max]
|
|
134
|
+
* @returns {number}
|
|
135
|
+
*/
|
|
136
|
+
function validatePositiveInt(value, name, max) {
|
|
137
|
+
const n = Number(value);
|
|
138
|
+
if (!Number.isInteger(n) || n < 1 || (max !== undefined && n > max)) {
|
|
139
|
+
const range = max === undefined ? "大于等于 1" : `1 到 ${max}`;
|
|
140
|
+
throw Object.assign(new Error(`${name} 须为 ${range} 的整数,收到: ${value}`), { type: "validation_error" });
|
|
141
|
+
}
|
|
142
|
+
return n;
|
|
143
|
+
}
|
|
144
|
+
|
|
127
145
|
function contentTypeForFilename(filename) {
|
|
128
146
|
const types = {
|
|
129
147
|
".avif": "image/avif",
|
|
@@ -291,7 +309,7 @@ async function webSearch(apiKey, params) {
|
|
|
291
309
|
* @param {boolean} [addCopyButton=true]
|
|
292
310
|
* @returns {Promise<{success: boolean, message: string, file_url: string, online_url: string}>}
|
|
293
311
|
*/
|
|
294
|
-
async function publishMdToHtml(apiKey, mdText, filename, addCopyButton = false) {
|
|
312
|
+
async function publishMdToHtml(apiKey, mdText, filename, addCopyButton = false) {
|
|
295
313
|
const reqBody = {
|
|
296
314
|
api_key: apiKey,
|
|
297
315
|
md_text: mdText,
|
|
@@ -301,8 +319,44 @@ async function publishMdToHtml(apiKey, mdText, filename, addCopyButton = false)
|
|
|
301
319
|
|
|
302
320
|
const resp = await httpPost(API_HOST, MD_TO_HTML_API_PATH, reqBody);
|
|
303
321
|
|
|
304
|
-
return stripBilling(resp);
|
|
305
|
-
}
|
|
322
|
+
return stripBilling(resp);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* 查询当前用户已发布的网页列表
|
|
327
|
+
* @param {string} apiKey
|
|
328
|
+
* @param {{page?: number|string, pageSize?: number|string}} [params]
|
|
329
|
+
* @returns {Promise<{success: boolean, webpages: Array, total: number, page: number, page_size: number}>}
|
|
330
|
+
*/
|
|
331
|
+
async function getPublishedWebpages(apiKey, params = {}) {
|
|
332
|
+
const page = validatePositiveInt(params.page === undefined ? 1 : params.page, "page");
|
|
333
|
+
const pageSize = validatePositiveInt(params.pageSize === undefined ? 20 : params.pageSize, "page_size", 100);
|
|
334
|
+
const resp = await httpPost(API_HOST, PUBLISHED_WEBPAGES_API_PATH, {
|
|
335
|
+
page,
|
|
336
|
+
page_size: pageSize,
|
|
337
|
+
}, {
|
|
338
|
+
Authorization: `Bearer ${apiKey}`,
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
return stripBilling(resp);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* 删除当前用户指定的已发布网页
|
|
346
|
+
* @param {string} apiKey
|
|
347
|
+
* @param {number|string} webpageId
|
|
348
|
+
* @returns {Promise<{success: boolean, message: string, webpage_id: number|null}>}
|
|
349
|
+
*/
|
|
350
|
+
async function deletePublishedWebpage(apiKey, webpageId) {
|
|
351
|
+
const id = validatePositiveInt(webpageId, "webpage_id");
|
|
352
|
+
const resp = await httpPost(API_HOST, DELETE_PUBLISHED_WEBPAGE_API_PATH, {
|
|
353
|
+
webpage_id: id,
|
|
354
|
+
}, {
|
|
355
|
+
Authorization: `Bearer ${apiKey}`,
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
return stripBilling(resp);
|
|
359
|
+
}
|
|
306
360
|
|
|
307
361
|
/**
|
|
308
362
|
* 获取抖音作品详情
|
|
@@ -417,17 +471,16 @@ async function douyinLinkToText(apiKey, videoUrl) {
|
|
|
417
471
|
/**
|
|
418
472
|
* 小红书笔记发布 — 生成扫码发布 H5 页面与二维码
|
|
419
473
|
* @param {string} apiKey
|
|
420
|
-
* @param {{ type: string, title?: string, content?: string, images?: string[], video?: string, cover?: string
|
|
474
|
+
* @param {{ type: string, title?: string, content?: string, images?: string[], video?: string, cover?: string }} params
|
|
421
475
|
* @returns {Promise<object>}
|
|
422
476
|
*/
|
|
423
477
|
async function xhsNotePublish(apiKey, params) {
|
|
424
478
|
const body = { type: params.type };
|
|
425
479
|
if (params.title) body.title = params.title;
|
|
426
480
|
if (params.content) body.content = params.content;
|
|
427
|
-
if (params.images && params.images.length > 0) body.images = params.images;
|
|
428
|
-
if (params.video) body.video = params.video;
|
|
429
|
-
if (params.cover) body.cover = params.cover;
|
|
430
|
-
if (params.transferToOss !== undefined) body.transfer_to_oss = params.transferToOss;
|
|
481
|
+
if (params.images && params.images.length > 0) body.images = params.images;
|
|
482
|
+
if (params.video) body.video = params.video;
|
|
483
|
+
if (params.cover) body.cover = params.cover;
|
|
431
484
|
|
|
432
485
|
const resp = await httpPost(API_HOST, XHS_NOTE_PUBLISH_PATH, body, {
|
|
433
486
|
Authorization: `Bearer ${apiKey}`,
|
|
@@ -437,8 +490,10 @@ async function xhsNotePublish(apiKey, params) {
|
|
|
437
490
|
|
|
438
491
|
module.exports = {
|
|
439
492
|
resolveApiKey,
|
|
440
|
-
publishHtml,
|
|
441
|
-
publishMdToHtml,
|
|
493
|
+
publishHtml,
|
|
494
|
+
publishMdToHtml,
|
|
495
|
+
getPublishedWebpages,
|
|
496
|
+
deletePublishedWebpage,
|
|
442
497
|
webSearch,
|
|
443
498
|
douyinVideoInfo,
|
|
444
499
|
douyinVideoComments,
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
const fs = require("fs");
|
|
2
2
|
const path = require("path");
|
|
3
|
-
const {
|
|
3
|
+
const {
|
|
4
|
+
publishHtml,
|
|
5
|
+
publishMdToHtml,
|
|
6
|
+
getPublishedWebpages,
|
|
7
|
+
deletePublishedWebpage,
|
|
8
|
+
} = require("../api");
|
|
4
9
|
const { outputResult, outputError, resolveKeyOrExit, handleApiError } = require("../lib/cli-utils");
|
|
5
10
|
|
|
6
11
|
/**
|
|
@@ -51,6 +56,47 @@ function formatPublishText(data) {
|
|
|
51
56
|
return out.trimEnd();
|
|
52
57
|
}
|
|
53
58
|
|
|
59
|
+
/**
|
|
60
|
+
* text 模式的网页列表格式化
|
|
61
|
+
* @param {{webpages?: Array, page?: number, page_size?: number, total?: number}} data
|
|
62
|
+
* @returns {string}
|
|
63
|
+
*/
|
|
64
|
+
function formatWebpageListText(data) {
|
|
65
|
+
const webpages = data.webpages || [];
|
|
66
|
+
if (!webpages.length) return "没有已发布网页。";
|
|
67
|
+
|
|
68
|
+
const header = `已发布网页(第 ${data.page} 页,每页 ${data.page_size} 条,共 ${data.total} 条)`;
|
|
69
|
+
const entries = webpages.map((webpage) => {
|
|
70
|
+
const lines = [`[${webpage.id}] ${webpage.title || "未命名网页"}`];
|
|
71
|
+
if (webpage.online_url) lines.push(` URL: ${webpage.online_url}`);
|
|
72
|
+
if (webpage.source) lines.push(` 来源: ${webpage.source}`);
|
|
73
|
+
if (webpage.created_at) lines.push(` 发布时间: ${webpage.created_at}`);
|
|
74
|
+
return lines.join("\n");
|
|
75
|
+
});
|
|
76
|
+
return [header, ...entries].join("\n");
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* text 模式的网页删除格式化
|
|
81
|
+
* @param {{webpage_id?: number|null, message?: string}} data
|
|
82
|
+
* @returns {string}
|
|
83
|
+
*/
|
|
84
|
+
function formatWebpageDeleteText(data) {
|
|
85
|
+
let output = `已删除网页 ID: ${data.webpage_id}`;
|
|
86
|
+
if (data.message) output += `\n信息: ${data.message}`;
|
|
87
|
+
return output;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* API 业务失败时抛出统一错误
|
|
92
|
+
* @param {{success?: boolean, message?: string}} result
|
|
93
|
+
*/
|
|
94
|
+
function throwOnApiFailure(result) {
|
|
95
|
+
if (result && result.success === false) {
|
|
96
|
+
throw Object.assign(new Error(result.message || "API 请求失败"), { type: "api_error" });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
54
100
|
/**
|
|
55
101
|
* webpage publish 命令 — 统一发布 Markdown 或 HTML 为在线网页
|
|
56
102
|
* @param {string} input - Markdown/HTML 代码或文件路径
|
|
@@ -84,4 +130,46 @@ async function publishCommand(input, options = {}) {
|
|
|
84
130
|
}
|
|
85
131
|
}
|
|
86
132
|
|
|
87
|
-
|
|
133
|
+
/**
|
|
134
|
+
* webpage list 命令 — 查询当前用户已发布的网页
|
|
135
|
+
* @param {{ apiKey?: string, format?: string, page?: string, pageSize?: string }} options
|
|
136
|
+
*/
|
|
137
|
+
async function listCommand(options = {}) {
|
|
138
|
+
const format = options.format || "json";
|
|
139
|
+
const apiKey = resolveKeyOrExit(options.apiKey, format);
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
const result = await getPublishedWebpages(apiKey, {
|
|
143
|
+
page: options.page,
|
|
144
|
+
pageSize: options.pageSize,
|
|
145
|
+
});
|
|
146
|
+
throwOnApiFailure(result);
|
|
147
|
+
outputResult(result, format, formatWebpageListText);
|
|
148
|
+
process.exit(0);
|
|
149
|
+
} catch (err) {
|
|
150
|
+
handleApiError(err, format);
|
|
151
|
+
process.exit(1);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* webpage delete 命令 — 删除指定的已发布网页
|
|
157
|
+
* @param {string} webpageId
|
|
158
|
+
* @param {{ apiKey?: string, format?: string }} options
|
|
159
|
+
*/
|
|
160
|
+
async function deleteCommand(webpageId, options = {}) {
|
|
161
|
+
const format = options.format || "json";
|
|
162
|
+
const apiKey = resolveKeyOrExit(options.apiKey, format);
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
const result = await deletePublishedWebpage(apiKey, webpageId);
|
|
166
|
+
throwOnApiFailure(result);
|
|
167
|
+
outputResult(result, format, formatWebpageDeleteText);
|
|
168
|
+
process.exit(0);
|
|
169
|
+
} catch (err) {
|
|
170
|
+
handleApiError(err, format);
|
|
171
|
+
process.exit(1);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
module.exports = { publishCommand, listCommand, deleteCommand };
|
package/src/commands/xhs.js
CHANGED
|
@@ -88,7 +88,6 @@ function textFormatter(resp) {
|
|
|
88
88
|
lines.push(` H5 地址: ${d.url}`);
|
|
89
89
|
if (resp.message) lines.push(` 消息: ${resp.message}`);
|
|
90
90
|
if (d.qrcode) lines.push(` 二维码: ${d.qrcode.substring(0, 50)}...`);
|
|
91
|
-
if (d.transfer_to_oss) lines.push(` OSS转存: 是`);
|
|
92
91
|
if (d.media) {
|
|
93
92
|
if (d.media.images) lines.push(` 图片: ${d.media.images.length} 张`);
|
|
94
93
|
if (d.media.video) lines.push(` 视频: ${d.media.video}`);
|
|
@@ -214,7 +213,6 @@ async function notePublishCommand(type, options = {}) {
|
|
|
214
213
|
images,
|
|
215
214
|
video,
|
|
216
215
|
cover,
|
|
217
|
-
transferToOss: Boolean(options.transferToOss),
|
|
218
216
|
});
|
|
219
217
|
outputResult(result, format, textFormatter);
|
|
220
218
|
process.exit(0);
|
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: aic-web-search
|
|
3
|
-
description: 通过 AIConductor CLI 进行网页搜索和图片搜索。当用户需要搜索网络信息、查找网页内容、搜索图片时应使用此 Skill。触发词包括:搜索/搜一下/查一下/网页搜索/图片搜索/联网搜索/web search/image search。
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
## 概述
|
|
7
|
-
|
|
8
|
-
此 Skill 通过全局安装的 `aiconductor-cli` 命令行工具,调用融合信息搜索 API,支持网页搜索和图片搜索。
|
|
9
|
-
|
|
10
|
-
## 准备工作
|
|
11
|
-
|
|
12
|
-
### API Key 配置
|
|
13
|
-
|
|
14
|
-
引导用户前往 https://dev.aiconductor.fun/register 注册并获取 API Key。
|
|
15
|
-
|
|
16
|
-
获取后配置环境变量:
|
|
17
|
-
|
|
18
|
-
**macOS / Linux:**
|
|
19
|
-
```bash
|
|
20
|
-
echo 'export AICONDUCTOR_API_KEY="your-api-key"' >> ~/.zshrc && source ~/.zshrc
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
**Windows PowerShell:**
|
|
24
|
-
```powershell
|
|
25
|
-
[Environment]::SetEnvironmentVariable("AICONDUCTOR_API_KEY", "your-api-key", "User")
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
> 也可使用 `.env` 文件(项目目录下写入 `AICONDUCTOR_API_KEY=your-api-key`)。
|
|
29
|
-
|
|
30
|
-
优先级:`-k` 参数 > `AICONDUCTOR_API_KEY` 环境变量 > `.env` 文件。
|
|
31
|
-
|
|
32
|
-
## 搜索流程
|
|
33
|
-
|
|
34
|
-
### 网页搜索
|
|
35
|
-
|
|
36
|
-
使用 JSON 格式(默认,AI 友好)。
|
|
37
|
-
|
|
38
|
-
```bash
|
|
39
|
-
aiconductor-cli search web <搜索关键词> [选项]
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
**选项:**
|
|
43
|
-
|
|
44
|
-
| 选项 | 说明 | 默认值 |
|
|
45
|
-
|------|------|--------|
|
|
46
|
-
| `-n, --count <n>` | 返回条数(最多50) | 5 |
|
|
47
|
-
| `--time-range <range>` | 时间范围过滤 | 无 |
|
|
48
|
-
| `--no-summary` | 不返回内容摘要 | 默认返回摘要 |
|
|
49
|
-
| `--content-format <format>` | 内容格式:`Text` 或 `Markdown` | `Text` |
|
|
50
|
-
| `--auth-level <level>` | 权威来源过滤:`0`=不过滤,`1`=优先权威来源 | `0` |
|
|
51
|
-
| `--query-rewrite` | 开启 Query 改写(口语化/长问题推荐) | 关闭 |
|
|
52
|
-
| `-k, --api-key <key>` | API Key | 环境变量或 .env |
|
|
53
|
-
| `-f, --format <format>` | 输出格式:`json` 或 `text` | `json` |
|
|
54
|
-
|
|
55
|
-
**time_range 可选值:**
|
|
56
|
-
- `OneDay` - 最近一天
|
|
57
|
-
- `OneWeek` - 最近一周
|
|
58
|
-
- `OneMonth` - 最近一月
|
|
59
|
-
- `OneYear` - 最近一年
|
|
60
|
-
- `YYYY-MM-DD..YYYY-MM-DD` - 自定义日期区间,如 `2025-01-01..2025-04-18`
|
|
61
|
-
|
|
62
|
-
**示例:**
|
|
63
|
-
|
|
64
|
-
```bash
|
|
65
|
-
aiconductor-cli search web "北京今日天气" -n 5 --auth-level 1
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
若用户需要人类可读输出:
|
|
69
|
-
|
|
70
|
-
```bash
|
|
71
|
-
aiconductor-cli search web "北京今日天气" -f text
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
### 图片搜索
|
|
75
|
-
|
|
76
|
-
```bash
|
|
77
|
-
aiconductor-cli search image <搜索关键词> [选项]
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
**选项:**
|
|
81
|
-
|
|
82
|
-
| 选项 | 说明 | 默认值 |
|
|
83
|
-
|------|------|--------|
|
|
84
|
-
| `-n, --count <n>` | 返回条数(最多5) | 5 |
|
|
85
|
-
| `--time-range <range>` | 时间范围过滤 | 无 |
|
|
86
|
-
| `-k, --api-key <key>` | API Key | 环境变量或 .env |
|
|
87
|
-
| `-f, --format <format>` | 输出格式:`json` 或 `text` | `json` |
|
|
88
|
-
|
|
89
|
-
**示例:**
|
|
90
|
-
|
|
91
|
-
```bash
|
|
92
|
-
aiconductor-cli search image "长城风景" -n 3
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
### 解析输出
|
|
96
|
-
|
|
97
|
-
JSON 格式输出示例(网页搜索):
|
|
98
|
-
```json
|
|
99
|
-
{
|
|
100
|
-
"success": true,
|
|
101
|
-
"data": {
|
|
102
|
-
"success": true,
|
|
103
|
-
"message": "搜索成功",
|
|
104
|
-
"data": {
|
|
105
|
-
"ResultCount": 5,
|
|
106
|
-
"TimeCost": 234,
|
|
107
|
-
"WebResults": [
|
|
108
|
-
{
|
|
109
|
-
"SortId": 1,
|
|
110
|
-
"Title": "北京今日天气",
|
|
111
|
-
"SiteName": "中国天气网",
|
|
112
|
-
"Url": "https://...",
|
|
113
|
-
"Snippet": "摘要片段...",
|
|
114
|
-
"Summary": "内容摘要...",
|
|
115
|
-
"Content": "页面正文...",
|
|
116
|
-
"PublishTime": "2025-06-23",
|
|
117
|
-
"RankScore": 0.95
|
|
118
|
-
}
|
|
119
|
-
]
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
从中提取 `data.data.WebResults`(网页搜索)或 `data.data.ImageResults`(图片搜索)作为搜索结果返回给用户。
|
|
126
|
-
|
|
127
|
-
### 错误处理
|
|
128
|
-
|
|
129
|
-
- 缺少 API Key → 提示用户前往 https://dev.aiconductor.fun/register 注册获取
|
|
130
|
-
- CLI 未安装 / command not found → 执行 `npm install -g aiconductor-cli`,安装后重试原命令
|
|
131
|
-
- API 返回错误 → 展示错误信息给用户
|
|
132
|
-
- 搜索失败不扣费,仅成功时计费
|
|
133
|
-
|
|
134
|
-
## 注意事项
|
|
135
|
-
|
|
136
|
-
1. 搜索关键词长度限制 1~100 字符
|
|
137
|
-
2. `need_summary`(摘要)仅对网页搜索有效,图片搜索忽略此参数
|
|
138
|
-
3. `auth_level=1` 适用于医疗、政策、金融等高可信度场景,仅在网页搜索时生效
|
|
139
|
-
4. `query_rewrite=true` 适用于口语化提问或长问题,开启后上游会先改写 query 再搜索
|
|
140
|
-
5. API 按次计费(0.03 元/次),计费信息不会暴露给终端用户
|
|
141
|
-
6. 默认使用 JSON 输出格式,便于 AI Agent 解析
|