@cloudglab/confluence-cli 0.0.3 → 0.0.5

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/CHANGELOG.md CHANGED
@@ -2,6 +2,41 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## 0.0.5 - 2026-06-17
6
+
7
+ ### 修复
8
+
9
+ - `install` / `update` 链路:把仓库内 `skills/confluence-cli` 从指向 `.agents/skills/confluence-cli` 的符号链接替换为真实目录副本,避免 npm tarball 中 `skills/confluence-cli` 路径在用户机器上不可用导致“未找到已安装包内的 Confluence skill”错误。
10
+ - `install` / `update` 链路:`installMmdCli` 不再因 `curl -fsSL ... | sh` 网络失败而中断整个安装,改为捕获异常并输出 `已跳过 mmd-cli 安装:<原因>` 与后续 `--mermaid none` 退路提示。
11
+ - `install` / `update` 链路:`installSkillFromInstalledPackage` 在 `access` 失败时不再直接抛错,自动回退到 `installSkillFromNpmPackage`(`npm pack` + `tar -xzf` + `npx -y skills add`),覆盖包内 skill 缺失场景。
12
+
13
+ ### 测试
14
+
15
+ - `tests/install.test.ts` 新增回归用例 `本地 skill 缺失时自动回退到 npm 包解压安装,mmd-cli 失败不阻断安装`:mock `curl` 退出码 35、stderr 含 `curl: (35) LibreSSL SSL_connect...`,断言安装链路按 `npm install -g` → 跳过 `mmd-cli` → `npm pack` → `tar` → `npx -y skills add` 顺序完成,并校验 stdout 中包含“已跳过 mmd-cli 安装”与“正在自动回退到 npm 包解压安装”两行提示。
16
+
17
+ ### 说明
18
+
19
+ - 本次热修不改变 `mmd-cli` 运行时仍需系统 Chrome/Chromium 的前提;网络环境下无法自动安装 `mmd-cli` 时,建议用户改用 `--mermaid none` 保留代码块。
20
+ - 验证结果:`pnpm test -- tests/install.test.ts tests/skill.test.ts tests/tools/transfer.test.ts` 全部通过(3 个文件、10 个测试)。
21
+
22
+ ## 0.0.4 - 2026-06-17
23
+
24
+ ### 修复
25
+
26
+ - Mermaid 渲染器从 `@mermaid-js/mermaid-cli` 全量替换为 `coolamit/mermaid-cli` 的原生二进制 `mmd-cli`,不再保留 `mmdc` / Puppeteer 兼容路径。
27
+ - `install` / `update` 链路会自动执行上游 `mmd-cli` 安装脚本,避免 npm 安装阶段拉取 Chrome 失败。
28
+ - `uninstall` 链路会一并清理 `~/.local/bin/mmd-cli` 和 `/usr/local/bin/mmd-cli`。
29
+ - `uploadMarkdown` 的 Mermaid 附件匹配逻辑改为优先按预期文件名命中,减少多附件场景下图片宏指向错误的问题。
30
+
31
+ ### 测试
32
+
33
+ - `tests/install.test.ts` 新增 `mmd-cli` 安装步骤断言,覆盖 install / update 流程。
34
+ - `tests/tools/transfer.test.ts` 新增 Mermaid 渲染调用测试,校验 `mmd-cli` 的输入、输出、背景和缩放参数。
35
+
36
+ ### 说明
37
+
38
+ - `mmd-cli` 运行时仍要求系统已有 Chrome/Chromium;如果目标机器没有浏览器,可在上传时传 `--mermaid none`,保留 Mermaid 代码块。
39
+
5
40
  ## 0.0.3 - 2026-06-16
6
41
 
7
42
  ### Fixed
package/README.md CHANGED
@@ -21,6 +21,15 @@ pnpm test
21
21
  pnpm release:smoke-query --dry-run
22
22
  ```
23
23
 
24
+ 从 `0.0.4` 起,Mermaid 渲染不再依赖 `@mermaid-js/mermaid-cli`。CLI 安装/更新时会自动安装 `coolamit/mermaid-cli` 提供的原生二进制 `mmd-cli`,避开 Puppeteer/Chrome 下载链路,并在卸载时清理常见安装位置下的 `mmd-cli` 二进制。
25
+
26
+ `mmd-cli` 运行时仍需要系统已有 Chrome/Chromium;如果机器完全没有浏览器,可在上传时传 `--mermaid none`,保留 Mermaid 代码块。
27
+
28
+ 从 `0.0.5` 起,`install` / `update` 在以下两种情况会**继续完成安装**而不是中断:
29
+
30
+ - `mmd-cli` 官方安装脚本因网络问题失败时,会打印 `已跳过 mmd-cli 安装:<原因>` 与 `后续如需 Mermaid 图片渲染,可稍后重试安装,或在上传时传 --mermaid none 保留代码块。`。
31
+ - 全局包内 `skills/confluence-cli` 缺失时,会打印 `未找到已安装包内的 Confluence skill:<path>,正在自动回退到 npm 包解压安装...` 并自动用 `npm pack` + `tar` + `npx -y skills add` 完成 skill 安装,无需手动切换 `--skill-source npm`。
32
+
24
33
  安装/更新入口:
25
34
 
26
35
  ```bash
