aiconductor-cli 1.6.0 → 1.8.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/README.md +582 -488
- package/bin/aiconductor-cli.js +412 -351
- package/package.json +1 -1
- package/src/api.js +151 -3
- package/src/api.test.js +360 -4
- package/src/commands/content.js +184 -184
- package/src/commands/douyin.js +252 -0
- package/src/commands/html_publish.js +54 -54
- package/src/commands/md_publish.js +175 -175
- package/src/commands/web.js +69 -0
- package/src/commands/web_search.js +99 -99
- package/src/commands/wx.js +116 -116
- package/src/commands/xhs.js +763 -609
- package/src/lib/cli-utils.js +172 -172
- package/src/lib/daren-score.js +91 -0
- package/src/lib/daren-score.test.js +57 -0
- package/AGENTS.md +0 -193
- package/api_docs/aic_web_search.md +0 -84
- package/api_docs/audio_converter.md +0 -51
- package/api_docs/balance_query.md +0 -38
- package/api_docs/bing_search.md +0 -55
- package/api_docs/china_stock_data.md +0 -221
- package/api_docs/delete_published_webpage.md +0 -38
- package/api_docs/doubao_search.md +0 -80
- package/api_docs/douyin_link_to_text_v1.md +0 -50
- package/api_docs/douyin_link_to_text_v2.md +0 -50
- package/api_docs/douyin_user_detail.md +0 -65
- package/api_docs/douyin_user_search.md +0 -58
- package/api_docs/douyin_user_videos.md +0 -49
- package/api_docs/douyin_user_videos_async.md +0 -45
- package/api_docs/douyin_user_videos_async_query.md +0 -41
- package/api_docs/douyin_user_videos_max.md +0 -59
- package/api_docs/douyin_video_comments.md +0 -68
- package/api_docs/douyin_video_info.md +0 -70
- package/api_docs/douyin_video_search.md +0 -60
- package/api_docs/douyin_video_search_fast.md +0 -48
- package/api_docs/fast_ocr.md +0 -51
- package/api_docs/flow_article2podcast_creat.md +0 -41
- package/api_docs/flow_article2podcast_query.md +0 -41
- package/api_docs/flow_wx_history_articles.md +0 -45
- package/api_docs/generate_image.md +0 -68
- package/api_docs/get_duration.md +0 -37
- package/api_docs/hot_list_mixed.md +0 -122
- package/api_docs/html_publish.md +0 -50
- package/api_docs/html_publish_flexible.md +0 -65
- package/api_docs/image_resize.md +0 -58
- package/api_docs/image_to_permalink.md +0 -61
- package/api_docs/link_expander.md +0 -39
- package/api_docs/llm_proxy.md +0 -61
- package/api_docs/markitdown.md +0 -47
- package/api_docs/md_to_docx.md +0 -51
- package/api_docs/md_to_html.md +0 -53
- package/api_docs/md_to_mindmap.md +0 -52
- package/api_docs/md_to_pdf.md +0 -52
- package/api_docs/md_to_png.md +0 -53
- package/api_docs/md_to_txt.md +0 -51
- package/api_docs/md_to_wxhtml.md +0 -64
- package/api_docs/md_to_xhs.md +0 -87
- package/api_docs/md_to_xlsx.md +0 -52
- package/api_docs/portrait_enhancement.md +0 -69
- package/api_docs/published_webpages.md +0 -45
- package/api_docs/r2_uploader.md +0 -51
- package/api_docs/schedulerx_job_create.md +0 -56
- package/api_docs/transaction_query.md +0 -52
- package/api_docs/url_to_markdown.md +0 -50
- package/api_docs/video_download_url.md +0 -57
- package/api_docs/video_download_url_oss.md +0 -55
- package/api_docs/video_to_text.md +0 -48
- package/api_docs/video_to_text_v2.md +0 -49
- package/api_docs/volcengine_face_pretty.md +0 -55
- package/api_docs/volcengine_faceswap.md +0 -55
- package/api_docs/website_screenshot.md +0 -49
- package/api_docs/wx_access_token.md +0 -44
- package/api_docs/wx_article_analysis_data_push.md +0 -43
- package/api_docs/wx_article_data.md +0 -48
- package/api_docs/wx_article_style_rewrite.md +0 -45
- package/api_docs/wx_draft_add.md +0 -56
- package/api_docs/wx_draft_publish.md +0 -47
- package/api_docs/wx_history_articles.md +0 -89
- package/api_docs/wx_image_upload.md +0 -46
- package/api_docs/wx_md_to_html.md +0 -48
- package/api_docs/wx_rss_reader.md +0 -59
- package/api_docs/xhs_hot_list.md +0 -48
- package/api_docs/xhs_note_comments_fast.md +0 -54
- package/api_docs/xhs_note_info_v2.md +0 -45
- package/api_docs/xhs_note_info_v3.md +0 -69
- package/api_docs/xhs_note_publish.md +0 -54
- package/api_docs/xhs_note_search_v2.md +0 -73
- package/api_docs/xhs_note_search_v3.md +0 -54
- package/api_docs/xhs_user_info_v2.md +0 -61
- package/api_docs/xhs_user_notes_v2.md +0 -56
- package/api_docs/xhs_user_notes_v3.md +0 -48
|
@@ -1,54 +1,54 @@
|
|
|
1
|
-
const fs = require("fs");
|
|
2
|
-
const path = require("path");
|
|
3
|
-
const { publishHtml } = require("../api");
|
|
4
|
-
const { outputResult, outputError, resolveKeyOrExit, handleApiError } = require("../lib/cli-utils");
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* 智能解析输入:如果是已存在的文件路径则读取,否则当作原始 HTML 字符串
|
|
8
|
-
* @param {string} input
|
|
9
|
-
* @returns {string}
|
|
10
|
-
*/
|
|
11
|
-
function resolveHtmlInput(input) {
|
|
12
|
-
const trimmed = input.trim();
|
|
13
|
-
try {
|
|
14
|
-
const filePath = path.resolve(trimmed);
|
|
15
|
-
if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
|
|
16
|
-
return fs.readFileSync(filePath, "utf-8");
|
|
17
|
-
}
|
|
18
|
-
} catch (_) {
|
|
19
|
-
// 不是文件路径,按原始字符串处理
|
|
20
|
-
}
|
|
21
|
-
return trimmed;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/** text 模式的自定义格式化 */
|
|
25
|
-
function formatPublishText(data) {
|
|
26
|
-
let out = "✓ 发布成功\n";
|
|
27
|
-
out += ` URL: ${data.online_url}\n`;
|
|
28
|
-
if (data.message) {
|
|
29
|
-
out += ` 信息: ${data.message}\n`;
|
|
30
|
-
}
|
|
31
|
-
return out.trimEnd();
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* publish 命令(子命令模式)
|
|
36
|
-
* @param {string} htmlCode - HTML 代码或文件路径
|
|
37
|
-
* @param {{ apiKey?: string, format?: string, title?: string }} options
|
|
38
|
-
*/
|
|
39
|
-
async function publishCommand(htmlCode, options = {}) {
|
|
40
|
-
const format = options.format || "json";
|
|
41
|
-
const apiKey = resolveKeyOrExit(options.apiKey, format);
|
|
42
|
-
|
|
43
|
-
try {
|
|
44
|
-
const code = resolveHtmlInput(htmlCode);
|
|
45
|
-
const result = await publishHtml(apiKey, code, options.title);
|
|
46
|
-
outputResult(result, format, formatPublishText);
|
|
47
|
-
process.exit(0);
|
|
48
|
-
} catch (err) {
|
|
49
|
-
handleApiError(err, format);
|
|
50
|
-
process.exit(1);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
module.exports = { publishCommand };
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const { publishHtml } = require("../api");
|
|
4
|
+
const { outputResult, outputError, resolveKeyOrExit, handleApiError } = require("../lib/cli-utils");
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 智能解析输入:如果是已存在的文件路径则读取,否则当作原始 HTML 字符串
|
|
8
|
+
* @param {string} input
|
|
9
|
+
* @returns {string}
|
|
10
|
+
*/
|
|
11
|
+
function resolveHtmlInput(input) {
|
|
12
|
+
const trimmed = input.trim();
|
|
13
|
+
try {
|
|
14
|
+
const filePath = path.resolve(trimmed);
|
|
15
|
+
if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
|
|
16
|
+
return fs.readFileSync(filePath, "utf-8");
|
|
17
|
+
}
|
|
18
|
+
} catch (_) {
|
|
19
|
+
// 不是文件路径,按原始字符串处理
|
|
20
|
+
}
|
|
21
|
+
return trimmed;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** text 模式的自定义格式化 */
|
|
25
|
+
function formatPublishText(data) {
|
|
26
|
+
let out = "✓ 发布成功\n";
|
|
27
|
+
out += ` URL: ${data.online_url}\n`;
|
|
28
|
+
if (data.message) {
|
|
29
|
+
out += ` 信息: ${data.message}\n`;
|
|
30
|
+
}
|
|
31
|
+
return out.trimEnd();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* publish 命令(子命令模式)
|
|
36
|
+
* @param {string} htmlCode - HTML 代码或文件路径
|
|
37
|
+
* @param {{ apiKey?: string, format?: string, title?: string }} options
|
|
38
|
+
*/
|
|
39
|
+
async function publishCommand(htmlCode, options = {}) {
|
|
40
|
+
const format = options.format || "json";
|
|
41
|
+
const apiKey = resolveKeyOrExit(options.apiKey, format);
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
const code = resolveHtmlInput(htmlCode);
|
|
45
|
+
const result = await publishHtml(apiKey, code, options.title);
|
|
46
|
+
outputResult(result, format, formatPublishText);
|
|
47
|
+
process.exit(0);
|
|
48
|
+
} catch (err) {
|
|
49
|
+
handleApiError(err, format);
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
module.exports = { publishCommand };
|
|
@@ -1,175 +1,175 @@
|
|
|
1
|
-
const fs = require("fs");
|
|
2
|
-
const path = require("path");
|
|
3
|
-
const {
|
|
4
|
-
publishHtml,
|
|
5
|
-
publishMdToHtml,
|
|
6
|
-
getPublishedWebpages,
|
|
7
|
-
deletePublishedWebpage,
|
|
8
|
-
} = require("../api");
|
|
9
|
-
const { outputResult, outputError, resolveKeyOrExit, handleApiError } = require("../lib/cli-utils");
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* 解析输入内容:如果是已存在的文件路径则读取,否则当作原始字符串
|
|
13
|
-
* @param {string} input
|
|
14
|
-
* @returns {{ content: string, filePath: string|null }}
|
|
15
|
-
*/
|
|
16
|
-
function resolveInput(input) {
|
|
17
|
-
const trimmed = input.trim();
|
|
18
|
-
try {
|
|
19
|
-
const filePath = path.resolve(trimmed);
|
|
20
|
-
if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
|
|
21
|
-
return { content: fs.readFileSync(filePath, "utf-8"), filePath };
|
|
22
|
-
}
|
|
23
|
-
} catch (_) {}
|
|
24
|
-
return { content: trimmed, filePath: null };
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* 自动检测内容类型:Markdown 或 HTML
|
|
29
|
-
* @param {string} content
|
|
30
|
-
* @param {string|null} filePath
|
|
31
|
-
* @returns {"md"|"html"}
|
|
32
|
-
*/
|
|
33
|
-
function detectType(content, filePath) {
|
|
34
|
-
if (filePath) {
|
|
35
|
-
const ext = path.extname(filePath).toLowerCase();
|
|
36
|
-
if (ext === ".md" || ext === ".markdown") return "md";
|
|
37
|
-
if (ext === ".html" || ext === ".htm") return "html";
|
|
38
|
-
}
|
|
39
|
-
const trimmed = content.trim();
|
|
40
|
-
if (trimmed.startsWith("<!DOCTYPE") || trimmed.startsWith("<html") || trimmed.startsWith("<head")) {
|
|
41
|
-
return "html";
|
|
42
|
-
}
|
|
43
|
-
if (trimmed.startsWith("<") && /<\w+[^>]*>/.test(trimmed.substring(0, 200))) {
|
|
44
|
-
return "html";
|
|
45
|
-
}
|
|
46
|
-
return "md";
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/** text 模式的自定义格式化 */
|
|
50
|
-
function formatPublishText(data) {
|
|
51
|
-
let out = "✓ 发布成功\n";
|
|
52
|
-
out += ` URL: ${data.online_url}\n`;
|
|
53
|
-
if (data.message) {
|
|
54
|
-
out += ` 信息: ${data.message}\n`;
|
|
55
|
-
}
|
|
56
|
-
return out.trimEnd();
|
|
57
|
-
}
|
|
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
|
-
|
|
100
|
-
/**
|
|
101
|
-
* webpage publish 命令 — 统一发布 Markdown 或 HTML 为在线网页
|
|
102
|
-
* @param {string} input - Markdown/HTML 代码或文件路径
|
|
103
|
-
* @param {{ apiKey?: string, format?: string, title?: string, copyButton?: boolean, type?: string }} options
|
|
104
|
-
*/
|
|
105
|
-
async function publishCommand(input, options = {}) {
|
|
106
|
-
const format = options.format || "json";
|
|
107
|
-
const apiKey = resolveKeyOrExit(options.apiKey, format);
|
|
108
|
-
|
|
109
|
-
try {
|
|
110
|
-
const { content, filePath } = resolveInput(input);
|
|
111
|
-
let type = options.type;
|
|
112
|
-
if (!type) {
|
|
113
|
-
type = detectType(content, filePath);
|
|
114
|
-
} else if (type !== "md" && type !== "html") {
|
|
115
|
-
outputError({ message: `无效的 --type 值: "${type}",可选: md 或 html` }, format);
|
|
116
|
-
process.exit(1);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
let result;
|
|
120
|
-
if (type === "html") {
|
|
121
|
-
result = await publishHtml(apiKey, content, options.title);
|
|
122
|
-
} else {
|
|
123
|
-
result = await publishMdToHtml(apiKey, content, options.title, options.copyButton === true);
|
|
124
|
-
}
|
|
125
|
-
outputResult(result, format, formatPublishText);
|
|
126
|
-
process.exit(0);
|
|
127
|
-
} catch (err) {
|
|
128
|
-
handleApiError(err, format);
|
|
129
|
-
process.exit(1);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
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 };
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const {
|
|
4
|
+
publishHtml,
|
|
5
|
+
publishMdToHtml,
|
|
6
|
+
getPublishedWebpages,
|
|
7
|
+
deletePublishedWebpage,
|
|
8
|
+
} = require("../api");
|
|
9
|
+
const { outputResult, outputError, resolveKeyOrExit, handleApiError } = require("../lib/cli-utils");
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 解析输入内容:如果是已存在的文件路径则读取,否则当作原始字符串
|
|
13
|
+
* @param {string} input
|
|
14
|
+
* @returns {{ content: string, filePath: string|null }}
|
|
15
|
+
*/
|
|
16
|
+
function resolveInput(input) {
|
|
17
|
+
const trimmed = input.trim();
|
|
18
|
+
try {
|
|
19
|
+
const filePath = path.resolve(trimmed);
|
|
20
|
+
if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
|
|
21
|
+
return { content: fs.readFileSync(filePath, "utf-8"), filePath };
|
|
22
|
+
}
|
|
23
|
+
} catch (_) {}
|
|
24
|
+
return { content: trimmed, filePath: null };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 自动检测内容类型:Markdown 或 HTML
|
|
29
|
+
* @param {string} content
|
|
30
|
+
* @param {string|null} filePath
|
|
31
|
+
* @returns {"md"|"html"}
|
|
32
|
+
*/
|
|
33
|
+
function detectType(content, filePath) {
|
|
34
|
+
if (filePath) {
|
|
35
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
36
|
+
if (ext === ".md" || ext === ".markdown") return "md";
|
|
37
|
+
if (ext === ".html" || ext === ".htm") return "html";
|
|
38
|
+
}
|
|
39
|
+
const trimmed = content.trim();
|
|
40
|
+
if (trimmed.startsWith("<!DOCTYPE") || trimmed.startsWith("<html") || trimmed.startsWith("<head")) {
|
|
41
|
+
return "html";
|
|
42
|
+
}
|
|
43
|
+
if (trimmed.startsWith("<") && /<\w+[^>]*>/.test(trimmed.substring(0, 200))) {
|
|
44
|
+
return "html";
|
|
45
|
+
}
|
|
46
|
+
return "md";
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** text 模式的自定义格式化 */
|
|
50
|
+
function formatPublishText(data) {
|
|
51
|
+
let out = "✓ 发布成功\n";
|
|
52
|
+
out += ` URL: ${data.online_url}\n`;
|
|
53
|
+
if (data.message) {
|
|
54
|
+
out += ` 信息: ${data.message}\n`;
|
|
55
|
+
}
|
|
56
|
+
return out.trimEnd();
|
|
57
|
+
}
|
|
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
|
+
|
|
100
|
+
/**
|
|
101
|
+
* webpage publish 命令 — 统一发布 Markdown 或 HTML 为在线网页
|
|
102
|
+
* @param {string} input - Markdown/HTML 代码或文件路径
|
|
103
|
+
* @param {{ apiKey?: string, format?: string, title?: string, copyButton?: boolean, type?: string }} options
|
|
104
|
+
*/
|
|
105
|
+
async function publishCommand(input, options = {}) {
|
|
106
|
+
const format = options.format || "json";
|
|
107
|
+
const apiKey = resolveKeyOrExit(options.apiKey, format);
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
const { content, filePath } = resolveInput(input);
|
|
111
|
+
let type = options.type;
|
|
112
|
+
if (!type) {
|
|
113
|
+
type = detectType(content, filePath);
|
|
114
|
+
} else if (type !== "md" && type !== "html") {
|
|
115
|
+
outputError({ message: `无效的 --type 值: "${type}",可选: md 或 html` }, format);
|
|
116
|
+
process.exit(1);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
let result;
|
|
120
|
+
if (type === "html") {
|
|
121
|
+
result = await publishHtml(apiKey, content, options.title);
|
|
122
|
+
} else {
|
|
123
|
+
result = await publishMdToHtml(apiKey, content, options.title, options.copyButton === true);
|
|
124
|
+
}
|
|
125
|
+
outputResult(result, format, formatPublishText);
|
|
126
|
+
process.exit(0);
|
|
127
|
+
} catch (err) {
|
|
128
|
+
handleApiError(err, format);
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
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 };
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
const { urlToMarkdown } = require("../api");
|
|
2
|
+
const { outputResult, resolveKeyOrExit, handleApiError } = require("../lib/cli-utils");
|
|
3
|
+
|
|
4
|
+
const TEXT_PREVIEW_LINES = 20;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 把任意网页(含微信公众号文章)转成 Markdown
|
|
8
|
+
* @param {string} url 网页链接
|
|
9
|
+
* @param {object} options
|
|
10
|
+
*/
|
|
11
|
+
async function toMdCommand(url, options = {}) {
|
|
12
|
+
const format = options.format || "json";
|
|
13
|
+
const apiKey = resolveKeyOrExit(options.apiKey, format);
|
|
14
|
+
try {
|
|
15
|
+
const result = await urlToMarkdown(apiKey, { url, timeout: options.timeout });
|
|
16
|
+
outputResult(result, format, formatToMdText);
|
|
17
|
+
process.exit(0);
|
|
18
|
+
} catch (err) { handleApiError(err, format); process.exit(1); }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* 从 Markdown 的 frontmatter 里取一个字段(没有则返回空串)
|
|
23
|
+
* @param {string} markdown
|
|
24
|
+
* @param {string} key
|
|
25
|
+
* @returns {string}
|
|
26
|
+
*/
|
|
27
|
+
function readFrontmatter(markdown, key) {
|
|
28
|
+
if (typeof markdown !== "string" || !markdown.startsWith("---")) return "";
|
|
29
|
+
const end = markdown.indexOf("\n---", 3);
|
|
30
|
+
if (end === -1) return "";
|
|
31
|
+
const block = markdown.slice(3, end);
|
|
32
|
+
const match = block.match(new RegExp(`^${key}:\\s*"?([^"\\n]*)"?\\s*$`, "m"));
|
|
33
|
+
return match ? match[1].trim() : "";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* text 输出:Markdown 结果预览
|
|
38
|
+
* @param {object} resp
|
|
39
|
+
* @returns {string}
|
|
40
|
+
*/
|
|
41
|
+
function formatToMdText(resp = {}) {
|
|
42
|
+
const markdown = typeof resp.markdown === "string" ? resp.markdown : "";
|
|
43
|
+
if (!markdown.trim()) return "✗ 未取到 Markdown 内容";
|
|
44
|
+
|
|
45
|
+
const lines = ["✓ 已转换为 Markdown"];
|
|
46
|
+
const title = readFrontmatter(markdown, "title");
|
|
47
|
+
const words = readFrontmatter(markdown, "word_count");
|
|
48
|
+
if (title) lines.push(` 标题: ${title}`);
|
|
49
|
+
if (words) lines.push(` 字数: ${words}`);
|
|
50
|
+
lines.push(` 字符: ${markdown.length}`);
|
|
51
|
+
|
|
52
|
+
const body = markdown.trimEnd().split("\n");
|
|
53
|
+
// 公众号链如果被裁掉 chksm 参数,微信只返回页面框架,抽出来就是“赞/在看”这类壳
|
|
54
|
+
if (!title && markdown.length < 400) {
|
|
55
|
+
lines.push(" 提示: 内容过少——公众号链接若被裁掉 chksm 参数,只能抓到页面框架,请用原始完整链接");
|
|
56
|
+
}
|
|
57
|
+
lines.push("");
|
|
58
|
+
lines.push(...body.slice(0, TEXT_PREVIEW_LINES));
|
|
59
|
+
if (body.length > TEXT_PREVIEW_LINES) {
|
|
60
|
+
lines.push(`…(共 ${body.length} 行,省略 ${body.length - TEXT_PREVIEW_LINES} 行;用 -f json 取全文)`);
|
|
61
|
+
}
|
|
62
|
+
return lines.join("\n");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
module.exports = {
|
|
66
|
+
toMdCommand,
|
|
67
|
+
formatToMdText,
|
|
68
|
+
readFrontmatter,
|
|
69
|
+
};
|