yuque-cookie-plugin 0.1.1 → 0.1.3

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 CHANGED
@@ -8,8 +8,8 @@
8
8
 
9
9
  ## 当前能力
10
10
 
11
- - Cookie 登录和登录态检测:`auth login`、`auth status`
12
- - 读取语雀信息:`book inspect`、`doc inspect`
11
+ - Cookie 登录、清理和登录态检测:`auth login`、`auth logout`、`auth status`
12
+ - 读取语雀信息:`book list`、`book inspect`、`doc inspect`
13
13
  - 文档快照和结构对比:`doc snapshot`、`lake diff`
14
14
  - Lake 和 Markdown 辅助转换:`lake to-markdown`、`lake serialize`
15
15
  - 安全写入:`doc apply-lake`、`doc update-lake`
@@ -78,6 +78,15 @@ npm run yuque-local -- auth login
78
78
 
79
79
  项目目录不会保存真实 Cookie。配置文件会记录保存时间、更新时间、最近成功验证时间和最近失败时间。
80
80
 
81
+ 如果你在同一台电脑上同时做全局 npm 版真实测试和本地源码开发,可以用独立配置目录隔离 Cookie:
82
+
83
+ ```bash
84
+ YUQUE_COOKIE_PLUGIN_CONFIG_DIR=/tmp/yuque-plugin-real-test yuque-local auth login
85
+ YUQUE_COOKIE_PLUGIN_CONFIG_DIR=/tmp/yuque-plugin-real-test yuque-local auth status --json
86
+ ```
87
+
88
+ 这会把配置保存到 `/tmp/yuque-plugin-real-test/config.json`,不会影响默认的 `~/.config/yuque-cookie-plugin/config.json`。
89
+
81
90
  也可以用环境变量覆盖本地配置:
82
91
 
83
92
  ```bash
@@ -98,6 +107,12 @@ npm run yuque-local -- auth status https://www.yuque.com/<your-login>/<your-book
98
107
  yuque-local auth login --manual
99
108
  ```
100
109
 
110
+ 清理本机保存的 Cookie:
111
+
112
+ ```bash
113
+ yuque-local auth logout
114
+ ```
115
+
101
116
  也可以一次性传入参数,适合本地受控脚本:
102
117
 
103
118
  ```bash
@@ -115,6 +130,12 @@ yuque-local auth login --manual \
115
130
  npm run yuque-local -- book inspect https://www.yuque.com/<your-login>/<your-book>
116
131
  ```
117
132
 
133
+ 列出当前账号可访问的知识库:
134
+
135
+ ```bash
136
+ yuque-local book list --json
137
+ ```
138
+
118
139
  下载整个知识库:
119
140
 