@@ -132,7 +141,7 @@ CONFLUENCE_SKIP_UPDATE_CHECK=true
132
141
  - Confluence 7.13.7 全量 REST API 端点注册与通用调用
133
142
  - MCP 页面/子页/评论/标签能力的语义化 CLI 命令
134
143
  - Markdown 转 Confluence Wiki Markup
135
- - Markdown/HTML 上传时默认用 `mermaid-cli` 将 Mermaid 渲染为 PNG 附件并以内置图片宏展示,必要时可用 `--mermaid none` 保留原代码块
144
+ - Markdown/HTML 上传时默认用 `mmd-cli` 将 Mermaid 渲染为 PNG 附件并以内置图片宏展示,必要时可用 `--mermaid none` 保留原代码块
136
145
  - mark 风格 metadata 生成
137
146
  - Markdown 上传预览、确认写入和附件上传
138
147
  - 页面下载为带 frontmatter 的 Markdown,并可下载附件和一层子页
@@ -143,7 +152,7 @@ CONFLUENCE_SKIP_UPDATE_CHECK=true
143
152
 
144
153
  `.opencode/opencode.json` 提供了两个项目级 OpenCode 命令:
145
154
 
146
- - `release`:复刻 zentao-cli 的 `/release` 流程,按 12 步固定顺序准备发布;默认手动发包,**未经再次授权不会执行 `npm publish`、`git push`、打 tag 或创建 GitHub Release**。
155
+ - `release`:复刻 zentao-cli 的 `/release` 流程,按 14 步固定顺序准备发布;默认手动发包,**未经再次授权不会执行 `npm publish`、`git push`、打 tag 或创建 GitHub Release**。
147
156
  - `smoke`:检查当前 CLI 的烟测与验证入口(`pnpm release:smoke-query --dry-run`、`pnpm typecheck`、`pnpm build` 等)。
148
157
 
149
158
  修改 `.opencode/opencode.json` 后,需要退出并重启 OpenCode 才能生效。
package/dist/install.js CHANGED
@@ -7,6 +7,7 @@ import { loadConfluenceConfig, normalizeConfig, saveConfig } from "./core/config
7
7
  import { writeUpdateCacheAfterInstall } from "./update-probe.js";
8
8
  const PACKAGE_NAME = "@cloudglab/confluence-cli";
9
9
  const GIT_SKILL_SOURCE = "cloudglab/confluence-cli";
