diff --git a/.env.example b/.env.example index e78bbd8..7ceb512 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,4 @@ -# 即页 环境变量示例 +# 纸鹊 Magpie 环境变量示例 # 复制为 .env 后填入真实值。docker-compose 会自动读取当前目录的 .env # 运行环境。生产环境务必设为 production @@ -9,7 +9,7 @@ PORT=8858 # 数据目录(数据库、会话、上传文件)。留空则默认 ./data。 # 用于把数据放到独立卷/分区;docker-compose 已把 ./data 挂载到 /app/data,通常无需改动。 -# JPAGE_DATA_DIR=/app/data +# MAGPIE_DATA_DIR=/app/data # 管理员用户名。首次启动且 users 表为空时生效;留空则默认 "admin" ADMIN_USER=admin @@ -53,7 +53,7 @@ SMTP_PORT=465 SMTP_SECURE=true SMTP_USER=noreply@example.com SMTP_PASS=your-password -SMTP_FROM="即页 " +SMTP_FROM="纸鹊 Magpie " APP_URL=http://localhost:8858 # --- 反馈邮件接收地址(可选)--- diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2e28e7a..2600fd6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,87 +1,22 @@ -# npm 自动发版:打 v* tag 触发,跑测试 + 校验版本号一致 + npm publish。 -# -# 用法(本地): -# 1. 改 package.json 的 version(如 1.5.1) -# 2. commit + push 到 main -# 3. git tag v1.5.1 && git push origin v1.5.1 -# 4. 本 workflow 自动跑:test → 校验 tag==version → npm publish -# -# 前置(仓库设置,一次性): -# GitHub 仓库 Settings → Secrets and variables → Actions → New repository secret -# Name: NPM_TOKEN -# Value: 在 npm 生成的 granular access token(@code2rich scope, Read and write) -# 生成地址:https://www.npmjs.com/settings/code2richnpm/tokens/granular-access-tokens/new -# -# 版本一致性:tag 名(去 v 前缀)必须等于 package.json 的 version,否则失败。 -# 这样能防止「tag 标 v1.6.0 但 package.json 还写着 1.5.0」这类手滑。 - -name: Release - +# Package preparation only. No registry publication occurs in this workflow. +name: Prepare npm package on: - push: - tags: ['v*'] - -# 同一 tag 不要并发跑(理论上 git push tag 是原子的,但保险起见) -concurrency: - group: release-${{ github.ref }} - cancel-in-progress: false - + workflow_dispatch: +permissions: + contents: read jobs: - release: - name: Publish to npm + package: runs-on: ubuntu-latest - # 同时跑测试矩阵确认绿(轻量:只 Node 20,CI workflow 已覆盖 20+22) steps: - uses: actions/checkout@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 + - uses: actions/setup-node@v4 with: - node-version: '20' - # 不启用 cache: 'npm':见 ci.yml 同款注释(EEXIST bug) - registry-url: 'https://registry.npmjs.org' - - - name: Install dependencies - run: npm ci - - - name: Lint - run: npm run lint - - - name: Run tests (unit + integration) - run: npm test - - - name: Build frontend bundle - run: npm run build - - - name: Verify tag matches package version - run: | - TAG="${GITHUB_REF_NAME#v}" - PKG=$(node -p "require('./package.json').version") - echo "tag (去 v 前缀): $TAG" - echo "package.json version: $PKG" - if [ "$TAG" != "$PKG" ]; then - echo "::error::tag ($TAG) 与 package.json version ($PKG) 不一致" - exit 1 - fi - - - name: Publish to npm - run: npm publish - env: - # setup-node 配置的 registry 会读这个环境变量做鉴权 - # publishConfig.access=public 已设,scoped 包公开发布无需 --access - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - - - name: Summary - if: always() - run: | - echo "## Release ${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - if [ "${{ job.status }}" = "success" ]; then - echo "✅ 已发布 \`$(node -p "require('./package.json').name")@$(node -p "require('./package.json').version")\`" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "安装:\`\`\`bash" >> $GITHUB_STEP_SUMMARY - echo "npm install -g $(node -p "require('./package.json').name")" >> $GITHUB_STEP_SUMMARY - echo "\`\`\`" >> $GITHUB_STEP_SUMMARY - else - echo "❌ 发布失败,查看上面日志" >> $GITHUB_STEP_SUMMARY - fi + node-version: '22' + - run: npm ci + - run: npm run lint + - run: npm test + - run: npm pack + - uses: actions/upload-artifact@v4 + with: + name: magpie-npm-package + path: magpie-*.tgz diff --git a/.gitignore b/.gitignore index 26cee47..89ff884 100644 --- a/.gitignore +++ b/.gitignore @@ -53,3 +53,13 @@ env/ # Operations docs containing credentials docs/market-operations.md + +# Local release and plugin build artifacts +*.tgz +plugin-workspace/dist/ + +# Local design reviews, backups and preview caches +.hallmark/ +.impeccable/ +backups/ +.thumbnails/ diff --git a/AGENTS.md b/AGENTS.md index 0e37c1f..23f6215 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,12 +1,12 @@ # AGENTS.md -本文件为 AI 编程助手(Codex / Claude Code 等)提供即页(jpage)项目的工作指南。读者应假设对项目一无所知;所有改动请以此文件为上下文基准。 +本文件为 AI 编程助手(Codex / Claude Code 等)提供纸鹊 Magpie项目的工作指南。读者应假设对项目一无所知;所有改动请以此文件为上下文基准。 --- ## 项目概述 -**即页(jpage)** 是一个零配置的 HTML / Markdown 即时预览与分享工具。用户上传 `.html`、`.md`/`.markdown` 或 `.zip` 文件后,即可得到在线渲染页面与短链接(`/s/:key`),无需额外部署流程。项目同时面向 AI 工作流:内置 MCP Streamable HTTP 端点与 CLI 工具,支持通过 API Token 自动上传、管理文件。 +**纸鹊 Magpie** 是一个零配置的 HTML / Markdown 即时预览与分享工具。用户上传 `.html`、`.md`/`.markdown` 或 `.zip` 文件后,即可得到在线渲染页面与短链接(`/s/:key`),无需额外部署流程。项目同时面向 AI 工作流:内置 MCP Streamable HTTP 端点与 CLI 工具,支持通过 API Token 自动上传、管理文件。 核心能力: @@ -15,7 +15,7 @@ - 文件版本历史与回滚 - 标签、分类、收藏 - 多用户 + admin / user 角色体系 -- API Token(`jp_` 前缀)与全局 `MCP_TOKEN` 双认证 +- API Token(`mg_` 前缀)与全局 `MCP_TOKEN` 双认证 - 内容模板市场(用户上架 → 管理员审核 → 公开使用) - Skills 注册与打包下载 @@ -99,11 +99,11 @@ mcp/ # MCP 实现 tools-tags.js # 标签 tool tools-categories.js # 分类 tool tools-content-templates.js # 内容模板 tool - resources.js # jpage://files / jpage://file/{id} + resources.js # magpie://files / magpie://file/{id} util.js / constants.js # 共享 -bin/ # CLI 工具(npm 包入口 `jpage`) - jpage.js # CLI 入口与命令分发 +bin/ # CLI 工具(npm 包入口 `magpie`) + magpie.js # CLI 入口与命令分发 args.js / config.js / client.js commands/ # upload、ls、cat、url、mv、rm、star、tags、skills、whoami、update @@ -177,7 +177,7 @@ npm run test:integration # 仅集成测试 - `test/unit/*.test.js`:纯函数与独立模块(util、render-cache、zip、fts、crypto、cli-args、cli-config)。 - `test/integration/*.test.js`:完整 Express app 集成测试(auth、users、tokens、files、versions、tags、categories、skills、admin、share、content-templates、cli)。 -- `test/helpers/setup.js`:每个测试文件使用独立数据目录 `data-test--`,require server 前设置 `JPAGE_DATA_DIR`,避免并发污染。 +- `test/helpers/setup.js`:每个测试文件使用独立数据目录 `data-test--`,require server 前设置 `MAGPIE_DATA_DIR`,避免并发污染。 - 手动 harness:`test/perf-harness.js`、`test/mcp-harness.js`、`test/perf-bench.js`、`test/browser-harness.js`、`test/dispatch-bench.js`、`test/run-server.sh`。 CI(`.github/workflows/ci.yml`)在 Node 20/22 矩阵上执行 `npm run lint`、`npm test`、`npm run build`。 @@ -238,7 +238,7 @@ CI(`.github/workflows/ci.yml`)在 Node 20/22 矩阵上执行 `npm run lint` - `data/token-key.key` — API Token 加密密钥(未设置 `TOKEN_ENCRYPTION_KEY` 时自动生成) - `data/backups/` — 自动备份目录(`BACKUP_CRON` 启用时) -数据目录可通过 `JPAGE_DATA_DIR` 覆盖,默认 `./data`。 +数据目录可通过 `MAGPIE_DATA_DIR` 覆盖,默认 `./data`。 ### 数据库 Schema @@ -288,7 +288,7 @@ CI(`.github/workflows/ci.yml`)在 Node 20/22 矩阵上执行 `npm run lint` ### MCP 端点 - 路径:`POST`/`GET`/`DELETE /mcp` -- 鉴权:`Authorization: Bearer ` 或任意用户级 API Token(`jp_` 前缀)。未配置任何 token 时 `/mcp` 禁用。 +- 鉴权:`Authorization: Bearer ` 或任意用户级 API Token(`mg_` 前缀)。未配置任何 token 时 `/mcp` 禁用。 - 实现:`mcp/transport.js` 管理 Streamable HTTP session;`mcp/server.js` 注册 **17 个 tools + 2 个 resources**。 - Tools 类别: - 文件:`list_files`、`upload_file`、`get_file_content`、`delete_file`、`rename_file`、`get_file_url`、`star_file`、`unstar_file` @@ -296,12 +296,12 @@ CI(`.github/workflows/ci.yml`)在 Node 20/22 矩阵上执行 `npm run lint` - 标签:`list_tags`、`add_tags_to_file` - 分类:`list_categories`、`create_category`、`set_file_category` - 内容模板:`list_content_templates`、`get_content_template` -- Resources:`jpage://files`、`jpage://file/{id}`(内容 ≤ 256KB) +- Resources:`magpie://files`、`magpie://file/{id}`(内容 ≤ 256KB) - 内部:tool 通过 `lib/dispatch.js` 进程内调用 REST API,复用同一 Bearer token,权限/限流/审计与 HTTP 完全一致。 ### CLI -`bin/jpage.js` 提供 `jpage` 命令,token 优先级:`--token` > `JPAGE_TOKEN` 环境变量 > `MCP_TOKEN` 环境变量 > `.env`。 +`bin/magpie.js` 提供 `magpie` 命令,token 优先级:`--token` > `MAGPIE_TOKEN` 环境变量 > `MCP_TOKEN` 环境变量 > `.env`。 命令:`upload`、`ls`、`cat`、`url`、`mv`、`rm`、`star`、`unstar`、`tags`、`skills`、`whoami`、`update`。 @@ -311,11 +311,11 @@ CLI 与 MCP 共用同一套 REST API,是对等的两个客户端入口。 ## 安全与权限 -- **鉴权三选一**:(1) session cookie `jpage.sid`;(2) 用户级 API Token `jp_...`;(3) 全局 `MCP_TOKEN`(向后兼容)。`requireAuth` 设置 `req.userId` 与 `req.userRole`。 +- **鉴权三选一**:(1) session cookie `magpie.sid`;(2) 用户级 API Token `mg_...`;(3) 全局 `MCP_TOKEN`(向后兼容)。`requireAuth` 设置 `req.userId` 与 `req.userRole`。 - **角色**:`users.role` 为 `admin` 或 `user`。admin 可访问所有文件与用户;普通用户只能操作自己的文件和公开文件。 - **文件访问**:`loadFileWithPrivacy` 按 admin / 所有者 / 公开 / 未登录 分层校验;`checkFileOwnership` 用于写操作。 - **密码**:bcrypt 哈希(cost 10)。 -- **API Token**:`jp_` + 32 位 base62;数据库存 SHA-256 哈希用于鉴权,可选 AES-256-GCM 密文(`token_enc`)用于界面查看/复制。每用户最多 10 个。 +- **API Token**:`mg_` + 32 位 base62;数据库存 SHA-256 哈希用于鉴权,可选 AES-256-GCM 密文(`token_enc`)用于界面查看/复制。每用户最多 10 个。 - **限流**: - 登录 `30 req / 15 min / IP` - 注册 `5 req / 15 min / IP` @@ -374,9 +374,9 @@ CLI 与 MCP 共用同一套 REST API,是对等的两个客户端入口。 ## 发版与部署 - 版本号以 `package.json` 为准。 -- 推荐发版:`npm version patch|minor|major` → `git push origin main` → `git push origin vX.Y.Z`,由 `.github/workflows/release.yml` 自动执行 lint、test、build、校验 tag、发布到 npm。 +- 当前尚未发布 npm:使用 `npm pack` 制作本地安装包;`.github/workflows/release.yml` 仅手动验证与打包,不执行发布。正式发布前依照 `docs/RELEASING.md` 确认账号、版本及授权。 - Docker:`Dockerfile` 多阶段构建(builder / frontend / runner),`EXPOSE 8858`;`docker-compose.yml` 映射 host 8858 → container 8858,挂载 `./data:/app/data`。 -- 环境变量完整清单(按需配置):`PORT`、`NODE_ENV`、`JPAGE_DATA_DIR`、`ADMIN_USER`、`ADMIN_PASSWORD`、`SESSION_SECRET`、`COOKIE_SECURE`、`MCP_TOKEN`、`MCP_IP`、`MCP_PROTOCOL`、`TOKEN_ENCRYPTION_KEY`、`SMTP_HOST`、`SMTP_PORT`、`SMTP_SECURE`、`SMTP_USER`、`SMTP_PASS`、`SMTP_FROM`、`APP_URL`、`FEEDBACK_EMAIL`、`ALLOW_REGISTRATION`、`GITHUB_CLIENT_ID`、`GITHUB_CLIENT_SECRET`、`GOOGLE_CLIENT_ID`、`GOOGLE_CLIENT_SECRET`、`WECHAT_OPEN_APP_ID`、`WECHAT_OPEN_APP_SECRET`、`MAX_FILE_VERSIONS`、`BACKUP_CRON`、`BACKUP_DIR`、`ICP_BEIAN`。 +- 环境变量完整清单(按需配置):`PORT`、`NODE_ENV`、`MAGPIE_DATA_DIR`、`ADMIN_USER`、`ADMIN_PASSWORD`、`SESSION_SECRET`、`COOKIE_SECURE`、`MCP_TOKEN`、`MCP_IP`、`MCP_PROTOCOL`、`TOKEN_ENCRYPTION_KEY`、`SMTP_HOST`、`SMTP_PORT`、`SMTP_SECURE`、`SMTP_USER`、`SMTP_PASS`、`SMTP_FROM`、`APP_URL`、`FEEDBACK_EMAIL`、`ALLOW_REGISTRATION`、`GITHUB_CLIENT_ID`、`GITHUB_CLIENT_SECRET`、`GOOGLE_CLIENT_ID`、`GOOGLE_CLIENT_SECRET`、`WECHAT_OPEN_APP_ID`、`WECHAT_OPEN_APP_SECRET`、`MAX_FILE_VERSIONS`、`BACKUP_CRON`、`BACKUP_DIR`、`ICP_BEIAN`。 --- diff --git a/CLAUDE.md b/CLAUDE.md index 5428c36..293da58 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2,7 +2,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. -## Project: 即页 (jpage) +## Project: 纸鹊 Magpie (magpie) 零配置 HTML / Markdown 即时预览与分享工具。Express 服务(`server.js`)+ MCP server 模块(`mcp-server.js`)+ Skills 注册模块(`skills-registry.js`)。SQLite 存元数据与用户表,磁盘 `data/uploads/` 存原始文件,session 存 `data/sessions.sqlite`。多用户 + 角色体系(admin / 普通用户),bcrypt 密码哈希,可选开放注册(邮箱验证)。支持 Markdown 增强渲染(代码高亮、KaTeX 公式、Mermaid 图表)。 @@ -68,7 +68,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co **REST API**: - `GET /api/auth/me` — 当前用户(返回 `{id, username, email, emailVerified, role}`) -- `POST /api/auth/login` — `{account, password}` 或 `{username, password}`(统一入口,自动识别用户名或邮箱),设置 `jpage.sid` cookie,限流 10/15min +- `POST /api/auth/login` — `{account, password}` 或 `{username, password}`(统一入口,自动识别用户名或邮箱),设置 `magpie.sid` cookie,限流 10/15min - `POST /api/auth/register` — `{email?, username?, password, confirmPassword}`(至少提供 email 或 username,邮箱注册自动生成用户名) - `POST /api/auth/logout` — 销毁 session - `POST /api/auth/change-password` — `{currentPassword, newPassword}`,所有用户可用 @@ -137,7 +137,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co - `GET /api/skills/:name` — skill 详情(含 SKILL.md 内容、文件列表、INSTALL.md 渲染) - `GET /api/skills/:name/download` — ZIP 下载整个 skill 目录 - `GET /api/mcp/config` — 返回 MCP 连接配置(URL、Token 列表、多客户端 mcpServers JSON 片段;仅 MCP 客户端) -- `GET /api/cli/guide` — 返回 `jpage` CLI 用法指南(baseUrl、渲染后的 guideHtml、纯文本 guideText);CLI 与 MCP 是并列的两个客户端入口,各自独立端点 +- `GET /api/cli/guide` — 返回 `magpie` CLI 用法指南(baseUrl、渲染后的 guideHtml、纯文本 guideText);CLI 与 MCP 是并列的两个客户端入口,各自独立端点 **Skills registry** — `skills-registry.js` 自动发现 `skills/*/SKILL.md`,解析 YAML frontmatter(`name`, `description`, `version`, `author`)。Web UI 首页展示 Skills 区块,管理员可查看详情(弹窗)和下载 ZIP。ZIP 包与磁盘目录结构一致,可直接解压到 `~/.claude/skills/`。 @@ -150,7 +150,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co - 标签管理:`list_tags`, `add_tags_to_file` - 收藏管理:`star_file`, `unstar_file` - 分类管理:`list_categories`, `create_category`, `set_file_category` -- Resources(2 个):`jpage://files`(列表), `jpage://file/{id}`(内容,≤ 256KB) +- Resources(2 个):`magpie://files`(列表), `magpie://file/{id}`(内容,≤ 256KB) **Static + SPA fallback** — `public/` served by `express.static`; `/s/:key` short link route renders files directly; catch-all `app.get('*')` returns `public/index.html` for client-side routing between home and preview views. @@ -164,7 +164,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co - **SPA 兜底 + 构建产物注入** — `public/` 的 `express.static` 设 `index:false`(不让 static 自动把 `/` 映射到 index.html),由 `app.get('*')` 的 `getIndexHtml()` 返回 index.html 并按 `public/dist/manifest.json` 把 `/css/style.css?v=` 与 `/js/app.js?v=` 替换为 `/dist/.css|.js`。无 dist 时回退源文件路径。新增 SPA 路由 hash 时,注意它在 catch-all 之前由前端 `route()` 处理。 - **数据库共享** — 单个 `db` 连接复用于所有请求。Promise 封装 `dbRun`/`dbGet`/`dbAll` 保持调用简洁。 - **SQLite 性能 PRAGMA** — `configureDatabase()` 在 `app.listen` 内、migration 之前执行:`journal_mode=WAL`(读写不互斥)、`synchronous=NORMAL`、`busy_timeout=5000`、`cache_size=-20000`、`temp_store=MEMORY`、`mmap_size`。WAL 会生成 `database.sqlite-wal` / `-shm` 文件,属正常。`admin/import` 替换连接后会重新调用 `configureDatabase()`。 -- **数据目录可配置** — `JPAGE_DATA_DIR` 环境变量(可选,默认 `./data`)。docker-compose 已把 `./data` 挂载到 `/app/data`,通常无需设置。 +- **数据目录可配置** — `MAGPIE_DATA_DIR` 环境变量(可选,默认 `./data`)。docker-compose 已把 `./data` 挂载到 `/app/data`,通常无需设置。 - **时间统一存 UTC** — `now()` 返回 UTC `YYYY-MM-DD HH:MM:SS`,与 SQLite 的 `CURRENT_TIMESTAMP` / `datetime('now')` 一致。展示层负责转本地时区。不要再改回北京时区字符串。 - **Markdown 渲染缓存** — `RENDER_CACHE` 以 `${fileId}:${stored_name}:${updated_at}:...` 为 key 缓存渲染结果(LRU,上限 256)。覆盖上传/恢复版本会改 `updated_at`/`stored_name` 自动失效;删除文件调 `invalidateRenderCache(id)`。历史版本渲染传入 `{ ...file, stored_name: ver.stored_name }`,故 key 必须含 `stored_name`。 - **分类名称内存缓存** — `categoryNameCache`(id→name)在启动时 `reloadCategoryNameCache()` 加载,分类增/删/改名/import 后失效重建。`/api/files` 与搜索经 `getCategoryName(id)` 取名,不再每次扫 `categories` 表。 @@ -189,7 +189,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co - **鉴权模型** — `requireAuth` 是异步中间件,接受三种认证方式:(1) session cookie,(2) 旧 `MCP_TOKEN` 环境变量(向后兼容),(3) 用户级 API Token(`tokens` 表)。中间件设置 `req.userId` 和 `req.userRole` 供下游使用。`requireAdmin` 检查 `req.userRole === 'admin'`。`loadFileWithPrivacy` 强制文件所有权:admin 可访问一切,普通用户仅可访问自己的文件和公开文件。 - **角色系统** — `users.role` 列,值为 `admin` 或 `user`。admin 可管理用户、查看所有文件。普通用户只能操作自己的文件。`bootstrapAdmin()` 创建时显式设置 `role='admin'`。 - **开放注册** — `ALLOW_REGISTRATION=true` 时允许用户自助注册,默认关闭。注册端点 `POST /api/auth/register`,支持邮箱或用户名注册。配合 SMTP 配置实现邮箱验证。环境变量必须在 `.env`、`docker-compose.yml`、`server.js` 三处同步。 -- **API Token** — 每用户最多 10 个,格式 `jp_` + 32 位 base62。DB 存 SHA-256 哈希(鉴权用,不可逆)+ 前 8 位前缀 + AES-256-GCM 密文(`token_enc`,使明文可后续查看/复制,旧令牌为 NULL)。加密密钥来自环境变量 `TOKEN_ENCRYPTION_KEY`,未设置时自动在数据目录生成 `token-key.key` 文件(持久化)。鉴权链路仅用哈希,与密文相互独立。 +- **API Token** — 每用户最多 10 个,格式 `mg_` + 32 位 base62。DB 存 SHA-256 哈希(鉴权用,不可逆)+ 前 8 位前缀 + AES-256-GCM 密文(`token_enc`,使明文可后续查看/复制,旧令牌为 NULL)。加密密钥来自环境变量 `TOKEN_ENCRYPTION_KEY`,未设置时自动在数据目录生成 `token-key.key` 文件(持久化)。鉴权链路仅用哈希,与密文相互独立。 - **`MCP_TOKEN` 是可选的** — 未设置时仍可通过用户级 Token 访问 MCP。`mountMcpServer` 接受 `authenticateRequest` 函数验证 Token。 - **`uploaded_by` 从 `req.userId` 设置** — 文件归属隔离:admin 看全部文件,普通用户看自己的 + 公开的。`PUT`/`DELETE` 增加所有权检查(`checkFileOwnership`)。 - **Markdown 渲染增强** — marked + highlight.js(代码高亮)+ KaTeX(数学公式 `$...$` / `$$...$$`)+ Mermaid(图表,支持深色/浅色主题)。渲染代码在 `server.js` 的 `renderMarkdown` 函数。 @@ -280,7 +280,7 @@ docker-compose.yml # port 8858, ./data:/app/data volume .env.example # 环境变量模板 .mcp.json # Claude Code / Desktop MCP 客户端配置示例 docs/api.md # REST API 完整参考 -skills/jpage/ # Claude Code / Desktop 统一技能 +skills/magpie/ # Claude Code / Desktop 统一技能 SKILL.md public/ index.html # 两个