120
141
  ```bash
@@ -234,6 +255,7 @@ npm run real:acceptance -- \
234
255
  ## 文档
235
256
 
236
257
  - `docs/usage-zh.md`:中文完整使用指南
258
+ - `docs/ai-dialogue-test-cases.md`:用户和 AI 对话测试用例
237
259
  - `docs/real-acceptance.md`:真实语雀验收流程
238
260
  - `docs/native-lake-capability.md`:原生 Lake 能力矩阵
239
261
  - `docs/development-plan.md`:开发计划和迭代日志
@@ -246,6 +268,7 @@ npm run real:acceptance -- \
246
268
 
247
269
  - 不要把真实 `_yuque_session` 或 `yuque_ctoken` 写入项目文件。
248
270
  - 不要提交 `~/.config/yuque-cookie-plugin/config.json`。
271
+ - 需要清理本机凭据时运行 `yuque-local auth logout`。
249
272
  - `reports/`、`backups/`、`node_modules/` 已被 `.gitignore` 忽略。
250
273
  - 批量写入前先对单篇文档做真实实验。
251
274
  - 不确定 Lake 语义时,先 snapshot,再 diff,再实现。
package/dist/auth.js CHANGED
@@ -1,15 +1,27 @@
1
1
  import { createServer } from 'node:http';
2
2
  import { existsSync, readFileSync } from 'node:fs';
3
- import { chmod, mkdir, writeFile } from 'node:fs/promises';
3
+ import { chmod, mkdir, rm, writeFile } from 'node:fs/promises';
4
4
  import { execFile } from 'node:child_process';
5
5
  import { createInterface } from 'node:readline/promises';
6
6
  import os from 'node:os';
7
7
  import path from 'node:path';
8
8
  import process from 'node:process';
9
- const CONFIG_DIR = path.join(os.homedir(), '.config', 'yuque-cookie-plugin');
10
- const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
9
+ export const CONFIG_DIR_ENV = 'YUQUE_COOKIE_PLUGIN_CONFIG_DIR';
10
+ export function getConfigDirPath() {
11
+ const override = process.env[CONFIG_DIR_ENV]?.trim();
12
+ return override ? path.resolve(override) : path.join(os.homedir(), '.config', 'yuque-cookie-plugin');
13
+ }
11
14
  export function getConfigFilePath() {
12
- return CONFIG_FILE;
15
+ return path.join(getConfigDirPath(), 'config.json');
16
+ }
17
+ export async function clearSavedCredentials() {
18
+ const configFile = getConfigFilePath();
19
+ const existed = existsSync(configFile);
20
+ await rm(configFile, { force: true });
21
+ return {
22
+ config_file: configFile,
23
+ removed: existed
24
+ };
13
25
  }
14
26
  export function getCredentials(flags = {}) {
15
27
  const envSession = process.env[String(flags.sessionEnv || 'YUQUE_SESSION')];
@@ -26,9 +38,9 @@ export function getCredentials(flags = {}) {
26
38
  }
27
39
  const saved = readSavedCredentials();
28
40
  if (saved?.session && saved?.ctoken) {
29
- return { ...saved, extraCookies: { ...(saved.extraCookies || {}), ...extraCookies }, source: CONFIG_FILE };
41
+ return { ...saved, extraCookies: { ...(saved.extraCookies || {}), ...extraCookies }, source: getConfigFilePath() };
30
42
  }
31
- throw new Error('Missing Yuque credentials. Run "yuque-local login" to configure them in your browser.');
43
+ throw new Error('Missing Yuque credentials. Run "yuque-local auth login" to configure them in your browser.');
32
44
  }
33
45
  export function getCredentialStatus(flags = {}) {
34
46
  const envSession = process.env[String(flags.sessionEnv || 'YUQUE_SESSION')];
@@ -48,12 +60,12 @@ export function getCredentialStatus(flags = {}) {
48
60
  if (!saved?.session || !saved?.ctoken) {
49
61
  return {
50
62
  configured: false,
51
- source: CONFIG_FILE
63
+ source: getConfigFilePath()
52
64
  };
53
65
  }
54
66
  return {
55
67
  configured: true,
56
- source: CONFIG_FILE,
68
+ source: getConfigFilePath(),
57
69
  home_url: saved.homeUrl || null,
58
70
  saved_at: saved.saved_at || null,
59
71
  updated_at: saved.updated_at || null,
@@ -103,7 +115,7 @@ export async function loginWithBrowser(flags = {}) {
103
115
  await openBrowser(result.url);
104
116
  const credentials = await result.done;
105
117
  await saveCredentials(credentials);
106
- console.log(`Saved Yuque credentials to ${CONFIG_FILE}`);
118
+ console.log(`Saved Yuque credentials to ${getConfigFilePath()}`);
107
119
  }
108
120
  export async function loginManually(flags = {}) {
109
121
  const sessionFromFlag = typeof flags.session === 'string' ? flags.session.trim() : '';
@@ -116,7 +128,7 @@ export async function loginManually(flags = {}) {
116
128
  homeUrl: normalizeYuqueHomeUrl(homeUrlFromFlag),
117
129
  saved_at: new Date().toISOString()
118
130
  });
119
- console.log(`Saved Yuque credentials to ${CONFIG_FILE}`);
131
+ console.log(`Saved Yuque credentials to ${getConfigFilePath()}`);
120
132
  return;
121
133
  }
122
134
  if (!process.stdin.isTTY) {
@@ -137,17 +149,18 @@ export async function loginManually(flags = {}) {
137
149
  homeUrl: normalizeYuqueHomeUrl(homeUrlInput.trim()),
138
150
  saved_at: new Date().toISOString()
139
151
  });
140
- console.log(`Saved Yuque credentials to ${CONFIG_FILE}`);
152
+ console.log(`Saved Yuque credentials to ${getConfigFilePath()}`);
141
153
  }
142
154
  finally {
143
155
  rl.close();
144
156
  }
145
157
  }
146
158
  function readSavedCredentials() {
147
- if (!existsSync(CONFIG_FILE))
159
+ const configFile = getConfigFilePath();
160
+ if (!existsSync(configFile))
148
161
  return null;
149
162
  try {
150
- return JSON.parse(readFileSync(CONFIG_FILE, 'utf8'));
163
+ return JSON.parse(readFileSync(configFile, 'utf8'));
151
164
  }
152
165
  catch {
153
166
  return null;
@@ -155,14 +168,16 @@ function readSavedCredentials() {
155
168
  }
156
169
  async function saveCredentials(credentials) {
157
170
  const now = new Date().toISOString();
171
+ const configDir = getConfigDirPath();
172
+ const configFile = getConfigFilePath();
158
173
  const data = {
159
174
  ...credentials,
160
175
  saved_at: credentials.saved_at || now,
161
176
  updated_at: now
162
177
  };
163
- await mkdir(CONFIG_DIR, { recursive: true, mode: 0o700 });
164
- await writeFile(CONFIG_FILE, `${JSON.stringify(data, null, 2)}\n`, { mode: 0o600 });
165
- await chmod(CONFIG_FILE, 0o600);
178
+ await mkdir(configDir, { recursive: true, mode: 0o700 });
179
+ await writeFile(configFile, `${JSON.stringify(data, null, 2)}\n`, { mode: 0o600 });
180
+ await chmod(configFile, 0o600);
166
181
  }
167
182
  function ageHours(value) {
168
183
  if (!value)
@@ -318,6 +333,7 @@ function sendHtml(res, html, status = 200) {
318
333
  res.end(html);
319
334
  }
320
335
  function loginPage(error = '') {
336
+ const configFile = getConfigFilePath();
321
337
  return `<!doctype html>
