wxpub-cli 0.0.0-stage → 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Tang Cheng
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,112 @@
1
- # Temporary Holding Version
1
+ # wxpub-cli
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ 公众号发文 CLI —— 一条命令把 Markdown 文章投进指定公众号的草稿箱。
4
+
5
+ [wechat-publish-service](https://yyps.net) 的官方命令行客户端,走 API-Key 通道,为 coding agent(Claude Code 等)与程序化发文设计。
6
+
7
+ ## 安装
8
+
9
+ ```bash
10
+ npm install -g wxpub-cli@0.1.1
11
+ ```
12
+
13
+ 装好后两个命令等价可用:`wxpub`(短,日常用)和 `wxpub-cli`(与包名一致)。文档以 `wxpub` 示例。
14
+
15
+ > 旧包 `wxpub@0.1.0` 已更名,请改用 `wxpub-cli`(命令 `wxpub` 保持不变)。
16
+
17
+ 要求 Node.js >= 20。
18
+
19
+ > ⚠️ 请从 npm registry 安装并锁定版本。不要引用仓库滚动分支/tarball(如 `github:…/wxpub-cli#main`)——那会让每次 `npm install` 隐性升级到未审阅代码;升级必须是显式改版本号的决策动作。
20
+
21
+ ## 快速开始
22
+
23
+ ```bash
24
+ # 1. 配置服务地址与 API Key(保存到 ~/.wxpub/config.json)
25
+ wxpub login --base-url https://yyps.net --api-key sk_live_xxx
26
+
27
+ # 2. 发文(markdown 直传,渲染与主题判定在服务端完成)
28
+ wxpub publish --account danxin --file article.md
29
+
30
+ # 3. 检查登录态与服务健康
31
+ wxpub me
32
+ ```
33
+
34
+ API Key 由服务端签发(管理后台或 `scripts/generate_api_key.py`)。
35
+
36
+ 也可以不落配置,用环境变量:`WXPUB_BASE_URL` / `WXPUB_API_KEY`。
37
+
38
+ ## 命令
39
+
40
+ ### `publish` — 投递文章到草稿箱
41
+
42
+ ```bash
43
+ wxpub publish -a <account> -f article.md [options]
44
+ ```
45
+
46
+ | 选项 | 说明 |
47
+ | ----------------------- | ---------------------------------------------------------------------------- |
48
+ | `-a, --account <key>` | 公众号账号 key(必填) |
49
+ | `-f, --file <path>` | Markdown 文件(必填) |
50
+ | `-t, --title <title>` | 标题;缺省取正文首个 `# 一级标题` |
51
+ | `--digest <text>` | 分享摘要(≤54 字) |
52
+ | `--author <name>` | 笔名;缺省用服务端账号配置 |
53
+ | `--cover <mode>` | 封面:`ai`=AI 出图(默认)/ `auto`=服务端兜底 / `provided`=固定封面 / `none` |
54
+ | `--cover-prompt <text>` | `cover=ai` 时的画面提示词;缺省由服务端 LLM 读全文提炼 |
55
+ | `--thumb <mediaId>` | `cover=provided` 时的封面素材 media_id |
56
+ | `--force` | 跳过幂等去重,强制创建新草稿 |
57
+
58
+ 行为契约:
59
+
60
+ - **Markdown 直传**(`content_format=markdown`),排版渲染在服务端,与后台直发一致
61
+ - **不携带 theme** —— 账号在服务端配置的默认主题自动生效(`wxpub theme` 可查询)
62
+ - 请求带 `timestamp + nonce` 防重放
63
+ - 同内容重复投递会被服务端幂等去重(返回 `duplicate: true`),不会新建草稿
64
+ - 只落草稿箱,**不群发**;群发请在公众号后台人工确认
65
+
66
+ ### `theme` — 查询账号默认主题
67
+
68
+ ```bash
69
+ wxpub theme -a danxin
70
+ ```
71
+
72
+ ### `materials` — 查询永久素材列表
73
+
74
+ ```bash
75
+ wxpub materials -a danxin [--type image] [--offset 0] [--count 10]
76
+ ```
77
+
78
+ ### `image` — 上传图片为正文插图
79
+
80
+ ```bash
81
+ wxpub image -a danxin -f chart.png
82
+ # 输出 media_id + mmbiz URL,直接以 ![](url) 内嵌 Markdown 正文
83
+ ```
84
+
85
+ ## 给 coding agent 用
86
+
87
+ 全局 `--json` 开关输出机器可读结果:
88
+
89
+ ```bash
90
+ wxpub --json publish -a danxin -f article.md
91
+ # {"ok":true,"media_id":"MzIxN...","duplicate":false}
92
+
93
+ wxpub --json theme -a danxin
94
+ # {"account":"danxin","theme_key":"green","theme_name":"品牌绿"}
95
+ ```
96
+
97
+ agent 典型工作流:`theme` 确认账号主题 → 写 markdown → `image` 传配图 → `publish` 投递 → 从 JSON 里取 `media_id` 复命。
98
+
99
+ ## 开发
100
+
101
+ ```bash
102
+ npm install
103
+ npm run lint # ESLint 9 (flat config)
104
+ npm run typecheck # tsc --noEmit
105
+ npm test # vitest(wire 契约测试)
106
+ npm run build # 产出 dist/
107
+ node dist/index.js --help
108
+ ```
109
+
110
+ ## 路线图
111
+
112
+ - v0.2(依赖服务端增量):`wxpub tone` 拉账号调性(写文前注入 agent 上下文);`wxpub illustrate` AI 生成文中插图
@@ -0,0 +1,10 @@
1
+ /** 读取的文章:Markdown 原文 + 从首个 H1 提取的候选标题。 */
2
+ export interface Article {
3
+ content: string;
4
+ /** 正文首个 `# 一级标题`;无 H1 时为 undefined */
5
+ h1Title?: string;
6
+ }
7
+ /** 读取 Markdown 文件并提取 H1 标题(--title 缺省时的标题来源)。 */
8
+ export declare function readArticle(filePath: string): Promise<Article>;
9
+ /** 标题定夺:显式 --title > 正文 H1;都缺时报错(草稿箱必须标题)。 */
10
+ export declare function resolveTitle(explicitTitle: string | undefined, article: Article): string;
@@ -0,0 +1,16 @@
1
+ import { readFile } from "node:fs/promises";
2
+ /** 读取 Markdown 文件并提取 H1 标题(--title 缺省时的标题来源)。 */
3
+ export async function readArticle(filePath) {
4
+ const content = await readFile(filePath, "utf8");
5
+ const match = content.match(/^#\s+(.+?)\s*$/m);
6
+ return { content, h1Title: match ? match[1].trim() : undefined };
7
+ }
8
+ /** 标题定夺:显式 --title > 正文 H1;都缺时报错(草稿箱必须标题)。 */
9
+ export function resolveTitle(explicitTitle, article) {
10
+ const title = (explicitTitle ?? article.h1Title ?? "").trim();
11
+ if (!title) {
12
+ throw new Error("缺少标题:用 --title 指定,或在正文开头写一行 `# 标题`");
13
+ }
14
+ return title;
15
+ }
16
+ //# sourceMappingURL=article.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"article.js","sourceRoot":"","sources":["../src/article.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAS5C,iDAAiD;AACjD,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,QAAgB;IAChD,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACjD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;IAC/C,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC;AACnE,CAAC;AAED,8CAA8C;AAC9C,MAAM,UAAU,YAAY,CAAC,aAAiC,EAAE,OAAgB;IAC9E,MAAM,KAAK,GAAG,CAAC,aAAa,IAAI,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC9D,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;IACxD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,111 @@
1
+ import type { WxpubConfig } from "./config.js";
2
+ /** publish 请求的用户输入(不含协议自动字段)。 */
3
+ export interface PublishInput {
4
+ account: string;
5
+ title: string;
6
+ /** Markdown 原文;渲染与主题判定在服务端完成 */
7
+ content: string;
8
+ digest?: string;
9
+ author?: string;
10
+ /** ai | auto | provided | none;不传则走服务端账号配置的默认封面策略 */
11
+ cover?: "ai" | "auto" | "provided" | "none";
12
+ /** cover=ai 时的画面提示词;不传则服务端 LLM 读全文提炼 */
13
+ coverImagePrompt?: string;
14
+ /** cover=provided 时的固定封面素材 id */
15
+ thumbMediaId?: string;
16
+ /** 跳过幂等去重,强制创建新草稿 */
17
+ forcePublish?: boolean;
18
+ }
19
+ /** /api/mp/publish 请求体(wire 契约:content_format=markdown、绝不携带 theme)。 */
20
+ export interface PublishPayload {
21
+ account: string;
22
+ title: string;
23
+ content: string;
24
+ content_format: "markdown";
25
+ timestamp: number;
26
+ nonce: string;
27
+ digest?: string;
28
+ author?: string;
29
+ cover?: string;
30
+ cover_image_prompt?: string;
31
+ thumb_media_id?: string;
32
+ force_publish?: boolean;
33
+ }
34
+ /** 组装 publish 请求体。timestamp/nonce 由调用方注入以便测试。 */
35
+ export declare function buildPublishPayload(input: PublishInput, now?: () => number, nonce?: () => string): PublishPayload;
36
+ export interface PublishResult {
37
+ ok: boolean;
38
+ /** 公众号草稿素材 id(成功时必有) */
39
+ mediaId?: string;
40
+ /** 同一篇重复投递被幂等去重短路 */
41
+ duplicate: boolean;
42
+ message: string;
43
+ raw: unknown;
44
+ }
45
+ export interface AccountTheme {
46
+ ok: boolean;
47
+ account: string;
48
+ themeKey?: string;
49
+ themeName?: string;
50
+ /** 主题配置 JSON(服务端渲染用;本地渲染调用方取色用) */
51
+ themeConfig?: unknown;
52
+ message: string;
53
+ }
54
+ export interface MaterialItem {
55
+ mediaId: string;
56
+ name?: string;
57
+ url?: string;
58
+ updateTime?: number;
59
+ }
60
+ export interface MaterialsResult {
61
+ ok: boolean;
62
+ message: string;
63
+ totalCount: number;
64
+ items: MaterialItem[];
65
+ }
66
+ export interface UploadResult {
67
+ ok: boolean;
68
+ message: string;
69
+ /** 永久素材 media_id */
70
+ mediaId?: string;
71
+ /** 微信 CDN URL(mmbiz…),可直接内嵌正文 */
72
+ url?: string;
73
+ }
74
+ /** 服务端错误(4xx/5xx 或业务失败),携带状态码与响应体。 */
75
+ export declare class WxpubApiError extends Error {
76
+ readonly status: number;
77
+ readonly body: unknown;
78
+ constructor(message: string, status: number, body: unknown);
79
+ }
80
+ type FetchLike = (input: string, init?: RequestInit) => Promise<Response>;
81
+ export interface WxpubClientOptions extends Partial<WxpubConfig> {
82
+ /** 依赖注入:测试捕获请求用 */
83
+ fetchImpl?: FetchLike;
84
+ timeoutMs?: number;
85
+ }
86
+ /** wechat-publish-service 客户端(API-Key 通道)。 */
87
+ export declare class WxpubClient {
88
+ private readonly baseUrl;
89
+ private readonly apiKey;
90
+ private readonly fetchImpl;
91
+ private readonly timeoutMs;
92
+ constructor(options: WxpubClientOptions);
93
+ /** sk_ 开头是 API Key(X-API-Key);三段点分是 JWT(Bearer 委托票)。 */
94
+ private authHeaders;
95
+ private request;
96
+ /** 服务健康检查(GET /health)。 */
97
+ health(): Promise<boolean>;
98
+ /** 投递文章到公众号草稿箱。成功判据与 exomind 对齐:有 media_id 即成功。 */
99
+ publish(input: PublishInput): Promise<PublishResult>;
100
+ /** 查询账号默认主题(不传 theme 发文时服务端即用它渲染)。 */
101
+ accountTheme(account: string): Promise<AccountTheme>;
102
+ /** 查询永久素材列表。 */
103
+ listMaterials(account: string, options?: {
104
+ type?: string;
105
+ offset?: number;
106
+ count?: number;
107
+ }): Promise<MaterialsResult>;
108
+ /** 上传本地图片为永久素材,返回 media_id + mmbiz URL(正文插图用)。 */
109
+ uploadImage(account: string, fileName: string, fileBytes: Uint8Array): Promise<UploadResult>;
110
+ }
111
+ export {};
package/dist/client.js ADDED
@@ -0,0 +1,198 @@
1
+ import { randomUUID } from "node:crypto";
2
+ /** 组装 publish 请求体。timestamp/nonce 由调用方注入以便测试。 */
3
+ export function buildPublishPayload(input, now = () => Math.floor(Date.now() / 1000), nonce = () => randomUUID().replaceAll("-", "")) {
4
+ const payload = {
5
+ account: input.account,
6
+ title: input.title,
7
+ content: input.content,
8
+ content_format: "markdown",
9
+ timestamp: now(),
10
+ nonce: nonce(),
11
+ };
12
+ if (input.digest !== undefined)
13
+ payload.digest = input.digest;
14
+ if (input.author !== undefined)
15
+ payload.author = input.author;
16
+ if (input.cover !== undefined)
17
+ payload.cover = input.cover;
18
+ if (input.coverImagePrompt !== undefined)
19
+ payload.cover_image_prompt = input.coverImagePrompt;
20
+ if (input.thumbMediaId !== undefined)
21
+ payload.thumb_media_id = input.thumbMediaId;
22
+ if (input.forcePublish)
23
+ payload.force_publish = true;
24
+ return payload;
25
+ }
26
+ /** 服务端错误(4xx/5xx 或业务失败),携带状态码与响应体。 */
27
+ export class WxpubApiError extends Error {
28
+ status;
29
+ body;
30
+ constructor(message, status, body) {
31
+ super(message);
32
+ this.name = "WxpubApiError";
33
+ this.status = status;
34
+ this.body = body;
35
+ }
36
+ }
37
+ const DEFAULT_TIMEOUT_MS = 60_000;
38
+ /** wechat-publish-service 客户端(API-Key 通道)。 */
39
+ export class WxpubClient {
40
+ baseUrl;
41
+ apiKey;
42
+ fetchImpl;
43
+ timeoutMs;
44
+ constructor(options) {
45
+ if (!options.baseUrl || !options.apiKey) {
46
+ throw new Error("WxpubClient 需要 baseUrl 与 apiKey");
47
+ }
48
+ this.baseUrl = options.baseUrl.replace(/\/+$/, "");
49
+ this.apiKey = options.apiKey;
50
+ this.fetchImpl = options.fetchImpl ?? ((input, init) => fetch(input, init));
51
+ this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
52
+ }
53
+ /** sk_ 开头是 API Key(X-API-Key);三段点分是 JWT(Bearer 委托票)。 */
54
+ authHeaders() {
55
+ const isJwt = !this.apiKey.startsWith("sk_") && this.apiKey.split(".").length === 3;
56
+ return isJwt ? { Authorization: `Bearer ${this.apiKey}` } : { "X-API-Key": this.apiKey };
57
+ }
58
+ async request(path, init) {
59
+ const controller = new AbortController();
60
+ const timer = setTimeout(() => controller.abort(), this.timeoutMs);
61
+ let resp;
62
+ try {
63
+ resp = await this.fetchImpl(`${this.baseUrl}${path}`, {
64
+ ...init,
65
+ signal: controller.signal,
66
+ });
67
+ }
68
+ catch (err) {
69
+ const reason = controller.signal.aborted
70
+ ? `超时(${this.timeoutMs}ms)`
71
+ : err.message;
72
+ throw new WxpubApiError(`请求 ${path} 失败:${reason}`, 0, null);
73
+ }
74
+ finally {
75
+ clearTimeout(timer);
76
+ }
77
+ const text = await resp.text();
78
+ let body = null;
79
+ try {
80
+ body = text ? JSON.parse(text) : null;
81
+ }
82
+ catch {
83
+ body = text;
84
+ }
85
+ if (!resp.ok) {
86
+ const message = body?.error ??
87
+ body?.message ??
88
+ `HTTP ${resp.status}`;
89
+ throw new WxpubApiError(`服务端返回 ${resp.status}:${message}`, resp.status, body);
90
+ }
91
+ return body;
92
+ }
93
+ /** 服务健康检查(GET /health)。 */
94
+ async health() {
95
+ const controller = new AbortController();
96
+ const timer = setTimeout(() => controller.abort(), 10_000);
97
+ try {
98
+ const resp = await this.fetchImpl(`${this.baseUrl}/health`, { signal: controller.signal });
99
+ return resp.ok;
100
+ }
101
+ catch {
102
+ return false;
103
+ }
104
+ finally {
105
+ clearTimeout(timer);
106
+ }
107
+ }
108
+ /** 投递文章到公众号草稿箱。成功判据与 exomind 对齐:有 media_id 即成功。 */
109
+ async publish(input) {
110
+ const body = (await this.request("/api/mp/publish", {
111
+ method: "POST",
112
+ headers: { ...this.authHeaders(), "Content-Type": "application/json" },
113
+ body: JSON.stringify(buildPublishPayload(input)),
114
+ }));
115
+ const mediaId = (typeof body.media_id === "string" && body.media_id) ||
116
+ (typeof body.draft_id === "string" && body.draft_id) ||
117
+ undefined;
118
+ const duplicate = body.duplicate === true;
119
+ const ok = mediaId !== undefined || body.success === true || body.errcode === 0;
120
+ const message = (typeof body.message === "string" && body.message) ||
121
+ (typeof body.error === "string" && body.error) ||
122
+ "";
123
+ if (!ok) {
124
+ throw new WxpubApiError(message || "投递失败(服务端未返回 media_id)", 0, body);
125
+ }
126
+ return { ok: true, mediaId, duplicate, message, raw: body };
127
+ }
128
+ /** 查询账号默认主题(不传 theme 发文时服务端即用它渲染)。 */
129
+ async accountTheme(account) {
130
+ const body = (await this.request(`/api/mp/account-theme?account=${encodeURIComponent(account)}`, { method: "GET", headers: this.authHeaders() }));
131
+ if (body.success !== true) {
132
+ throw new WxpubApiError(body.message || "查询账号主题失败", 0, body);
133
+ }
134
+ return {
135
+ ok: true,
136
+ account: body.account ?? account,
137
+ themeKey: body.theme_key || undefined,
138
+ themeName: body.theme_name || undefined,
139
+ themeConfig: body.theme_config,
140
+ message: body.message || "",
141
+ };
142
+ }
143
+ /** 查询永久素材列表。 */
144
+ async listMaterials(account, options = {}) {
145
+ const body = (await this.request("/api/mp/materials/list", {
146
+ method: "POST",
147
+ headers: { ...this.authHeaders(), "Content-Type": "application/json" },
148
+ body: JSON.stringify({
149
+ account,
150
+ material_type: options.type ?? "image",
151
+ offset: options.offset ?? 0,
152
+ count: options.count ?? 10,
153
+ timestamp: Math.floor(Date.now() / 1000),
154
+ }),
155
+ }));
156
+ if (body.success !== true) {
157
+ throw new WxpubApiError(body.message || "查询素材失败", 0, body);
158
+ }
159
+ const items = Array.isArray(body.items)
160
+ ? body.items.map((item) => ({
161
+ mediaId: item.media_id || "",
162
+ name: item.name || undefined,
163
+ url: item.url || undefined,
164
+ updateTime: typeof item.update_time === "number" ? item.update_time : undefined,
165
+ }))
166
+ : [];
167
+ return {
168
+ ok: true,
169
+ message: body.message || "",
170
+ totalCount: body.total_count ?? items.length,
171
+ items,
172
+ };
173
+ }
174
+ /** 上传本地图片为永久素材,返回 media_id + mmbiz URL(正文插图用)。 */
175
+ async uploadImage(account, fileName, fileBytes) {
176
+ const form = new FormData();
177
+ form.append("account", account);
178
+ form.append("nonce", randomUUID().replaceAll("-", ""));
179
+ form.append("file", new Blob([fileBytes]), fileName);
180
+ const body = (await this.request("/api/mp/materials/upload", {
181
+ method: "POST",
182
+ headers: this.authHeaders(),
183
+ body: form,
184
+ }));
185
+ const mediaId = typeof body.media_id === "string" ? body.media_id : undefined;
186
+ const url = typeof body.url === "string" ? body.url : undefined;
187
+ if (body.success !== true || !mediaId) {
188
+ throw new WxpubApiError(body.message || "上传素材失败", 0, body);
189
+ }
190
+ return {
191
+ ok: true,
192
+ message: body.message || "",
193
+ mediaId,
194
+ url,
195
+ };
196
+ }
197
+ }
198
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export interface ImageOptions {
2
+ account: string;
3
+ file: string;
4
+ json: boolean;
5
+ }
6
+ /** wxpub image:本地图片上传为微信永久素材,输出可内嵌正文的 mmbiz URL。 */
7
+ export declare function runImage(options: ImageOptions): Promise<void>;
@@ -0,0 +1,22 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { WxpubClient } from "../client.js";
4
+ import { resolveConfig } from "../config.js";
5
+ /** wxpub image:本地图片上传为微信永久素材,输出可内嵌正文的 mmbiz URL。 */
6
+ export async function runImage(options) {
7
+ const config = resolveConfig();
8
+ const bytes = await readFile(options.file);
9
+ const fileName = path.basename(options.file);
10
+ const client = new WxpubClient(config);
11
+ const result = await client.uploadImage(options.account, fileName, bytes);
12
+ if (options.json) {
13
+ console.log(JSON.stringify({ media_id: result.mediaId, url: result.url ?? null }));
14
+ return;
15
+ }
16
+ console.log(`✓ 已上传永久素材`);
17
+ console.log(` media_id:${result.mediaId}`);
18
+ if (result.url) {
19
+ console.log(` 正文插图:![](${result.url})`);
20
+ }
21
+ }
22
+ //# sourceMappingURL=image.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image.js","sourceRoot":"","sources":["../../src/commands/image.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAQ7C,oDAAoD;AACpD,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,OAAqB;IAClD,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;IAC/B,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,CAAC;IACvC,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;IAC1E,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACjB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,OAAO,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC;QACnF,OAAO;IACT,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IACzB,OAAO,CAAC,GAAG,CAAC,cAAc,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;IAC5C,IAAI,MAAM,CAAC,GAAG,EAAE,CAAC;QACf,OAAO,CAAC,GAAG,CAAC,cAAc,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC;IAC3C,CAAC;AACH,CAAC"}
@@ -0,0 +1,6 @@
1
+ export interface LoginOptions {
2
+ baseUrl: string;
3
+ apiKey: string;
4
+ }
5
+ /** wxpub login:保存服务地址与 API Key 到 ~/.wxpub/config.json。 */
6
+ export declare function runLogin(options: LoginOptions): void;
@@ -0,0 +1,10 @@
1
+ import { configPath, normalizeBaseUrl, saveConfig } from "../config.js";
2
+ /** wxpub login:保存服务地址与 API Key 到 ~/.wxpub/config.json。 */
3
+ export function runLogin(options) {
4
+ const baseUrl = normalizeBaseUrl(options.baseUrl);
5
+ saveConfig({ baseUrl, apiKey: options.apiKey.trim() });
6
+ console.log(`已保存到 ${configPath()}`);
7
+ console.log(`服务地址:${baseUrl}`);
8
+ console.log("现在可以发文了:wxpub publish --account <账号> --file article.md");
9
+ }
10
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAOxE,0DAA0D;AAC1D,MAAM,UAAU,QAAQ,CAAC,OAAqB;IAC5C,MAAM,OAAO,GAAG,gBAAgB,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAClD,UAAU,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACvD,OAAO,CAAC,GAAG,CAAC,QAAQ,UAAU,EAAE,EAAE,CAAC,CAAC;IACpC,OAAO,CAAC,GAAG,CAAC,QAAQ,OAAO,EAAE,CAAC,CAAC;IAC/B,OAAO,CAAC,GAAG,CAAC,wDAAwD,CAAC,CAAC;AACxE,CAAC"}
@@ -0,0 +1,9 @@
1
+ export interface MaterialsOptions {
2
+ account: string;
3
+ type?: string;
4
+ offset?: number;
5
+ count?: number;
6
+ json: boolean;
7
+ }
8
+ /** wxpub materials:查询账号永久素材列表。 */
9
+ export declare function runMaterials(options: MaterialsOptions): Promise<void>;
@@ -0,0 +1,32 @@
1
+ import { WxpubClient } from "../client.js";
2
+ import { resolveConfig } from "../config.js";
3
+ /** wxpub materials:查询账号永久素材列表。 */
4
+ export async function runMaterials(options) {
5
+ const config = resolveConfig();
6
+ const client = new WxpubClient(config);
7
+ const result = await client.listMaterials(options.account, {
8
+ type: options.type,
9
+ offset: options.offset,
10
+ count: options.count,
11
+ });
12
+ if (options.json) {
13
+ console.log(JSON.stringify({
14
+ total_count: result.totalCount,
15
+ items: result.items.map((item) => ({
16
+ media_id: item.mediaId,
17
+ name: item.name ?? null,
18
+ url: item.url ?? null,
19
+ })),
20
+ }));
21
+ return;
22
+ }
23
+ console.log(`共 ${result.totalCount} 条素材(类型 ${options.type ?? "image"}):`);
24
+ if (result.items.length === 0) {
25
+ console.log("(空)");
26
+ return;
27
+ }
28
+ for (const item of result.items) {
29
+ console.log(` ${item.mediaId} ${item.name ?? ""} ${item.url ?? ""}`);
30
+ }
31
+ }
32
+ //# sourceMappingURL=materials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"materials.js","sourceRoot":"","sources":["../../src/commands/materials.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAU7C,kCAAkC;AAClC,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAAyB;IAC1D,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;IAC/B,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,CAAC;IACvC,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,OAAO,EAAE;QACzD,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,KAAK,EAAE,OAAO,CAAC,KAAK;KACrB,CAAC,CAAC;IACH,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACjB,OAAO,CAAC,GAAG,CACT,IAAI,CAAC,SAAS,CAAC;YACb,WAAW,EAAE,MAAM,CAAC,UAAU;YAC9B,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;gBACjC,QAAQ,EAAE,IAAI,CAAC,OAAO;gBACtB,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,IAAI;gBACvB,GAAG,EAAE,IAAI,CAAC,GAAG,IAAI,IAAI;aACtB,CAAC,CAAC;SACJ,CAAC,CACH,CAAC;QACF,OAAO;IACT,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,KAAK,MAAM,CAAC,UAAU,WAAW,OAAO,CAAC,IAAI,IAAI,OAAO,IAAI,CAAC,CAAC;IAC1E,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACnB,OAAO;IACT,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QAChC,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC,OAAO,KAAK,IAAI,CAAC,IAAI,IAAI,EAAE,KAAK,IAAI,CAAC,GAAG,IAAI,EAAE,EAAE,CAAC,CAAC;IAC1E,CAAC;AACH,CAAC"}
@@ -0,0 +1,5 @@
1
+ export interface MeOptions {
2
+ json: boolean;
3
+ }
4
+ /** wxpub me:登录态展示 + 服务健康检查。 */
5
+ export declare function runMe(options: MeOptions): Promise<void>;
@@ -0,0 +1,27 @@
1
+ import { WxpubClient } from "../client.js";
2
+ import { loadConfig } from "../config.js";
3
+ /** wxpub me:登录态展示 + 服务健康检查。 */
4
+ export async function runMe(options) {
5
+ const config = loadConfig();
6
+ if (!config) {
7
+ if (options.json) {
8
+ console.log(JSON.stringify({ configured: false, healthy: false }));
9
+ }
10
+ else {
11
+ console.log("未配置。先执行:wxpub login --base-url <url> --api-key <key>");
12
+ }
13
+ return;
14
+ }
15
+ const client = new WxpubClient(config);
16
+ const healthy = await client.health();
17
+ const masked = `${config.apiKey.slice(0, 6)}…${config.apiKey.slice(-4)}`;
18
+ if (options.json) {
19
+ console.log(JSON.stringify({ configured: true, baseUrl: config.baseUrl, apiKey: masked, healthy }));
20
+ }
21
+ else {
22
+ console.log(`服务地址:${config.baseUrl}`);
23
+ console.log(`API Key:${masked}`);
24
+ console.log(`服务健康:${healthy ? "正常" : "不可达"}`);
25
+ }
26
+ }
27
+ //# sourceMappingURL=me.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"me.js","sourceRoot":"","sources":["../../src/commands/me.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAM1C,+BAA+B;AAC/B,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,OAAkB;IAC5C,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;YACjB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;QACrE,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,sDAAsD,CAAC,CAAC;QACtE,CAAC;QACD,OAAO;IACT,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,CAAC;IACvC,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,MAAM,EAAE,CAAC;IACtC,MAAM,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACzE,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACjB,OAAO,CAAC,GAAG,CACT,IAAI,CAAC,SAAS,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,CACvF,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,QAAQ,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;QACtC,OAAO,CAAC,GAAG,CAAC,WAAW,MAAM,EAAE,CAAC,CAAC;QACjC,OAAO,CAAC,GAAG,CAAC,QAAQ,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;IAChD,CAAC;AACH,CAAC"}
@@ -0,0 +1,14 @@
1
+ export interface PublishOptions {
2
+ account: string;
3
+ file: string;
4
+ title?: string;
5
+ digest?: string;
6
+ author?: string;
7
+ cover?: "ai" | "auto" | "provided" | "none";
8
+ coverPrompt?: string;
9
+ thumbMediaId?: string;
10
+ force?: boolean;
11
+ json: boolean;
12
+ }
13
+ /** wxpub publish:markdown 直投公众号草稿箱。 */
14
+ export declare function runPublish(options: PublishOptions): Promise<void>;
@@ -0,0 +1,36 @@
1
+ import { WxpubClient } from "../client.js";
2
+ import { readArticle, resolveTitle } from "../article.js";
3
+ import { resolveConfig } from "../config.js";
4
+ /** wxpub publish:markdown 直投公众号草稿箱。 */
5
+ export async function runPublish(options) {
6
+ const config = resolveConfig();
7
+ const article = await readArticle(options.file);
8
+ const title = resolveTitle(options.title, article);
9
+ const client = new WxpubClient(config);
10
+ const result = await client.publish({
11
+ account: options.account,
12
+ title,
13
+ content: article.content,
14
+ digest: options.digest,
15
+ author: options.author,
16
+ cover: options.cover,
17
+ coverImagePrompt: options.coverPrompt,
18
+ thumbMediaId: options.thumbMediaId,
19
+ forcePublish: options.force,
20
+ });
21
+ if (options.json) {
22
+ console.log(JSON.stringify({ ok: result.ok, media_id: result.mediaId, duplicate: result.duplicate }));
23
+ return;
24
+ }
25
+ const dedupNote = result.duplicate ? "(内容未变,幂等去重,未新建草稿)" : "";
26
+ console.log(`✓ 已投递到草稿箱${dedupNote}`);
27
+ console.log(` 标题:${title}`);
28
+ console.log(` 账号:${options.account}`);
29
+ if (result.mediaId) {
30
+ console.log(` media_id:${result.mediaId}`);
31
+ }
32
+ if (options.cover === "ai" || (!options.cover && !result.duplicate)) {
33
+ console.log(" 封面:AI 出图为后台异步任务,稍后可在公众号后台确认");
34
+ }
35
+ }
36
+ //# sourceMappingURL=publish.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publish.js","sourceRoot":"","sources":["../../src/commands/publish.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAe7C,uCAAuC;AACvC,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,OAAuB;IACtD,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;IAC/B,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,MAAM,KAAK,GAAG,YAAY,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACnD,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,CAAC;IACvC,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC;QAClC,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,KAAK;QACL,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,gBAAgB,EAAE,OAAO,CAAC,WAAW;QACrC,YAAY,EAAE,OAAO,CAAC,YAAY;QAClC,YAAY,EAAE,OAAO,CAAC,KAAK;KAC5B,CAAC,CAAC;IACH,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACjB,OAAO,CAAC,GAAG,CACT,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,CAAC,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC,CACzF,CAAC;QACF,OAAO;IACT,CAAC;IACD,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9D,OAAO,CAAC,GAAG,CAAC,YAAY,SAAS,EAAE,CAAC,CAAC;IACrC,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,EAAE,CAAC,CAAC;IAC7B,OAAO,CAAC,GAAG,CAAC,QAAQ,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IACvC,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,OAAO,CAAC,GAAG,CAAC,cAAc,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;IAC9C,CAAC;IACD,IAAI,OAAO,CAAC,KAAK,KAAK,IAAI,IAAI,CAAC,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;QACpE,OAAO,CAAC,GAAG,CAAC,+BAA+B,CAAC,CAAC;IAC/C,CAAC;AACH,CAAC"}
@@ -0,0 +1,6 @@
1
+ export interface ThemeOptions {
2
+ account: string;
3
+ json: boolean;
4
+ }
5
+ /** wxpub theme:查询账号默认主题(不传 theme 发文时服务端即用它渲染)。 */
6
+ export declare function runTheme(options: ThemeOptions): Promise<void>;
@@ -0,0 +1,23 @@
1
+ import { WxpubClient } from "../client.js";
2
+ import { resolveConfig } from "../config.js";
3
+ /** wxpub theme:查询账号默认主题(不传 theme 发文时服务端即用它渲染)。 */
4
+ export async function runTheme(options) {
5
+ const config = resolveConfig();
6
+ const client = new WxpubClient(config);
7
+ const theme = await client.accountTheme(options.account);
8
+ if (options.json) {
9
+ console.log(JSON.stringify({
10
+ account: theme.account,
11
+ theme_key: theme.themeKey ?? null,
12
+ theme_name: theme.themeName ?? null,
13
+ }));
14
+ return;
15
+ }
16
+ if (theme.themeKey) {
17
+ console.log(`账号 ${theme.account} 的默认主题:${theme.themeName ?? theme.themeKey}(${theme.themeKey})`);
18
+ }
19
+ else {
20
+ console.log(`账号 ${theme.account} 未配置默认主题,发文时将使用服务端内置 default 主题`);
21
+ }
22
+ }
23
+ //# sourceMappingURL=theme.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme.js","sourceRoot":"","sources":["../../src/commands/theme.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAO7C,kDAAkD;AAClD,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,OAAqB;IAClD,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;IAC/B,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,CAAC;IACvC,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACzD,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;QACjB,OAAO,CAAC,GAAG,CACT,IAAI,CAAC,SAAS,CAAC;YACb,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,SAAS,EAAE,KAAK,CAAC,QAAQ,IAAI,IAAI;YACjC,UAAU,EAAE,KAAK,CAAC,SAAS,IAAI,IAAI;SACpC,CAAC,CACH,CAAC;QACF,OAAO;IACT,CAAC;IACD,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;QACnB,OAAO,CAAC,GAAG,CACT,MAAM,KAAK,CAAC,OAAO,UAAU,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,QAAQ,GAAG,CACpF,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,MAAM,KAAK,CAAC,OAAO,iCAAiC,CAAC,CAAC;IACpE,CAAC;AACH,CAAC"}
@@ -0,0 +1,22 @@
1
+ /** CLI 凭据配置:yyps 服务地址 + API Key。 */
2
+ export interface WxpubConfig {
3
+ /** 服务基地址,如 https://yyps.net(不带尾斜杠) */
4
+ baseUrl: string;
5
+ /** API Key(sk_…)或 JWT(三段式,走 Bearer 通道) */
6
+ apiKey: string;
7
+ }
8
+ /** 环境变量优先于配置文件:WXPUB_BASE_URL / WXPUB_API_KEY。 */
9
+ export interface ConfigOverrides {
10
+ baseUrl?: string;
11
+ apiKey?: string;
12
+ }
13
+ /** 校验并规范化 baseUrl(去尾斜杠;缺协议补 https://)。 */
14
+ export declare function normalizeBaseUrl(raw: string): string;
15
+ /** 持久化配置到 ~/.wxpub/config.json(0600 权限,含密钥)。 */
16
+ export declare function saveConfig(config: WxpubConfig): void;
17
+ /** 读取已保存的配置;未配置返回 null。 */
18
+ export declare function loadConfig(): WxpubConfig | null;
19
+ /** 配置解析优先级:显式传入 > 环境变量 > 配置文件。未配置时抛出带指引的错误。 */
20
+ export declare function resolveConfig(overrides?: ConfigOverrides): WxpubConfig;
21
+ /** 配置文件路径(login 成功提示用)。 */
22
+ export declare function configPath(): string;
package/dist/config.js ADDED
@@ -0,0 +1,50 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import path from "node:path";
4
+ const CONFIG_DIR = path.join(homedir(), ".wxpub");
5
+ const CONFIG_PATH = path.join(CONFIG_DIR, "config.json");
6
+ /** 校验并规范化 baseUrl(去尾斜杠;缺协议补 https://)。 */
7
+ export function normalizeBaseUrl(raw) {
8
+ const trimmed = raw.trim().replace(/\/+$/, "");
9
+ if (!trimmed) {
10
+ throw new Error("baseUrl 不能为空");
11
+ }
12
+ return /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(trimmed) ? trimmed : `https://${trimmed}`;
13
+ }
14
+ /** 持久化配置到 ~/.wxpub/config.json(0600 权限,含密钥)。 */
15
+ export function saveConfig(config) {
16
+ mkdirSync(CONFIG_DIR, { recursive: true });
17
+ writeFileSync(CONFIG_PATH, `${JSON.stringify(config, null, 2)}\n`, { mode: 0o600 });
18
+ }
19
+ /** 读取已保存的配置;未配置返回 null。 */
20
+ export function loadConfig() {
21
+ if (!existsSync(CONFIG_PATH)) {
22
+ return null;
23
+ }
24
+ try {
25
+ const raw = JSON.parse(readFileSync(CONFIG_PATH, "utf8"));
26
+ if (typeof raw.baseUrl !== "string" || typeof raw.apiKey !== "string") {
27
+ return null;
28
+ }
29
+ return { baseUrl: raw.baseUrl, apiKey: raw.apiKey };
30
+ }
31
+ catch {
32
+ return null;
33
+ }
34
+ }
35
+ /** 配置解析优先级:显式传入 > 环境变量 > 配置文件。未配置时抛出带指引的错误。 */
36
+ export function resolveConfig(overrides = {}) {
37
+ const file = loadConfig();
38
+ const baseUrl = overrides.baseUrl ?? process.env.WXPUB_BASE_URL ?? file?.baseUrl;
39
+ const apiKey = overrides.apiKey ?? process.env.WXPUB_API_KEY ?? file?.apiKey;
40
+ if (!baseUrl || !apiKey) {
41
+ throw new Error("未配置服务地址或 API Key。先执行 wxpub login --base-url <url> --api-key <key>," +
42
+ "或设置环境变量 WXPUB_BASE_URL / WXPUB_API_KEY。");
43
+ }
44
+ return { baseUrl: normalizeBaseUrl(baseUrl), apiKey };
45
+ }
46
+ /** 配置文件路径(login 成功提示用)。 */
47
+ export function configPath() {
48
+ return CONFIG_PATH;
49
+ }
50
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAU7B,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAC;AAClD,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;AAQzD,0CAA0C;AAC1C,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC/C,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,cAAc,CAAC,CAAC;IAClC,CAAC;IACD,OAAO,+BAA+B,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,OAAO,EAAE,CAAC;AACxF,CAAC;AAED,gDAAgD;AAChD,MAAM,UAAU,UAAU,CAAC,MAAmB;IAC5C,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3C,aAAa,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AACtF,CAAC;AAED,2BAA2B;AAC3B,MAAM,UAAU,UAAU;IACxB,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAC7B,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,WAAW,EAAE,MAAM,CAAC,CAAyB,CAAC;QAClF,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,IAAI,OAAO,GAAG,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;YACtE,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC;IACtD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,+CAA+C;AAC/C,MAAM,UAAU,aAAa,CAAC,YAA6B,EAAE;IAC3D,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;IAC1B,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,IAAI,EAAE,OAAO,CAAC;IACjF,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,IAAI,EAAE,MAAM,CAAC;IAC7E,IAAI,CAAC,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CACb,oEAAoE;YAClE,yCAAyC,CAC5C,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,gBAAgB,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;AACxD,CAAC;AAED,2BAA2B;AAC3B,MAAM,UAAU,UAAU;IACxB,OAAO,WAAW,CAAC;AACrB,CAAC"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from "commander";
3
+ import { runImage } from "./commands/image.js";
4
+ import { runLogin } from "./commands/login.js";
5
+ import { runMaterials } from "./commands/materials.js";
6
+ import { runMe } from "./commands/me.js";
7
+ import { runPublish } from "./commands/publish.js";
8
+ import { runTheme } from "./commands/theme.js";
9
+ import { WxpubApiError } from "./client.js";
10
+ const program = new Command();
11
+ program
12
+ .name("wxpub")
13
+ .description("公众号发文 CLI — markdown 直投草稿箱(wechat-publish-service 客户端)")
14
+ .version("0.1.0")
15
+ .option("--json", "输出 JSON(供 coding agent / 程序消费)", false);
16
+ program
17
+ .command("login")
18
+ .description("配置服务地址与 API Key(保存到 ~/.wxpub/config.json)")
19
+ .requiredOption("--base-url <url>", "服务地址,如 https://yyps.net")
20
+ .requiredOption("--api-key <key>", "API Key(sk_…)")
21
+ .action((options) => {
22
+ runLogin(options);
23
+ });
24
+ program
25
+ .command("me")
26
+ .description("查看登录态与服务健康")
27
+ .action(async () => {
28
+ await runMe({ json: program.opts().json });
29
+ });
30
+ program
31
+ .command("publish")
32
+ .description("投递 markdown 文章到公众号草稿箱")
33
+ .requiredOption("-a, --account <key>", "公众号账号 key(如 danxin)")
34
+ .requiredOption("-f, --file <path>", "markdown 文件路径")
35
+ .option("-t, --title <title>", "标题;缺省取正文首个 # 一级标题")
36
+ .option("--digest <text>", "分享摘要(≤54 字)")
37
+ .option("--author <name>", "笔名;缺省用服务端账号配置")
38
+ .option("--cover <mode>", "封面策略:ai=AI出图(默认) / auto=服务端兜底 / provided=用固定封面 / none", undefined)
39
+ .option("--cover-prompt <text>", "cover=ai 时的画面提示词;缺省由服务端 LLM 提炼")
40
+ .option("--thumb <mediaId>", "cover=provided 时的封面素材 media_id")
41
+ .option("--force", "跳过幂等去重,强制创建新草稿", false)
42
+ .action(async (options) => {
43
+ await runPublish({ ...options, json: program.opts().json });
44
+ });
45
+ program
46
+ .command("theme")
47
+ .description("查询账号默认主题")
48
+ .requiredOption("-a, --account <key>", "公众号账号 key")
49
+ .action(async (options) => {
50
+ await runTheme({ ...options, json: program.opts().json });
51
+ });
52
+ program
53
+ .command("materials")
54
+ .description("查询账号永久素材列表")
55
+ .requiredOption("-a, --account <key>", "公众号账号 key")
56
+ .option("--type <type>", "素材类型(默认 image)")
57
+ .option("--offset <n>", "偏移量", (value) => Number.parseInt(value, 10))
58
+ .option("--count <n>", "每页条数(1-20)", (value) => Number.parseInt(value, 10))
59
+ .action(async (options) => {
60
+ await runMaterials({ ...options, json: program.opts().json });
61
+ });
62
+ program
63
+ .command("image")
64
+ .description("上传本地图片为微信永久素材,输出可内嵌正文的 mmbiz URL")
65
+ .requiredOption("-a, --account <key>", "公众号账号 key")
66
+ .requiredOption("-f, --file <path>", "图片文件路径")
67
+ .action(async (options) => {
68
+ await runImage({ ...options, json: program.opts().json });
69
+ });
70
+ program.parseAsync().catch((err) => {
71
+ if (err instanceof WxpubApiError) {
72
+ console.error(`✗ ${err.message}`);
73
+ }
74
+ else if (err instanceof Error) {
75
+ console.error(`✗ ${err.message}`);
76
+ }
77
+ else {
78
+ console.error("✗ 未知错误", err);
79
+ }
80
+ process.exitCode = 1;
81
+ });
82
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,62 @@
1
1
  {
2
2
  "name": "wxpub-cli",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.1",
4
+ "description": "WeChat Official Account publishing CLI — markdown 直投公众号草稿箱(wechat-publish-service 客户端,API-Key 通道)",
5
+ "type": "module",
6
+ "bin": {
7
+ "wxpub": "dist/index.js",
8
+ "wxpub-cli": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "README.md",
13
+ "LICENSE"
14
+ ],
15
+ "engines": {
16
+ "node": ">=20"
17
+ },
18
+ "scripts": {
19
+ "build": "tsc -p tsconfig.json",
20
+ "lint": "eslint .",
21
+ "lint:fix": "eslint . --fix",
22
+ "format": "prettier --write .",
23
+ "format:check": "prettier --check .",
24
+ "typecheck": "tsc --noEmit",
25
+ "test": "vitest run",
26
+ "prepublishOnly": "npm run lint && npm run typecheck && npm test && npm run build"
27
+ },
28
+ "keywords": [
29
+ "wechat",
30
+ "official-account",
31
+ "publish",
32
+ "markdown",
33
+ "cli"
34
+ ],
35
+ "license": "MIT",
36
+ "publishConfig": {
37
+ "access": "public"
38
+ },
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "git+https://github.com/helloworldtang/wxpub-cli.git"
42
+ },
43
+ "bugs": {
44
+ "url": "https://github.com/helloworldtang/wxpub-cli/issues"
45
+ },
46
+ "homepage": "https://github.com/helloworldtang/wxpub-cli#readme",
47
+ "dependencies": {
48
+ "commander": "^14.0.0"
49
+ },
50
+ "devDependencies": {
51
+ "@eslint/js": "^9.0.0",
52
+ "@types/node": "^24.19.2",
53
+ "eslint": "^9.0.0",
54
+ "eslint-config-prettier": "^10.0.0",
55
+ "globals": "^16.0.0",
56
+ "prettier": "^3.0.0",
57
+ "tsx": "^4.0.0",
58
+ "typescript": "^5.7.0",
59
+ "typescript-eslint": "^8.0.0",
60
+ "vitest": "^3.0.0"
61
+ }
62
+ }