@vdfor/confluence-sync 0.0.1 → 0.3.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 ADDED
@@ -0,0 +1,148 @@
1
+ # @vdfor/confluence-sync
2
+
3
+ Confluence 文档同步 CLI 工具,支持将本地 Markdown 文件上传至 Confluence 并自动转换为 Confluence 页面格式。
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ # pnpm
9
+ pnpm add -g @vdfor/confluence-sync
10
+
11
+ # npm
12
+ npm install -g @vdfor/confluence-sync
13
+
14
+ # yarn
15
+ yarn global add @vdfor/confluence-sync
16
+ ```
17
+
18
+ ## 配置
19
+
20
+ ### 方式1:环境变量
21
+
22
+ ```bash
23
+ # 必填:Confluence 服务地址
24
+ export CONFLUENCE_BASE_URL="https://your-confluence-instance.com"
25
+
26
+ # 必填:Space Key
27
+ export CONFLUENCE_SPACE_KEY="YOUR_SPACE_KEY"
28
+
29
+ # 认证方式(二选一)
30
+ # 方式1(推荐):Personal Access Token
31
+ export CONFLUENCE_PAT="your-personal-access-token"
32
+
33
+ # 方式2:Basic Auth
34
+ export CONFLUENCE_USERNAME="your-username"
35
+ export CONFLUENCE_API_TOKEN="your-api-token"
36
+ ```
37
+
38
+ ### 方式2:CLI 参数(优先于环境变量)
39
+
40
+ ```bash
41
+ confluence-sync upload --file test.md \
42
+ --base-url https://your-confluence-instance.com \
43
+ --space-key YOUR_SPACE_KEY \
44
+ --pat your-token
45
+ ```
46
+
47
+ ## 命令
48
+
49
+ ### upload - 上传 Markdown 为新页面
50
+
51
+ ```bash
52
+ confluence-sync upload --file <path> [--title <标题>] [--space <key>] [--parent <ID或标题>] [--dry-run]
53
+ ```
54
+
55
+ ### update - 更新已有页面
56
+
57
+ ```bash
58
+ confluence-sync update --file <path> (--page-id <ID> | --title <标题>) [--space <key>] [--minor-edit]
59
+ ```
60
+
61
+ ### upload-file - 上传附件
62
+
63
+ ```bash
64
+ confluence-sync upload-file --page-id <ID> --file <path> [--name <文件名>]
65
+ ```
66
+
67
+ ### list-pages - 列出空间页面
68
+
69
+ ```bash
70
+ confluence-sync list-pages [--space <key>]
71
+ ```
72
+
73
+ ### list-files - 列出页面附件
74
+
75
+ ```bash
76
+ confluence-sync list-files --page-id <ID>
77
+ ```
78
+
79
+ ### get-content - 获取页面内容
80
+
81
+ ```bash
82
+ confluence-sync get-content --page-id <ID> [--markdown]
83
+ ```
84
+
85
+ 使用 `--markdown` 参数可将 Confluence 内容转换为 Markdown 格式输出。
86
+
87
+ ## 配置参数
88
+
89
+ | 参数 | 说明 |
90
+ |------|------|
91
+ | `--base-url` | Confluence 服务地址 |
92
+ | `--space-key` | Space Key |
93
+ | `--pat` | Personal Access Token |
94
+ | `--username` | Confluence 登录名 |
95
+ | `--api-token` | API Token 或密码 |
96
+ | `--space` | Space Key(覆盖默认值) |
97
+ | `--file` | 本地文件路径 |
98
+ | `--title` | 页面标题 |
99
+ | `--page-id` | 页面 ID |
100
+ | `--parent` | 父页面 ID 或标题 |
101
+ | `--dry-run` | 仅打印转换结果,不实际上传 |
102
+ | `--minor-edit` | 标记为小修改(不通知关注者) |
103
+ | `--markdown` | get-content 时输出为 Markdown 格式 |
104
+
105
+ ## 示例
106
+
107
+ ```bash
108
+ # 上传 Markdown 文件
109
+ confluence-sync upload --file README.md --title "项目文档" --space MYSPACE
110
+
111
+ # 更新已有页面
112
+ confluence-sync update --file README.md --page-id 123456
113
+
114
+ # 上传附件
115
+ confluence-sync upload-file --page-id 123456 --file image.png
116
+
117
+ # 列出空间所有页面
118
+ confluence-sync list-pages --space MYSPACE
119
+
120
+ # 获取页面内容
121
+ confluence-sync get-content --page-id 123456
122
+
123
+ # 获取页面内容并转换为 Markdown
124
+ confluence-sync get-content --page-id 123456 --markdown
125
+ ```
126
+
127
+ ## Skill 集成
128
+
129
+ 本项目包含 Trae Skill,安装后可被智能体调用。
130
+
131
+ ### 安装
132
+
133
+ ```bash
134
+ pnpm add -g @vdfor/confluence-sync
135
+ ```
136
+
137
+ ### 使用
138
+
139
+ 安装后,智能体可直接调用 `confluence-sync` 命令:
140
+
141
+ ```bash
142
+ confluence-sync upload --file README.md --title "项目文档" --space MYSPACE
143
+ confluence-sync get-content --page-id 123456 --markdown
144
+ ```
145
+
146
+ ## License
147
+
148
+ MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vdfor/confluence-sync",
3
- "version": "0.0.1",
3
+ "version": "0.3.0",
4
4
  "author": "vdfor",
5
5
  "license": "MIT",
6
6
  "private": false,
@@ -20,7 +20,8 @@
20
20
  },
21
21
  "files": [
22
22
  "upload.mjs",
23
- "src/"
23
+ "src/",
24
+ "skills/"
24
25
  ],
25
26
  "engines": {
26
27
  "node": ">=18"
@@ -37,9 +38,9 @@
37
38
  "upload": "node upload.mjs upload",
38
39
  "update": "node upload.mjs update",
39
40
  "help": "node upload.mjs help",
40
- "release:beta": "read -p '请输入版本号 (例如: patch, minor, major 或具体版本号): ' v && pnpm version $v && pnpm publish --registry https://registry.npmjs.org/ --access public --tag beta --no-git-checks",
41
- "release": "read -p '请输入版本号 (例如: patch, minor, major 或具体版本号): ' v && pnpm version $v && pnpm publish --registry https://registry.npmjs.org/ --access public --no-git-checks",
42
- "v": "read -p '请输入版本号 (例如: patch, minor, major 或具体版本号): ' v && pnpm version $v",
41
+ "release:beta": "read -p '请输入版本号 (例如: patch, minor, major 或具体版本号): ' v && NPM_CONFIG_GIT_CHECKS=false pnpm version $v --no-git-tag-version --no-git-checks && pnpm publish --tag beta --access public --no-git-checks",
42
+ "release": "read -p '请输入版本号 (例如: patch, minor, major 或具体版本号): ' v && NPM_CONFIG_GIT_CHECKS=false pnpm version $v --no-git-tag-version --no-git-checks && pnpm publish --access public --no-git-checks",
43
+ "v": "read -p '请输入版本号 (例如: patch, minor, major 或具体版本号): ' v && pnpm version $v --no-git-tag-version --no-git-checks",
43
44
  "clean": "rimraf dist node_modules"
44
45
  }
45
46
  }
@@ -0,0 +1,101 @@
1
+ ---
2
+ name: "confluence-sync"
3
+ description: "Confluence 文档同步 CLI 工具,支持将本地 Markdown 文件上传至 Confluence 并自动转换为 Confluence 页面格式。当用户需要上传、更新、获取 Confluence 页面内容或管理附件时调用。"
4
+ ---
5
+
6
+ # Confluence Sync
7
+
8
+ Confluence 文档同步 CLI 工具,支持将本地 Markdown 文件上传至 Confluence 并自动转换为 Confluence 页面格式。
9
+
10
+ ## 功能特性
11
+
12
+ - 上传 Markdown 文件为 Confluence 页面
13
+ - 更新已有页面内容
14
+ - 上传文件作为页面附件
15
+ - 列出空间页面和页面附件
16
+ - 获取页面内容(支持转换为 Markdown 格式)
17
+
18
+ ## 配置要求
19
+
20
+ 使用前需要配置以下环境变量或 CLI 参数:
21
+
22
+ ### 必填配置
23
+
24
+ - `CONFLUENCE_BASE_URL`: Confluence 服务地址
25
+ - `CONFLUENCE_SPACE_KEY`: Space Key
26
+ - 认证方式(二选一):
27
+ - `CONFLUENCE_PAT`: Personal Access Token(推荐)
28
+ - `CONFLUENCE_USERNAME` + `CONFLUENCE_API_TOKEN`: Basic Auth 认证
29
+
30
+ ### 可选配置
31
+
32
+ - `--space`: Space Key(覆盖默认值)
33
+
34
+ ## 命令列表
35
+
36
+ ### upload - 上传 Markdown 为新页面
37
+
38
+ ```bash
39
+ confluence-sync upload --file <path> [--title <标题>] [--space <key>] [--parent <ID或标题>] [--dry-run]
40
+ ```
41
+
42
+ ### update - 更新已有页面
43
+
44
+ ```bash
45
+ confluence-sync update --file <path> (--page-id <ID> | --title <标题>) [--space <key>] [--minor-edit]
46
+ ```
47
+
48
+ ### upload-file - 上传附件
49
+
50
+ ```bash
51
+ confluence-sync upload-file --page-id <ID> --file <path> [--name <文件名>]
52
+ ```
53
+
54
+ ### list-pages - 列出空间页面
55
+
56
+ ```bash
57
+ confluence-sync list-pages [--space <key>]
58
+ ```
59
+
60
+ ### list-files - 列出页面附件
61
+
62
+ ```bash
63
+ confluence-sync list-files --page-id <ID>
64
+ ```
65
+
66
+ ### get-content - 获取页面内容
67
+
68
+ ```bash
69
+ confluence-sync get-content --page-id <ID> [--markdown]
70
+ ```
71
+
72
+ 使用 `--markdown` 参数可将 Confluence 内容转换为 Markdown 格式输出。
73
+
74
+ ## 使用示例
75
+
76
+ ```bash
77
+ # 上传 Markdown 文件
78
+ confluence-sync upload --file README.md --title "项目文档" --space MYSPACE
79
+
80
+ # 更新已有页面
81
+ confluence-sync update --file README.md --page-id 123456
82
+
83
+ # 上传附件
84
+ confluence-sync upload-file --page-id 123456 --file image.png
85
+
86
+ # 列出空间所有页面
87
+ confluence-sync list-pages --space MYSPACE
88
+
89
+ # 获取页面内容
90
+ confluence-sync get-content --page-id 123456
91
+
92
+ # 获取页面内容并转换为 Markdown
93
+ confluence-sync get-content --page-id 123456 --markdown
94
+ ```
95
+
96
+ ## 注意事项
97
+
98
+ 1. 使用前请确保已正确配置认证信息
99
+ 2. Personal Access Token 需要在 Confluence 个人设置中生成
100
+ 3. 上传附件时,如果附件已存在会自动更新
101
+ 4. 使用 `--dry-run` 参数可以预览转换结果而不实际上传
@@ -1,13 +1,10 @@
1
1
  import axios from 'axios';
2
2
  import { marked } from 'marked';
3
- import { readFileSync } from 'node:fs';
4
3
  import { extname } from 'node:path';
5
4
 
6
5
  // ── 配置 ──────────────────────────────────────────────────────────────────────
7
- const config = JSON.parse(readFileSync(new URL('../config.json', import.meta.url), 'utf-8'));
8
- const { spaceKey: defaultSpaceKey } = config;
9
-
10
6
  let baseUrl = process.env.CONFLUENCE_BASE_URL;
7
+ let spaceKey = process.env.CONFLUENCE_SPACE_KEY;
11
8
  let pat = process.env.CONFLUENCE_PAT;
12
9
  let username = process.env.CONFLUENCE_USERNAME;
13
10
  let apiToken = process.env.CONFLUENCE_API_TOKEN;
@@ -17,12 +14,14 @@ let api;
17
14
  * 用 CLI 参数覆盖环境变量配置,需在其他函数调用前执行
18
15
  * @param {object} [opts]
19
16
  * @param {string} [opts.baseUrl]
17
+ * @param {string} [opts.spaceKey]
20
18
  * @param {string} [opts.pat]
21
19
  * @param {string} [opts.username]
22
20
  * @param {string} [opts.apiToken]
23
21
  */
24
22
  export function configure(opts = {}) {
25
23
  if (opts.baseUrl) baseUrl = opts.baseUrl;
24
+ if (opts.spaceKey) spaceKey = opts.spaceKey;
26
25
  if (opts.pat) pat = opts.pat;
27
26
  if (opts.username) username = opts.username;
28
27
  if (opts.apiToken) apiToken = opts.apiToken;
@@ -271,6 +270,104 @@ export function mdToConfluence(md) {
271
270
  return renderTokens(tokens);
272
271
  }
273
272
 
273
+ // ── Confluence Storage Format → Markdown ─────────────────────────────────────
274
+
275
+ /**
276
+ * 将 Confluence storage format(XHTML)转换为 Markdown
277
+ * @param {string} html - Confluence storage format HTML
278
+ * @returns {string} Markdown 文本
279
+ */
280
+ export function confluenceToMd(html) {
281
+ if (!html) return '';
282
+
283
+ let md = html;
284
+
285
+ // 代码块:<ac:structured-macro ac:name="code">
286
+ md = md.replace(/<ac:structured-macro\s+ac:name="code">[\s\S]*?<ac:parameter\s+ac:name="language">([\s\S]*?)<\/ac:parameter>[\s\S]*?<ac:plain-text-body><!\[CDATA\[([\s\S]*?)\]\]><\/ac:plain-text-body>[\s\S]*?<\/ac:structured-macro>/g,
287
+ (_, lang, code) => `\n\`\`\`${lang}\n${code}\n\`\`\`\n`);
288
+
289
+ // 标题
290
+ md = md.replace(/<h1[^>]*>([\s\S]*?)<\/h1>/gi, '\n# $1\n');
291
+ md = md.replace(/<h2[^>]*>([\s\S]*?)<\/h2>/gi, '\n## $1\n');
292
+ md = md.replace(/<h3[^>]*>([\s\S]*?)<\/h3>/gi, '\n### $1\n');
293
+ md = md.replace(/<h4[^>]*>([\s\S]*?)<\/h4>/gi, '\n#### $1\n');
294
+ md = md.replace(/<h5[^>]*>([\s\S]*?)<\/h5>/gi, '\n##### $1\n');
295
+ md = md.replace(/<h6[^>]*>([\s\S]*?)<\/h6>/gi, '\n###### $1\n');
296
+
297
+ // 粗体、斜体、删除线、行内代码
298
+ md = md.replace(/<strong>([\s\S]*?)<\/strong>/gi, '**$1**');
299
+ md = md.replace(/<b>([\s\S]*?)<\/b>/gi, '**$1**');
300
+ md = md.replace(/<em>([\s\S]*?)<\/em>/gi, '*$1*');
301
+ md = md.replace(/<i>([\s\S]*?)<\/i>/gi, '*$1*');
302
+ md = md.replace(/<s>([\s\S]*?)<\/s>/gi, '~~$1~~');
303
+ md = md.replace(/<del>([\s\S]*?)<\/del>/gi, '~~$1~~');
304
+ md = md.replace(/<code>([\s\S]*?)<\/code>/gi, '`$1`');
305
+
306
+ // 链接
307
+ md = md.replace(/<a\s+href="([^"]*)"[^>]*>([\s\S]*?)<\/a>/gi, '[$2]($1)');
308
+
309
+ // 图片:<ac:image><ri:attachment ri:filename="xxx" /></ac:image>
310
+ md = md.replace(/<ac:image>[\s\S]*?<ri:attachment\s+ri:filename="([^"]*)"[\s\S]*?\/>[\s\S]*?<\/ac:image>/gi, '![]($1)');
311
+
312
+ // 普通图片
313
+ md = md.replace(/<img\s+[^>]*src="([^"]*)"[^>]*\/?>/gi, '![]($1)');
314
+
315
+ // 水平线
316
+ md = md.replace(/<hr\s*\/?>/gi, '\n---\n');
317
+
318
+ // 换行
319
+ md = md.replace(/<br\s*\/?>/gi, '\n');
320
+
321
+ // 段落
322
+ md = md.replace(/<p>([\s\S]*?)<\/p>/gi, '\n$1\n');
323
+
324
+ // 引用块
325
+ md = md.replace(/<blockquote>([\s\S]*?)<\/blockquote>/gi, (_, content) => {
326
+ return content.split('\n').map(line => `> ${line}`).join('\n');
327
+ });
328
+
329
+ // 无序列表
330
+ md = md.replace(/<ul>([\s\S]*?)<\/ul>/gi, (_, content) => {
331
+ return content.replace(/<li>([\s\S]*?)<\/li>/gi, '- $1\n');
332
+ });
333
+
334
+ // 有序列表
335
+ md = md.replace(/<ol>([\s\S]*?)<\/ol>/gi, (_, content) => {
336
+ let i = 0;
337
+ return content.replace(/<li>([\s\S]*?)<\/li>/gi, () => `${++i}. $1\n`);
338
+ });
339
+
340
+ // 表格
341
+ md = md.replace(/<table>([\s\S]*?)<\/table>/gi, (_, tableContent) => {
342
+ let rows = [];
343
+ // 表头
344
+ const headerMatch = tableContent.match(/<thead>([\s\S]*?)<\/thead>/i);
345
+ if (headerMatch) {
346
+ const cells = headerMatch[1].match(/<th[^>]*>([\s\S]*?)<\/th>/gi) || [];
347
+ rows.push(cells.map(c => c.replace(/<th[^>]*>([\s\S]*?)<\/th>/i, '$1').trim()));
348
+ rows.push(rows[0].map(() => '---'));
349
+ }
350
+ // 表体
351
+ const bodyMatch = tableContent.match(/<tbody>([\s\S]*?)<\/tbody>/i);
352
+ if (bodyMatch) {
353
+ const trMatches = bodyMatch[1].match(/<tr>([\s\S]*?)<\/tr>/gi) || [];
354
+ for (const tr of trMatches) {
355
+ const cells = tr.match(/<td[^>]*>([\s\S]*?)<\/td>/gi) || [];
356
+ rows.push(cells.map(c => c.replace(/<td[^>]*>([\s\S]*?)<\/td>/i, '$1').trim()));
357
+ }
358
+ }
359
+ return '\n' + rows.map(r => '| ' + r.join(' | ') + ' |').join('\n') + '\n';
360
+ });
361
+
362
+ // 移除剩余 HTML 标签
363
+ md = md.replace(/<[^>]+>/g, '');
364
+
365
+ // 清理多余空行
366
+ md = md.replace(/\n{3,}/g, '\n\n');
367
+
368
+ return md.trim();
369
+ }
370
+
274
371
  // ── 文件上传(附件)────────────────────────────────────────────────────────────