322
338
  <html lang="zh-CN">
323
339
  <head>
@@ -362,7 +378,7 @@ function loginPage(error = '') {
362
378
  <p>本地保存语雀网页 Session,让 CLI 使用浏览器同款 Cookie 访问语雀。</p>
363
379
  </div>
364
380
  <div class="facts">
365
- <div class="fact"><strong>保存位置</strong><span>~/.config/yuque-cookie-plugin/config.json</span></div>
381
+ <div class="fact"><strong>保存位置</strong><span>${escapeHtml(configFile)}</span></div>
366
382
  <div class="fact"><strong>安全边界</strong><span>只写入本机配置,不写入项目仓库。</span></div>
367
383
  <div class="fact"><strong>失效处理</strong><span>后续可用 auth-status 检测并提示重新登录。</span></div>
368
384
  </div>
package/dist/cli.js CHANGED
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import process from 'node:process';
3
3
  import path from 'node:path';
4
- import { cp, mkdir, readFile, writeFile } from 'node:fs/promises';
4
+ import { cp, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
5
5
  import { existsSync } from 'node:fs';
6
6
  import { fileURLToPath } from 'node:url';
7
7
  import { YuqueCookieClient } from "./client-cookie.js";
8
8
  import { diffLakeSnapshots } from "./lake-diff.js";
9
9
  import { writeJson } from "./fs-utils.js";
10
- import { getConfigFilePath, getCredentialStatus, getCredentials, loginWithBrowser, recordCredentialValidation } from "./auth.js";
10
+ import { clearSavedCredentials, getConfigFilePath, getCredentialStatus, getCredentials, loginWithBrowser, recordCredentialValidation } from "./auth.js";
11
11
  import { serializeHtmlToLake } from "./editor-bridge.js";
12
12
  import { snapshotToMarkdown } from "./lake-markdown.js";
13
13
  import { downloadBook, downloadDocs } from "./downloader.js";
@@ -22,9 +22,11 @@ function help() {
22
22
  Usage:
23
23
  yuque-local --version
24
24
  yuque-local auth login [--manual]
25
+ yuque-local auth logout [--json]
25
26
  yuque-local auth status [doc-or-book-url] [--json]
26
27
  yuque-local doctor [--json]
27
- yuque-local skill install|status [--json]
28
+ yuque-local skill install|status|uninstall [--json]
29
+ yuque-local book list [--json]
28
30
  yuque-local book create --name <name> --slug <slug> [--description <text>] [--public]
29
31
  yuque-local book download <book-url> [--dist-dir download] [--incremental]
30
32
  yuque-local doc create <book-url> --title <title> --markdown-file <file> [--number-headings]
@@ -131,9 +133,11 @@ function normalizeInvocation(argv) {
131
133
  const grouped = {
132
134
  auth: {
133
135
  login: 'login',
136
+ logout: 'logout',
134
137
  status: 'auth-status'
135
138
  },
136
139
  book: {
140
+ list: 'list-books',
137
141
  create: 'create-book',
138
142
  download: 'download-book',
139
143
  inspect: 'inspect'
@@ -286,9 +290,24 @@ async function commandSkill(action, flags) {
286
290
  console.log(JSON.stringify(data, null, 2));
287
291
  return;
288
292
  }
293
+ const source = path.join(PACKAGE_ROOT, 'skills', 'yuque-cookie-plugin');
294
+ if (action === 'uninstall') {
295
+ const targets = skillTargets();
296
+ const removed = [];
297
+ for (const target of targets) {
298
+ const existed = existsSync(path.join(target, 'SKILL.md'));
299
+ await rm(target, { recursive: true, force: true });
300
+ removed.push({ path: target, removed: existed });
301
+ }
302
+ const data = { removed };
303
+ if (flags.json)
304
+ printJsonResult(jsonResult('success', 'skill uninstalled', data));
305
+ else
306
+ console.log(JSON.stringify(data, null, 2));
307
+ return;
308
+ }
289
309
  if (action !== 'install')
290
310
  throw new Error(`Unknown skill action: ${action}`);
291
- const source = path.join(PACKAGE_ROOT, 'skills', 'yuque-cookie-plugin');
292
311
  if (!existsSync(path.join(source, 'SKILL.md')))
293
312
  throw new Error(`Skill source is missing: ${source}`);
294
313
  const installed = [];
@@ -371,6 +390,17 @@ async function main() {
371
390
  await loginWithBrowser(flags);
372
391
  return;
373
392
  }
393
+ if (command === 'logout') {
394
+ const result = await clearSavedCredentials();
395
+ printCommandResult({
396
+ ok: true,
397
+ ...result,
398
+ note: process.env.YUQUE_SESSION || process.env.YUQUE_CTOKEN
399
+ ? 'Environment credentials are still set in the current shell.'
400
+ : undefined
401
+ }, flags, 'auth credentials cleared');
402
+ return;
403
+ }
374
404
  if (command === 'doctor') {
375
405
  await commandDoctor(flags);
376
406
  return;
@@ -435,6 +465,12 @@ async function main() {
435
465
  printCommandResult(info, flags, 'inspect completed');
436
466
  return;
437
467
  }
468
+ if (command === 'list-books') {
469
+ const result = await client.listBooks();
470
+ await recordCredentialValidation(true);
471
+ printCommandResult(result, flags, 'book list completed');
472
+ return;
473
+ }
438
474
  if (command === 'snapshot') {
439
475
  const [url] = positional;
440
476
  if (!url)
@@ -54,6 +54,17 @@ export class YuqueCookieClient {
54
54
  const appData = await this.fetchAppData(url);
55
55
  return summarizeAppData(appData);
56
56
  }
57
+ async listBooks() {
58
+ const result = await this.requestJson('GET', '/api/mine/books?offset=0&limit=100', undefined, `${this.host}/dashboard`);
59
+ const books = normalizeBookListResponse(result)
60
+ .map((book) => normalizeBookSummary(book, this.host))
61
+ .filter((book) => Boolean(book));
62
+ return {
63
+ ok: true,
64
+ count: books.length,
65
+ books
66
+ };
67
+ }
57
68
  async getBookInfo(url) {
58
69
  const appData = await this.fetchAppData(url);
59
70
  if (!appData.book?.id)
@@ -490,6 +501,40 @@ function buildDocUrl(host, appData, book, doc) {
490
501
  return `${host}/${bookSlug}/${docSlug}`;
491
502
  return `${host}/api/docs/${doc.id || ''}`;
492
503
  }
504
+ function normalizeBookListResponse(result) {
505
+ const data = result.data;
506
+ if (Array.isArray(data))
507
+ return data;
508
+ if (Array.isArray(data?.books))
509
+ return data.books;
510
+ if (Array.isArray(data?.repos))
511
+ return data.repos;
512
+ if (Array.isArray(result.books))
513
+ return result.books;
514
+ if (Array.isArray(result.repos))
515
+ return result.repos;
516
+ return [];
517
+ }
518
+ function normalizeBookSummary(book, host) {
519
+ const slug = typeof book.slug === 'string' ? book.slug : '';
520
+ const login = typeof book.login === 'string'
521
+ ? book.login
522
+ : typeof book.user?.login === 'string'
523
+ ? book.user.login
524
+ : undefined;
525
+ if (!slug)
526
+ return null;
527
+ return {
528
+ id: typeof book.id === 'number' ? book.id : undefined,
529
+ name: typeof book.name === 'string' ? book.name : slug,
530
+ slug,
531
+ login,
532
+ public: typeof book.public === 'number' || typeof book.public === 'boolean' ? book.public : undefined,
533
+ items_count: typeof book.items_count === 'number' ? book.items_count : undefined,
534
+ updated_at: typeof book.updated_at === 'string' ? book.updated_at : undefined,
535
+ url: login ? `${host}/${login}/${slug}` : `${host}/${slug}`
536
+ };
537
+ }
493
538
  function guessContentType(fileName) {
494
539
  const ext = extname(fileName).toLowerCase();
495
540
  const types = {
package/docs/usage-zh.md CHANGED
@@ -17,6 +17,21 @@
17
17
 
18
18
  项目目录不会保存真实 Cookie。
19
19
 
20
+ 如果需要把全局 npm 版、源码开发版、Docker 或 AI 真实测试环境隔离开,可以通过 `YUQUE_COOKIE_PLUGIN_CONFIG_DIR` 指定独立配置目录:
21
+
22
+ ```bash
23
+ YUQUE_COOKIE_PLUGIN_CONFIG_DIR=/tmp/yuque-plugin-real-test yuque-local auth login
24
+ YUQUE_COOKIE_PLUGIN_CONFIG_DIR=/tmp/yuque-plugin-real-test yuque-local auth status --json
25
+ ```
26
+
27
+ 指定后,Cookie 会保存到:
28
+
29
+ ```bash
30
+ /tmp/yuque-plugin-real-test/config.json
31
+ ```
32
+
33
+ 不会影响默认的 `~/.config/yuque-cookie-plugin/config.json`。
34
+
20
35
  ## 2. 安装和准备
21
36
 
22
37
  普通用户推荐从 npm 安装:
@@ -97,6 +112,18 @@ Docker、SSH、远程服务器等无头环境中,推荐使用手动模式,
97
112
  yuque-local auth login --manual
98
113
  ```
99
114
 
115
+ 清理本机保存的 Cookie:
116
+
117
+ ```bash
118
+ yuque-local auth logout
119
+ ```
120
+
121
+ 如果你使用了隔离配置目录,清理时也要带上同一个环境变量:
122
+
123
+ ```bash
124
+ YUQUE_COOKIE_PLUGIN_CONFIG_DIR=/tmp/yuque-plugin-real-test yuque-local auth logout
125
+ ```
126
+
100
127
  正常执行 `inspect`、`snapshot`、`download-book`、`download-doc` 等真实语雀命令成功后,也会刷新 `last_validated_at`。如果检测到 401、403、页面无法解析等登录态问题,CLI 会提示重新登录。
101
128
 
102
129
  ### 额外 Cookie
@@ -123,7 +150,38 @@ export YUQUE_EXTRA_COOKIE_VALUE='<cookie-value>'
123
150
 
124
151
  额外 Cookie 不会改变本项目的主链路,只用于兼容特殊访问场景。
125
152
 
126
- ## 4. 下载整个知识库
153
+ ## 4. 列出知识库
154
+
155
+ 查看当前账号可访问的语雀知识库:
156
+
157
+ ```bash
158
+ yuque-local book list --json
159
+ ```
160
+
161
+ 这个命令会通过网页 Session 调用语雀列表接口,只输出知识库摘要,不会打印本机配置文件或 Cookie。
162
+
163
+ 输出示例:
164
+
165
+ ```json
166
+ {
167
+ "status": "success",
168
+ "message": "book list completed",
169
+ "data": {
170
+ "ok": true,
171
+ "count": 2,
172
+ "books": [
173
+ {
174
+ "name": "工控",
175
+ "slug": "ydv5i5",
176
+ "login": "your-login",
177
+ "url": "https://www.yuque.com/your-login/ydv5i5"
178
+ }
179
+ ]
180
+ }
181
+ }
182
+ ```
183
+
184
+ ## 5. 下载整个知识库
127
185
 
128
186
  推荐先下载到临时目录验证:
129
187
 
@@ -228,7 +286,7 @@ AI 或脚本可以用 `retry.args` 只重试失败文档。
228
286
 
229
287
  `resources.files` 只记录本地相对路径和大小,不保存 Cookie。
230
288
 
231
- ## 4.1 创建知识库和文档
289
+ ## 6. 创建知识库和文档
232
290
 
233
291
  创建私密测试知识库:
234
292
 
@@ -255,7 +313,7 @@ npm run yuque-local -- create-doc https://www.yuque.com/<your-login>/<your-book>
255
313
  - 不默认修改标题文本。
256
314
  - 返回 JSON 中 `format` 表示语雀创建接口的初始格式;如果默认 Lake 写回成功,会额外返回 `final_format: "lake"`。
257
315
 
258
- ## 4.2 上传和插入附件
316
+ ## 7. 上传和插入附件
259
317
 
260
318
  只上传文件,不写入正文:
261
319
 
@@ -327,7 +385,7 @@ npm run yuque-local -- create-doc https://www.yuque.com/<your-login>/<your-book>
327
385
  - 语雀会把 Markdown 创建结果自动转换成 Lake 文档,工具随后会默认写回 Lake,后续修改应按 Lake 链路处理。
328
386
  - 当前原生写入能力矩阵见 `docs/native-lake-capability.md`。图片、附件、思维导图、画板属于下一阶段真实探索项,不能在未验证前手写 card。
329
387
 
330
- ## 4.2 上传图片或附件探索
388
+ ## 8. 上传图片或附件探索
331
389
 
332
390
  只上传本地文件,不写入正文:
333
391
 
@@ -386,7 +444,7 @@ npm run yuque-local -- insert-attachment https://www.yuque.com/user/book/doc \
386
444
 
387
445
  和图片一样,`insert-attachment --dry-run` 会真实上传附件以获得语雀附件 URL,但不会修改文档正文。
388
446
 
389
- ## 5. 下载单篇或多篇文档
447
+ ## 9. 下载单篇或多篇文档
390
448
 
391
449
  下载单篇:
392
450
 
@@ -403,7 +461,7 @@ npm run yuque-local -- download-doc \
403
461
  --dist-dir /tmp/yuque-doc-test
404
462
  ```
405
463
 
406
- ## 6. 本地预览下载结果
464
+ ## 10. 本地预览下载结果
407
465
 
408
466
  只生成或检查 VitePress 配置,不启动长驻服务:
409
467
 
@@ -435,7 +493,7 @@ http://localhost:5173/
435
493
  npm run yuque-local -- serve-book /tmp/yuque-download-test/工控 --port 5173 --force
436
494
  ```
437
495
 
438
- ## 7. 快照和差异比较
496
+ ## 11. 快照和差异比较
439
497
 
440
498
  抓取语雀文档快照:
441
499
 
@@ -455,7 +513,7 @@ npm run yuque-local -- diff-lake /tmp/before.json /tmp/after.json
455
513
  npm run yuque-local -- lake-to-markdown /tmp/doc.snapshot.json --out /tmp/doc.md
456
514
  ```
457
515
 
458
- ## 8. HTML 转语雀 Lake
516
+ ## 12. HTML 转语雀 Lake
459
517
 
460
518
  把本地 HTML 交给语雀 Lake editor runtime 序列化:
461
519
 
@@ -471,7 +529,7 @@ npm run yuque-local -- editor-serialize --html-file article.html --out article.l
471
529
 
472
530
  说明使用的是 vendored Lake editor,而不是 fallback。
473
531
 
474
- ## 9. 安全写入语雀文档
532
+ ## 13. 安全写入语雀文档
475
533
 
476
534
  强烈建议先 dry-run:
477
535
 
@@ -497,7 +555,7 @@ backups/
497
555
  reports/
498
556
  ```
499
557
 
500
- ## 10. 文章格式化流程
558
+ ## 14. 文章格式化流程
501
559
 
502
560
  本地 HTML 到语雀文档的完整流程:
503
561
 
@@ -511,7 +569,7 @@ npm run yuque-local -- format-article https://www.yuque.com/user/book/doc --html
511
569
  npm run yuque-local -- format-article https://www.yuque.com/user/book/doc --html-file article.html
512
570
  ```
513
571
 
514
- ## 11. 常用选项
572
+ ## 15. 常用选项
515
573
 
516
574
  忽略图片下载:
517
575
 
@@ -559,15 +617,16 @@ npm run yuque-local -- format-article https://www.yuque.com/user/book/doc --html
559
617
 
560
618
  如果需要严格的 JSON 输出,建议加 `--quiet`。
561
619
 
562
- ## 12. 安全规则
620
+ ## 16. 安全规则
563
621
 
564
622
  - 不要把真实 `_yuque_session` 或 `yuque_ctoken` 写入项目文件。
565
623
  - 不要提交 `~/.config/yuque-cookie-plugin/config.json`。
624
+ - 真实测试多个版本时,用 `YUQUE_COOKIE_PLUGIN_CONFIG_DIR` 隔离配置目录。
566
625
  - 线上写入必须先 dry-run。
567
626
  - 批量写入前先对单篇文档做实验。
568
627
  - 章节编号不要直接改标题文本硬塞编号,必须先做 snapshot before/after diff。
569
628
 
570
- ## 13. 当前已验证场景
629
+ ## 17. 当前已验证场景
571
630
 
572
631
  已真实验证:
573
632
 
@@ -578,7 +637,7 @@ npm run yuque-local -- format-article https://www.yuque.com/user/book/doc --html
578
637
  - 第二次增量下载全部跳过。
579
638
  - 导出文件名保留正常空格。
580
639
 
581
- ## 14. 当前未完全覆盖场景
640
+ ## 18. 当前未完全覆盖场景
582
641
 
583
642
  仍需继续补齐:
584
643
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yuque-cookie-plugin",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "type": "module",
5
5
  "description": "Cookie-based Yuque CLI for AI agents, without MCP or OpenAPI token limits.",
6
6
  "homepage": "https://github.com/c-sunc6/yuque-cookie-plugin#readme",
@@ -2,7 +2,7 @@
2
2
  name: yuque-cookie-plugin
3
3
  description: Use when an AI agent needs to operate Yuque through the local yuque-local CLI with _yuque_session and yuque_ctoken, including login, auth status, inspect, snapshot, download, create, native Lake update, image/PDF insertion, and safe document formatting. This skill is for CLI orchestration, not MCP or Yuque OpenAPI token usage.
4
4
  metadata:
5
- version: "0.1.0"
5
+ version: "0.1.3"
6
6
  author: "c-sunc6"
7
7
  ---
8
8
 
@@ -33,8 +33,10 @@ Legacy flat commands such as `yuque-local snapshot` and `yuque-local download-bo
33
33
  ```bash
34
34
  yuque-local auth login
35
35
  yuque-local auth login --manual
36
+ yuque-local auth logout --json
36
37
  yuque-local auth status <doc-or-book-url> --json
37
38
  yuque-local doctor --json
39
+ yuque-local book list --json
38
40
  yuque-local book download <book-url> --dist-dir download --incremental --quiet --json
39
41
  yuque-local doc download <doc-url> --dist-dir download --quiet --json
40
42
  yuque-local doc create <book-url> --title "Title" --markdown-file article.md --number-headings --json
@@ -42,15 +44,17 @@ yuque-local doc snapshot <doc-url> --out /tmp/doc.snapshot.json --json
42
44
  yuque-local doc apply-lake <doc-url> --lake-file body_asl.html --dry-run --json
43
45
  yuque-local doc insert-image <doc-url> --file image.png --after-text "位置" --json
44
46
  yuque-local doc insert-attachment <doc-url> --file file.pdf --after-text "附件" --json
47
+ yuque-local skill uninstall --json
45
48
  ```
46
49
 
47
50
  ## Workflow
48
51
 
49
52
  1. Run `yuque-local doctor --json` to inspect local installation and credential state.
50
53
  2. If credentials are missing or stale, ask the user to run `yuque-local auth login` or `yuque-local auth login --manual`.
51
- 3. For reads and downloads, call the relevant `book` or `doc` command with `--json`.
52
- 4. For writes, snapshot first, prepare the change, run a dry run when available, then apply after user approval.
53
- 5. Save local reports and paths from the CLI result for later audit.
54
+ 3. For book discovery, use `yuque-local book list --json`; do not read or print the local config file.
55
+ 4. For reads and downloads, call the relevant `book` or `doc` command with `--json`.
56
+ 5. For writes, snapshot first, prepare the change, run a dry run when available, then apply after user approval.
57
+ 6. Save local reports and paths from the CLI result for later audit.
54
58
 
55
59
  ## References
56
60
 
@@ -14,6 +14,13 @@ auth.status:
14
14
  example: "yuque-local auth status https://www.yuque.com/user/book --json"
15
15
  reference: "reference/agent-spec.md"
16
16
 
17
+ auth.logout:
18
+ cli: "yuque-local auth logout"
19
+ description: "Remove saved local Yuque cookies from the user config file"
20
+ optional: ["--json"]
21
+ example: "yuque-local auth logout --json"
22
+ reference: "reference/agent-spec.md"
23
+
17
24
  doctor:
18
25
  cli: "yuque-local doctor"
19
26
  description: "Inspect CLI version, runtime, auth config path, and skill install status"
@@ -28,6 +35,20 @@ skill.install:
28
35
  example: "yuque-local skill install --json"
29
36
  reference: "reference/agent-spec.md"
30
37
 
38
+ skill.uninstall:
39
+ cli: "yuque-local skill uninstall"
40
+ description: "Remove the installed yuque-cookie-plugin AI skill from local supported agent skill directories"
41
+ optional: ["--json"]
42
+ example: "yuque-local skill uninstall --json"
43
+ reference: "reference/agent-spec.md"
44
+
45
+ book.list:
46
+ cli: "yuque-local book list"
47
+ description: "List Yuque books available to the current cookie-authenticated account without printing local credential files"
48
+ optional: ["--json"]
49
+ example: "yuque-local book list --json"
50
+ reference: "reference/agent-spec.md"
51
+
31
52
  book.download:
32
53
  cli: "yuque-local book download"
33
54
  description: "Download a Yuque knowledge base with resources and reports"
@@ -38,8 +38,24 @@ For Docker, SSH, or headless terminals:
38
38
  yuque-local auth login --manual
39
39
  ```
40
40
 
41
+ To clear saved local cookies:
42
+
43
+ ```bash
44
+ yuque-local auth logout --json
45
+ ```
46
+
41
47
  Never request raw cookie values in the conversation.
42
48
 
49
+ ## Book Discovery
50
+
51
+ Use the dedicated command:
52
+
53
+ ```bash
54
+ yuque-local book list --json
55
+ ```
56
+
57
+ Do not inspect, print, or copy the local `config.json` file to discover books. The CLI reads credentials internally and returns only book summaries.
58
+
43
59
  ## Safe Write Flow
44
60
 
45
61
  1. Snapshot the online document.