10
+ const MMD_CLI_INSTALL_URL = "https://raw.githubusercontent.com/coolamit/mermaid-cli/master/install.sh";
10
11
  export async function runInstallCommand(args = []) {
11
12
  const options = parseInstallOptions(args);
12
13
  await installPackageAndSkill("安装", options);
@@ -56,6 +57,7 @@ function printSuccessGuide(action, status) {
56
57
  confluence update 更新 CLI 和 Skill
57
58
  confluence install --skip-config-check 仅安装,跳过配置校验
58
59
  confluence install --skill-global 把 skill 装到 user-level 全局目录
60
+ mmd-cli --version 检查 Mermaid 渲染器
59
61
  CONFLUENCE_DISABLE_WRITE=true 禁用真实写操作
60
62
 
61
63
  写操作提示:真实写入仍需显式传 confirm=true。
@@ -158,7 +160,7 @@ function parseUninstallOptions(args) {
158
160
  function printUninstallPreview(options) {
159
161
  const steps = [
160
162
  ...(!options.cliOnly ? ["卸载 confluence skill(项目级和全局级)"] : []),
161
- ...(!options.skillOnly ? ["卸载全局 CLI 包并清理 npm 残留目录"] : []),
163
+ ...(!options.skillOnly ? ["卸载全局 CLI 包、清理 npm 残留目录,并删除 mmd-cli 二进制"] : []),
162
164
  ...(shouldRemoveConfig(options) ? ["删除 ~/.confluence/config.json"] : ["保留 ~/.confluence/config.json"]),
163
165
  ];
164
166
  process.stdout.write(`卸载预览:\n${steps.map((step) => ` - ${step}`).join("\n")}\n\n真实执行请运行:\n confluence uninstall --confirm true\n npx -y ${PACKAGE_NAME}@latest uninstall --confirm true\n\n可选参数:\n --keep-config true 保留 Confluence 配置\n --cli-only true 只卸载 CLI\n --skill-only true 只卸载 skill\n`);
@@ -222,6 +224,7 @@ async function installPackageAndSkill(action, options) {
222
224
  if (!options.skillOnly) {
223
225
  await cleanupGlobalPackageResidues();
224
226
  await installGlobalCli(action);
227
+ await installMmdCli(action);
225
228
  }
226
229
  if (!options.cliOnly) {
227
230
  await installSkill(action, options);
@@ -242,6 +245,16 @@ async function installGlobalCli(action) {
242
245
  await runStep(`${action} Confluence CLI`, "npm", args);
243
246
  }
244
247
  }
248
+ async function installMmdCli(action) {
249
+ try {
250
+ await runStep(`${action} Mermaid 原生渲染器 mmd-cli`, "sh", ["-c", `curl -fsSL ${MMD_CLI_INSTALL_URL} | sh`]);
251
+ }
252
+ catch (error) {
253
+ const message = error instanceof Error ? error.message : String(error);
254
+ process.stdout.write(`已跳过 mmd-cli 安装:${message}\n`);
255
+ process.stdout.write("后续如需 Mermaid 图片渲染,可稍后重试安装,或在上传时传 --mermaid none 保留代码块。\n");
256
+ }
257
+ }
245
258
  function isNpmDirectoryNotEmptyError(error) {
246
259
  const message = error instanceof Error ? error.message : String(error);
247
260
  return message.includes("ENOTEMPTY") || message.toLowerCase().includes("directory not empty");
@@ -280,7 +293,9 @@ async function installSkillFromInstalledPackage(action, skillGlobal) {
280
293
  await access(skillPath);
281
294
  }
282
295
  catch {
283
- throw new Error(`未找到已安装包内的 Confluence skill:${skillPath}。可重试 --skill-source npm 或 --skill-source git。`);
296
+ process.stdout.write(`未找到已安装包内的 Confluence skill:${skillPath},正在自动回退到 npm 包解压安装...\n`);
297
+ await installSkillFromNpmPackage(action, skillGlobal);
298
+ return;
284
299
  }
285
300
  await runNpxStepWithRetry(`${action} Confluence skill`, createSkillAddArgs(skillPath, skillGlobal));
286
301
  }
@@ -311,6 +326,11 @@ async function uninstallSkill() {
311
326
  async function uninstallPackage() {
312
327
  await runStep("卸载 Confluence CLI", "npm", ["uninstall", "-g", PACKAGE_NAME]);
313
328
  await cleanupGlobalPackageResidues();
329
+ await removeMmdCliBinary();
330
+ }
331
+ async function removeMmdCliBinary() {
332
+ await rm(path.join(os.homedir(), ".local", "bin", "mmd-cli"), { force: true });
333
+ await rm("/usr/local/bin/mmd-cli", { force: true });
314
334
  }
315
335
  async function cleanupGlobalPackageResidues() {
316
336
  const globalNodeModules = (await runCommandOutput("npm", ["root", "-g"])).trim();
@@ -370,7 +390,7 @@ async function runStep(title, command, args) {
370
390
  }
371
391
  function runCommand(command, args) {
372
392
  return new Promise((resolve, reject) => {
373
- const child = spawn(command, args, { shell: process.platform === "win32" });
393
+ const child = spawn(command, args, { shell: process.platform === "win32", env: createInstallEnv() });
374
394
  let stderr = "";
375
395
  child.stdout?.pipe(process.stdout);
376
396
  child.stderr?.on("data", (chunk) => {
@@ -389,7 +409,7 @@ function runCommand(command, args) {
389
409
  }
390
410
  function runCommandOutput(command, args) {
391
411
  return new Promise((resolve, reject) => {
392
- const child = spawn(command, args, { shell: process.platform === "win32" });
412
+ const child = spawn(command, args, { shell: process.platform === "win32", env: createInstallEnv() });
393
413
  let stdout = "";
394
414
  let stderr = "";
395
415
  child.stdout?.on("data", (chunk) => { stdout += chunk.toString("utf8"); });
@@ -404,6 +424,12 @@ function runCommandOutput(command, args) {
404
424
  });
405
425
  });
406
426
  }
427
+ function createInstallEnv() {
428
+ return {
429
+ ...process.env,
430
+ CI: process.env.CI ?? "true",
431
+ };
432
+ }
407
433
  function renderBanner() {
408
434
  return [
409
435
  " ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ",
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.0.3",
2
+ "version": "0.0.5",
3
3
  "commands": [
4
4
  "addComment",
5
5
  "addLabels",
@@ -171,10 +171,9 @@ function applyMermaidImageMacros(body, generatedFiles, uploadedAttachments) {
171
171
  for (let index = 0; index < generatedFiles.length; index += 1) {
172
172
  const generated = generatedFiles[index];
173
173
  const uploaded = uploadedAttachments[index];
174
- const attachment = readUploadedAttachment(uploaded?.result);
175
- if (!attachment)
176
- continue;
177
- const macro = buildImageMacro(attachment.title ?? generated.attachmentName);
174
+ const attachment = readUploadedAttachment(uploaded?.result, generated.attachmentName);
175
+ const filename = attachment?.title ?? generated.attachmentName;
176
+ const macro = buildImageMacro(filename);
178
177
  output = output.replaceAll(`<p>${generated.marker}</p>`, macro).replaceAll(generated.marker, macro);
179
178
  }
180
179
  return output;
@@ -182,11 +181,17 @@ function applyMermaidImageMacros(body, generatedFiles, uploadedAttachments) {
182
181
  function buildImageMacro(filename) {
183
182
  return `<ac:image><ri:attachment ri:filename="${escapeXml(filename)}" /></ac:image>`;
184
183
  }
185
- function readUploadedAttachment(value) {
184
+ function readUploadedAttachment(value, expectedTitle) {
186
185
  if (typeof value !== "object" || value === null)
187
186
  return undefined;
188
187
  if ("results" in value && Array.isArray(value.results)) {
189
- return value.results?.[0];
188
+ const list = value.results ?? [];
189
+ if (expectedTitle) {
190
+ const matched = list.find((entry) => entry?.title === expectedTitle);
191
+ if (matched)
192
+ return matched;
193
+ }
194
+ return list[0];
190
195
  }
191
196
  if ("id" in value)
192
197
  return value;
@@ -377,29 +382,29 @@ function createMermaidFile(mermaidSource, pageTitle, sourceFile, idx, renderKind
377
382
  function renderMermaidFile(mermaidSource, outputFile, renderKind) {
378
383
  const inputFile = join(dirname(outputFile), `${basename(outputFile, extname(outputFile))}.mmd`);
379
384
  writeFileSync(inputFile, mermaidSource, "utf8");
380
- const mmdc = resolveMmdcBin();
385
+ const mmdCli = resolveMmdCliBin();
381
386
  const args = ["-i", inputFile, "-o", outputFile, "-b", "transparent"];
382
387
  if (renderKind === "png") {
383
- args.push("--scale", "3");
388
+ args.push("-s", "3");
384
389
  }
385
390
  try {
386
- execFileSync(mmdc, args, { stdio: "pipe" });
391
+ execFileSync(mmdCli, args, { stdio: "pipe" });
387
392
  }
388
393
  catch (error) {
389
394
  const message = error instanceof Error ? error.message : String(error);
390
- throw new Error(`Failed to render Mermaid as ${renderKind} with mermaid-cli: ${message}`);
395
+ throw new Error(`Failed to render Mermaid as ${renderKind} with mmd-cli: ${message}`);
391
396
  }
392
397
  }
393
- function resolveMmdcBin() {
394
- const extension = process.platform === "win32" ? ".cmd" : "";
398
+ function resolveMmdCliBin() {
399
+ const extension = process.platform === "win32" ? ".exe" : "";
395
400
  const candidates = [
396
- join(process.cwd(), "node_modules", ".bin", `mmdc${extension}`),
397
- join(dirname(process.argv[1] ?? process.cwd()), "..", "node_modules", ".bin", `mmdc${extension}`),
401
+ join(process.env.HOME ?? "", ".local", "bin", `mmd-cli${extension}`),
402
+ "/usr/local/bin/mmd-cli",
398
403
  ];
399
404
  const found = candidates.find((candidate) => existsSync(candidate));
400
405
  if (found)
401
406
  return found;
402
- return `mmdc${extension}`;
407
+ return `mmd-cli${extension}`;
403
408
  }
404
409
  function safeFileName(value) {
405
410
  return value.replace(/[\\/:*?"<>|]/g, "_");
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const VERSION = "0.0.3";
1
+ export declare const VERSION = "0.0.5";
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
- export const VERSION = "0.0.3";
1
+ export const VERSION = "0.0.5";
2
2
  //# sourceMappingURL=version.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudglab/confluence-cli",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "description": "Confluence REST CLI for agents and local automation",
5
5
  "repository": {
6
6
  "type": "git",
@@ -38,7 +38,6 @@
38
38
  "prepare": "node scripts/prepare.mjs"
39
39
  },
40
40
  "dependencies": {
41
- "@mermaid-js/mermaid-cli": "^11.15.0",
42
41
  "@whitebite/diagram-converter": "^0.2.0",
43
42
  "axios": "^1.7.9",
44
43
  "marked": "^18.0.5",