275
372
 
276
373
  /**
@@ -381,7 +478,8 @@ async function resolveParentId(parentIdentifier, spaceKey) {
381
478
  * @param {boolean} [opts.dryRun] - 仅打印转换结果
382
479
  */
383
480
  export async function uploadMarkdown(opts) {
384
- const { filePath, title: optTitle, spaceKey = defaultSpaceKey, parentId: parentRaw, dryRun = false } = opts;
481
+ const { filePath, title: optTitle, spaceKey: spaceKeyOpt, parentId: parentRaw, dryRun = false } = opts;
482
+ const effectiveSpaceKey = spaceKeyOpt || spaceKey;
385
483
  const fs = await import('node:fs');
386
484
  const path = await import('node:path');
387
485
 
@@ -392,16 +490,16 @@ export async function uploadMarkdown(opts) {
392
490
  if (dryRun) {
393
491
  console.log('── 转换结果(dry-run)────────────────────────');
394
492
  console.log(` 标题: ${title}`);
395
- console.log(` Space: ${spaceKey}`);
493
+ console.log(` Space: ${effectiveSpaceKey}`);
396
494
  console.log(' Body:\n' + bodyHtml);
397
495
  return null;
398
496
  }
399
497
 
400
498
  // 查找父页面
401
- const parentId = await resolveParentId(parentRaw, spaceKey);
499
+ const parentId = await resolveParentId(parentRaw, effectiveSpaceKey);
402
500
 
403
501
  // 检查是否已存在同名页面
404
- const existing = await getPageByTitle(spaceKey, title);
502
+ const existing = await getPageByTitle(effectiveSpaceKey, title);
405
503
  if (existing) {
406
504
  console.log(` 页面「${title}」已存在 (ID: ${existing.id}),将更新…`);
407
505
  const updated = await updatePage(existing.id, title, bodyHtml, existing.version.number);
@@ -409,7 +507,7 @@ export async function uploadMarkdown(opts) {
409
507
  return updated;
410
508
  }
411
509
 
412
- const page = await createPage(spaceKey, title, bodyHtml, parentId);
510
+ const page = await createPage(effectiveSpaceKey, title, bodyHtml, parentId);
413
511
  console.log(` ✅ 创建完成 → ${baseUrl}/pages/viewpage.action?pageId=${page.id}`);
414
512
  return page;
415
513
  }
@@ -418,7 +516,8 @@ export async function uploadMarkdown(opts) {
418
516
  * 更新指定页面(通过 ID 或 space+title 查找)
419
517
  */
420
518
  export async function updateMarkdown(opts) {
421
- const { pageId, title: searchTitle, filePath, spaceKey = defaultSpaceKey, minorEdit = false } = opts;
519
+ const { pageId, title: searchTitle, filePath, spaceKey: spaceKeyOpt, minorEdit = false } = opts;
520
+ const effectiveSpaceKey = spaceKeyOpt || spaceKey;
422
521
  const fs = await import('node:fs');
423
522
 
424
523
  const md = fs.readFileSync(filePath, 'utf-8');
@@ -428,8 +527,8 @@ export async function updateMarkdown(opts) {
428
527
  if (pageId) {
429
528
  page = await getPageById(pageId);
430
529
  } else if (searchTitle) {
431
- page = await getPageByTitle(spaceKey, searchTitle);
432
- if (!page) throw new Error(`未找到页面「${searchTitle}」(space: ${spaceKey})`);
530
+ page = await getPageByTitle(effectiveSpaceKey, searchTitle);
531
+ if (!page) throw new Error(`未找到页面「${searchTitle}」(space: ${effectiveSpaceKey})`);
433
532
  } else {
434
533
  throw new Error('必须指定 --page-id 或 --title 来定位目标页面');
435
534
  }
@@ -441,5 +540,5 @@ export async function updateMarkdown(opts) {
441
540
  }
442
541
 
443
542
  // ── 导出配置 ──────────────────────────────────────────────────────────────────
444
- export { defaultSpaceKey };
445
543
  export function getBaseUrl() { return baseUrl; }
544
+ export function getSpaceKey() { return spaceKey; }
package/upload.mjs CHANGED
@@ -4,6 +4,7 @@
4
4
  *
5
5
  * 环境变量(或通过 CLI 参数配置):
6
6
  * 必填: CONFLUENCE_BASE_URL - Confluence 服务地址
7
+ * CONFLUENCE_SPACE_KEY - Space Key
7
8
  * 认证方式(二选一):
8
9
  * 方式1 PAT: CONFLUENCE_PAT - Personal Access Token
9
10
  * 方式2 Basic Auth: CONFLUENCE_USERNAME - Confluence 登录名(工号)
@@ -15,10 +16,11 @@
15
16
  * confluence-sync upload-file --page-id <ID> --file <path> [--name <文件名>]
16
17
  * confluence-sync list-pages [--space <key>]
17
18
  * confluence-sync list-files --page-id <ID>
18
- * confluence-sync get-content --page-id <ID>
19
+ * confluence-sync get-content --page-id <ID> [--markdown]
19
20
  *
20
21
  * 可选配置参数(优先于环境变量):
21
22
  * --base-url Confluence 服务地址
23
+ * --space-key Space Key
22
24
  * --pat Personal Access Token
23
25
  * --username Confluence 登录名
24
26
  * --api-token API Token 或密码
@@ -31,8 +33,9 @@ import {
31
33
  listPages,
32
34
  listAttachments,
33
35
  getPageContent,
34
- defaultSpaceKey,
36
+ confluenceToMd,
35
37
  getBaseUrl,
38
+ getSpaceKey,
36
39
  configure,
37
40
  } from './src/confluence.mjs';
38
41
 
@@ -101,9 +104,11 @@ list-files 选项:
101
104
 
102
105
  get-content 选项:
103
106
  --page-id 页面 ID(必填)
107
+ --markdown 输出为 Markdown 格式(默认输出 Storage HTML)
104
108
 
105
109
  配置参数(优先于环境变量):
106
110
  --base-url Confluence 服务地址
111
+ --space-key Space Key
107
112
  --pat Personal Access Token
108
113
  --username Confluence 登录名
109
114
  --api-token API Token 或密码
@@ -111,6 +116,7 @@ get-content 选项:
111
116
  环境变量:
112
117
  必填:
113
118
  CONFLUENCE_BASE_URL Confluence 服务地址
119
+ CONFLUENCE_SPACE_KEY Space Key
114
120
 
115
121
  认证方式(二选一):
116
122
  方式1 - Personal Access Token:
@@ -137,11 +143,13 @@ async function main() {
137
143
  // 用 CLI 参数覆盖环境变量配置
138
144
  configure({
139
145
  baseUrl: args['base-url'],
146
+ spaceKey: args['space-key'],
140
147
  pat: args['pat'],
141
148
  username: args['username'],
142
149
  apiToken: args['api-token'],
143
150
  });
144
151
  const baseUrl = getBaseUrl();
152
+ const spaceKey = getSpaceKey();
145
153
 
146
154
  try {
147
155
  switch (command) {
@@ -153,7 +161,7 @@ async function main() {
153
161
  const result = await uploadMarkdown({
154
162
  filePath: args.file,
155
163
  title: args.title,
156
- spaceKey: args.space || defaultSpaceKey,
164
+ spaceKey: args.space || spaceKey,
157
165
  parentId: args.parent,
158
166
  dryRun: args['dry-run'] === true,
159
167
  });
@@ -180,7 +188,7 @@ async function main() {
180
188
  pageId: args['page-id'],
181
189
  title: args.title,
182
190
  filePath: args.file,
183
- spaceKey: args.space || defaultSpaceKey,
191
+ spaceKey: args.space || spaceKey,
184
192
  minorEdit: args['minor-edit'] === true,
185
193
  });
186
194
  console.log('\n页面信息:');
@@ -208,7 +216,7 @@ async function main() {
208
216
  }
209
217
 
210
218
  case 'list-pages': {
211
- const space = args.space || defaultSpaceKey;
219
+ const space = args.space || spaceKey;
212
220
  console.log(`Space: ${space}\n`);
213
221
  const pages = await listPages(space);
214
222
  if (!pages.length) {
@@ -252,8 +260,13 @@ async function main() {
252
260
  console.log(`标题: ${page.title}`);
253
261
  console.log(`Space: ${page.space?.key}`);
254
262
  console.log(`版本: ${page.version?.number}`);
255
- console.log(`\n── Storage HTML ──────────────────────────────\n`);
256
- console.log(page.body.storage.value);
263
+ if (args.markdown) {
264
+ console.log(`\n── Markdown ──────────────────────────────\n`);
265
+ console.log(confluenceToMd(page.body.storage.value));
266
+ } else {
267
+ console.log(`\n── Storage HTML ──────────────────────────────\n`);
268
+ console.log(page.body.storage.value);
269
+ }
257
270
  break;
258
271
  }
259
272