From 762233c9f320730bd1ccfe809fe2c6fe2a360bf5 Mon Sep 17 00:00:00 2001 From: Ambr0se Date: Sun, 15 Mar 2026 23:34:52 +0800 Subject: [PATCH 01/11] ci: Attempt to resolve Workflows and introduce pre-release mechanism --- .github/workflows/release.yml | 79 +++++++++++++++++++++++++++++++---- mcp/package.json | 5 +-- scripts/test-meting.js | 4 +- 3 files changed, 75 insertions(+), 13 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 12bf103..e81f656 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,6 +4,14 @@ on: push: branches: - main + - dev + +concurrency: + group: release-${{ github.ref_name }} + cancel-in-progress: true + +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true jobs: publish-npm: @@ -20,10 +28,35 @@ jobs: with: node-version: 24 registry-url: "https://registry.npmjs.org" - - run: node scripts/sync-mcp-core.mjs - run: npm ci - run: npm run verify - - run: npm publish --provenance --access public + - name: Prepare npm release channel + id: npm-release + shell: bash + run: | + if [[ "${GITHUB_REF_NAME}" == "dev" ]]; then + node -e "const fs=require('node:fs'); const packagePath='package.json'; const packageJson=JSON.parse(fs.readFileSync(packagePath, 'utf8')); const stableVersion=packageJson.version.split('-')[0]; packageJson.version=`${stableVersion}-dev.${process.env.GITHUB_RUN_NUMBER}`; fs.writeFileSync(packagePath, JSON.stringify(packageJson, null, 2) + '\n');" + echo "tag=prerelease" >> "$GITHUB_OUTPUT" + else + echo "tag=latest" >> "$GITHUB_OUTPUT" + fi + package_name=$(node -p "JSON.parse(require('fs').readFileSync('package.json', 'utf8')).name") + version=$(node -p "JSON.parse(require('fs').readFileSync('package.json', 'utf8')).version") + echo "package_name=$package_name" >> "$GITHUB_OUTPUT" + echo "version=$version" >> "$GITHUB_OUTPUT" + - name: Check npm version availability + id: npm-version-check + shell: bash + run: | + if npm view "${{ steps.npm-release.outputs.package_name }}@${{ steps.npm-release.outputs.version }}" version --registry https://registry.npmjs.org > /dev/null 2>&1; then + echo "should_publish=false" >> "$GITHUB_OUTPUT" + echo "Version ${{ steps.npm-release.outputs.version }} already exists on npm, skipping publish." + else + echo "should_publish=true" >> "$GITHUB_OUTPUT" + fi + - name: Publish npm package + if: steps.npm-version-check.outputs.should_publish == 'true' + run: npm publish --provenance --access public --tag "${{ steps.npm-release.outputs.tag }}" release-skill: runs-on: ubuntu-latest @@ -34,23 +67,53 @@ jobs: - uses: actions/setup-node@v4 with: node-version: 24 + - name: Prepare skill release channel + id: skill-release + shell: bash + run: | + if [[ "${GITHUB_REF_NAME}" == "dev" ]]; then + echo "release_tag=dev" >> "$GITHUB_OUTPUT" + echo "release_title=dev" >> "$GITHUB_OUTPUT" + echo "asset_name=meting-agent-skill-dev.zip" >> "$GITHUB_OUTPUT" + echo "is_prerelease=true" >> "$GITHUB_OUTPUT" + else + echo "release_tag=main" >> "$GITHUB_OUTPUT" + echo "release_title=main" >> "$GITHUB_OUTPUT" + echo "asset_name=meting-agent-skill-main.zip" >> "$GITHUB_OUTPUT" + echo "is_prerelease=false" >> "$GITHUB_OUTPUT" + fi - name: Build skill release bundle run: node scripts/build-skill-release.mjs - name: Smoke test built skill bundle run: | node dist/meting-agent-skill/scripts/meting-cli.mjs platforms - node dist/meting-agent-skill/scripts/meting-cli.mjs search --platform netease --keyword "我怀念的" --limit 1 + node dist/meting-agent-skill/scripts/meting-cli.mjs search --platform netease --keyword "Hello Adele" --limit 1 - name: Create zip archive + shell: bash run: | cd dist - zip -r "meting-agent-skill-${GITHUB_REF_NAME}.zip" meting-agent-skill + zip -r "${{ steps.skill-release.outputs.asset_name }}" meting-agent-skill - name: Publish GitHub release asset env: GH_TOKEN: ${{ github.token }} + shell: bash run: | - asset="dist/meting-agent-skill-${GITHUB_REF_NAME}.zip" - if gh release view "${GITHUB_REF_NAME}" > /dev/null 2>&1; then - gh release upload "${GITHUB_REF_NAME}" "$asset" --clobber + asset="dist/${{ steps.skill-release.outputs.asset_name }}" + release_tag="${{ steps.skill-release.outputs.release_tag }}" + release_title="${{ steps.skill-release.outputs.release_title }}" + is_prerelease="${{ steps.skill-release.outputs.is_prerelease }}" + + if gh release view "$release_tag" > /dev/null 2>&1; then + if [[ "$is_prerelease" == "true" ]]; then + gh release edit "$release_tag" --title "$release_title" --prerelease + else + gh release edit "$release_tag" --title "$release_title" --latest + fi + gh release upload "$release_tag" "$asset" --clobber else - gh release create "${GITHUB_REF_NAME}" "$asset" --title "${GITHUB_REF_NAME}" --generate-notes + if [[ "$is_prerelease" == "true" ]]; then + gh release create "$release_tag" "$asset" --title "$release_title" --prerelease --generate-notes + else + gh release create "$release_tag" "$asset" --title "$release_title" --generate-notes + fi fi diff --git a/mcp/package.json b/mcp/package.json index 0cec4fe..1b586a8 100644 --- a/mcp/package.json +++ b/mcp/package.json @@ -18,9 +18,8 @@ "scripts": { "sync:core": "node ../scripts/sync-mcp-core.mjs", "start": "npm run sync:core && node src/index.js", - "lint": "npm run sync:core && node --check src/index.js && node --check src/mcp-server.js && node --check src/meting.js && node --check src/providers/index.js && node --check ../test/test.js && node --check ../test/example.js", - "test": "npm run sync:core && node ../test/test.js", - "example": "npm run sync:core && node ../test/example.js", + "lint": "npm run sync:core && node --check src/index.js && node --check src/mcp-server.js && node --check src/meting.js && node --check src/providers/index.js && node --check ../scripts/test-meting.js", + "test": "npm run sync:core && node ../scripts/test-meting.js", "build": "npm run sync:core && npm pack --dry-run", "verify": "npm run lint && npm test && npm run build", "prepublishOnly": "npm run verify" diff --git a/scripts/test-meting.js b/scripts/test-meting.js index 60a71ab..8eaf746 100644 --- a/scripts/test-meting.js +++ b/scripts/test-meting.js @@ -53,7 +53,7 @@ async function RunPlatformTest(platform, testKeyword) { } } -async function runTests() { +async function RunTests() { console.log("=== Meting smoke test ===\n"); const platform = process.argv[2]; @@ -72,7 +72,7 @@ async function runTests() { console.log("\n=== Smoke test finished ==="); } -runTests().catch((error) => { +RunTests().catch((error) => { console.error("Unexpected test failure:", error); process.exitCode = 1; }); From d044f58ee00f948cb0199eea341b686ffb3361b6 Mon Sep 17 00:00:00 2001 From: Ambr0se Date: Sun, 15 Mar 2026 23:41:07 +0800 Subject: [PATCH 02/11] ci: Repair dev prerelease workflow and root test script paths --- .github/workflows/release.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e81f656..9acab00 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -35,7 +35,14 @@ jobs: shell: bash run: | if [[ "${GITHUB_REF_NAME}" == "dev" ]]; then - node -e "const fs=require('node:fs'); const packagePath='package.json'; const packageJson=JSON.parse(fs.readFileSync(packagePath, 'utf8')); const stableVersion=packageJson.version.split('-')[0]; packageJson.version=`${stableVersion}-dev.${process.env.GITHUB_RUN_NUMBER}`; fs.writeFileSync(packagePath, JSON.stringify(packageJson, null, 2) + '\n');" + node <<'NODE' + const fs = require("node:fs"); + const packagePath = "package.json"; + const packageJson = JSON.parse(fs.readFileSync(packagePath, "utf8")); + const stableVersion = packageJson.version.split("-")[0]; + packageJson.version = `${stableVersion}-dev.${process.env.GITHUB_RUN_NUMBER}`; + fs.writeFileSync(packagePath, `${JSON.stringify(packageJson, null, 2)}\n`); + NODE echo "tag=prerelease" >> "$GITHUB_OUTPUT" else echo "tag=latest" >> "$GITHUB_OUTPUT" From 9bbde19d4d541791e80fd7138d0b87d7c7ee2775 Mon Sep 17 00:00:00 2001 From: Ambr0se Date: Sun, 15 Mar 2026 23:46:41 +0800 Subject: [PATCH 03/11] ci: Correct release workflow quoting and root test script paths --- .github/workflows/release.yml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9acab00..a06bc98 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -35,14 +35,7 @@ jobs: shell: bash run: | if [[ "${GITHUB_REF_NAME}" == "dev" ]]; then - node <<'NODE' - const fs = require("node:fs"); - const packagePath = "package.json"; - const packageJson = JSON.parse(fs.readFileSync(packagePath, "utf8")); - const stableVersion = packageJson.version.split("-")[0]; - packageJson.version = `${stableVersion}-dev.${process.env.GITHUB_RUN_NUMBER}`; - fs.writeFileSync(packagePath, `${JSON.stringify(packageJson, null, 2)}\n`); - NODE + node -e 'const fs=require("node:fs"); const packagePath="package.json"; const packageJson=JSON.parse(fs.readFileSync(packagePath, "utf8")); const stableVersion=packageJson.version.split("-")[0]; packageJson.version=stableVersion + "-dev." + process.env.GITHUB_RUN_NUMBER; fs.writeFileSync(packagePath, JSON.stringify(packageJson, null, 2) + "\n");' echo "tag=prerelease" >> "$GITHUB_OUTPUT" else echo "tag=latest" >> "$GITHUB_OUTPUT" From d3308679a30d20d4375dfce6c35aa1e33d023000 Mon Sep 17 00:00:00 2001 From: Ambr0se Date: Sun, 15 Mar 2026 23:58:38 +0800 Subject: [PATCH 04/11] ci: Simplify release verification by removing smoke tests --- .github/workflows/release.yml | 4 ---- mcp/package.json | 5 ++--- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a06bc98..162d4a0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -84,10 +84,6 @@ jobs: fi - name: Build skill release bundle run: node scripts/build-skill-release.mjs - - name: Smoke test built skill bundle - run: | - node dist/meting-agent-skill/scripts/meting-cli.mjs platforms - node dist/meting-agent-skill/scripts/meting-cli.mjs search --platform netease --keyword "Hello Adele" --limit 1 - name: Create zip archive shell: bash run: | diff --git a/mcp/package.json b/mcp/package.json index 1b586a8..53adac0 100644 --- a/mcp/package.json +++ b/mcp/package.json @@ -18,10 +18,9 @@ "scripts": { "sync:core": "node ../scripts/sync-mcp-core.mjs", "start": "npm run sync:core && node src/index.js", - "lint": "npm run sync:core && node --check src/index.js && node --check src/mcp-server.js && node --check src/meting.js && node --check src/providers/index.js && node --check ../scripts/test-meting.js", - "test": "npm run sync:core && node ../scripts/test-meting.js", + "lint": "npm run sync:core && node --check src/index.js && node --check src/mcp-server.js && node --check src/meting.js && node --check src/providers/index.js", "build": "npm run sync:core && npm pack --dry-run", - "verify": "npm run lint && npm test && npm run build", + "verify": "npm run lint && npm run build", "prepublishOnly": "npm run verify" }, "keywords": [ From ddffd31b00a313c8a3a652151c9ec7185128ef3b Mon Sep 17 00:00:00 2001 From: Ambr0se Date: Mon, 16 Mar 2026 09:28:44 +0800 Subject: [PATCH 05/11] ci: Add shared versioning for npm and skill releases --- .github/workflows/release.yml | 94 +++++++++++++++++++++-------------- 1 file changed, 58 insertions(+), 36 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 162d4a0..36e0711 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,14 +7,58 @@ on: - dev concurrency: - group: release-${{ github.ref_name }} + group: release-${{ github.ref }} cancel-in-progress: true env: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true jobs: + versioning: + runs-on: ubuntu-latest + outputs: + packageName: ${{ steps.versioning.outputs.package_name }} + baseVersion: ${{ steps.versioning.outputs.base_version }} + fullVersion: ${{ steps.versioning.outputs.full_version }} + npmTag: ${{ steps.versioning.outputs.npm_tag }} + releaseTag: ${{ steps.versioning.outputs.release_tag }} + releaseTitle: ${{ steps.versioning.outputs.release_title }} + assetName: ${{ steps.versioning.outputs.asset_name }} + isPrerelease: ${{ steps.versioning.outputs.is_prerelease }} + steps: + - uses: actions/checkout@v4 + - name: Compute release version + id: versioning + shell: bash + run: | + package_name=$(node -p "JSON.parse(require('fs').readFileSync('mcp/package.json', 'utf8')).name") + base_version=$(node -p "JSON.parse(require('fs').readFileSync('mcp/package.json', 'utf8')).version.split('-')[0]") + + if [[ "${GITHUB_REF_NAME}" == "dev" ]]; then + full_version="${base_version}-dev.${GITHUB_RUN_NUMBER}.${GITHUB_RUN_ATTEMPT}" + npm_tag="prerelease" + is_prerelease="true" + else + full_version="${base_version}" + npm_tag="latest" + is_prerelease="false" + fi + + release_tag="v${full_version}" + release_title="Release ${release_tag}" + asset_name="meting-agent-skill-${full_version}.zip" + + echo "package_name=$package_name" >> "$GITHUB_OUTPUT" + echo "base_version=$base_version" >> "$GITHUB_OUTPUT" + echo "full_version=$full_version" >> "$GITHUB_OUTPUT" + echo "npm_tag=$npm_tag" >> "$GITHUB_OUTPUT" + echo "release_tag=$release_tag" >> "$GITHUB_OUTPUT" + echo "release_title=$release_title" >> "$GITHUB_OUTPUT" + echo "asset_name=$asset_name" >> "$GITHUB_OUTPUT" + echo "is_prerelease=$is_prerelease" >> "$GITHUB_OUTPUT" + publish-npm: + needs: versioning runs-on: ubuntu-latest permissions: contents: read @@ -30,35 +74,28 @@ jobs: registry-url: "https://registry.npmjs.org" - run: npm ci - run: npm run verify - - name: Prepare npm release channel - id: npm-release + - name: Set package version for publish shell: bash run: | - if [[ "${GITHUB_REF_NAME}" == "dev" ]]; then - node -e 'const fs=require("node:fs"); const packagePath="package.json"; const packageJson=JSON.parse(fs.readFileSync(packagePath, "utf8")); const stableVersion=packageJson.version.split("-")[0]; packageJson.version=stableVersion + "-dev." + process.env.GITHUB_RUN_NUMBER; fs.writeFileSync(packagePath, JSON.stringify(packageJson, null, 2) + "\n");' - echo "tag=prerelease" >> "$GITHUB_OUTPUT" - else - echo "tag=latest" >> "$GITHUB_OUTPUT" - fi - package_name=$(node -p "JSON.parse(require('fs').readFileSync('package.json', 'utf8')).name") - version=$(node -p "JSON.parse(require('fs').readFileSync('package.json', 'utf8')).version") - echo "package_name=$package_name" >> "$GITHUB_OUTPUT" - echo "version=$version" >> "$GITHUB_OUTPUT" + node -e 'const fs=require("node:fs"); const packagePath="package.json"; const packageJson=JSON.parse(fs.readFileSync(packagePath, "utf8")); packageJson.version=process.env.RELEASE_VERSION; fs.writeFileSync(packagePath, JSON.stringify(packageJson, null, 2) + "\n");' + env: + RELEASE_VERSION: ${{ needs.versioning.outputs.fullVersion }} - name: Check npm version availability id: npm-version-check shell: bash run: | - if npm view "${{ steps.npm-release.outputs.package_name }}@${{ steps.npm-release.outputs.version }}" version --registry https://registry.npmjs.org > /dev/null 2>&1; then + if npm view "${{ needs.versioning.outputs.packageName }}@${{ needs.versioning.outputs.fullVersion }}" version --registry https://registry.npmjs.org > /dev/null 2>&1; then echo "should_publish=false" >> "$GITHUB_OUTPUT" - echo "Version ${{ steps.npm-release.outputs.version }} already exists on npm, skipping publish." + echo "Version ${{ needs.versioning.outputs.fullVersion }} already exists on npm, skipping publish." else echo "should_publish=true" >> "$GITHUB_OUTPUT" fi - name: Publish npm package if: steps.npm-version-check.outputs.should_publish == 'true' - run: npm publish --provenance --access public --tag "${{ steps.npm-release.outputs.tag }}" + run: npm publish --provenance --access public --tag "${{ needs.versioning.outputs.npmTag }}" release-skill: + needs: versioning runs-on: ubuntu-latest permissions: contents: write @@ -67,37 +104,22 @@ jobs: - uses: actions/setup-node@v4 with: node-version: 24 - - name: Prepare skill release channel - id: skill-release - shell: bash - run: | - if [[ "${GITHUB_REF_NAME}" == "dev" ]]; then - echo "release_tag=dev" >> "$GITHUB_OUTPUT" - echo "release_title=dev" >> "$GITHUB_OUTPUT" - echo "asset_name=meting-agent-skill-dev.zip" >> "$GITHUB_OUTPUT" - echo "is_prerelease=true" >> "$GITHUB_OUTPUT" - else - echo "release_tag=main" >> "$GITHUB_OUTPUT" - echo "release_title=main" >> "$GITHUB_OUTPUT" - echo "asset_name=meting-agent-skill-main.zip" >> "$GITHUB_OUTPUT" - echo "is_prerelease=false" >> "$GITHUB_OUTPUT" - fi - name: Build skill release bundle run: node scripts/build-skill-release.mjs - name: Create zip archive shell: bash run: | cd dist - zip -r "${{ steps.skill-release.outputs.asset_name }}" meting-agent-skill + zip -r "${{ needs.versioning.outputs.assetName }}" meting-agent-skill - name: Publish GitHub release asset env: GH_TOKEN: ${{ github.token }} shell: bash run: | - asset="dist/${{ steps.skill-release.outputs.asset_name }}" - release_tag="${{ steps.skill-release.outputs.release_tag }}" - release_title="${{ steps.skill-release.outputs.release_title }}" - is_prerelease="${{ steps.skill-release.outputs.is_prerelease }}" + asset="dist/${{ needs.versioning.outputs.assetName }}" + release_tag="${{ needs.versioning.outputs.releaseTag }}" + release_title="${{ needs.versioning.outputs.releaseTitle }}" + is_prerelease="${{ needs.versioning.outputs.isPrerelease }}" if gh release view "$release_tag" > /dev/null 2>&1; then if [[ "$is_prerelease" == "true" ]]; then From 92deee9e5af72caca358e6c47f6042dc3e4a0bb7 Mon Sep 17 00:00:00 2001 From: Ambr0se Date: Mon, 16 Mar 2026 10:29:21 +0800 Subject: [PATCH 06/11] refactor: Rename shared meting sync targets and update docs --- .gitignore | 7 +-- .prettierignore | 7 +-- README.md | 59 +++++++------------ docs/CONTRIBUTING.md | 56 ++++++++++++++++++ mcp/README.md | 30 ++++++---- mcp/package.json | 2 +- mcp/src/mcp-server.js | 2 +- scripts/build-skill-release.mjs | 10 ++-- scripts/sync-mcp-core.mjs | 8 +-- scripts/test-meting.js | 2 +- shared/{core-src => meting}/meting.js | 0 .../{core-src => meting}/providers/baidu.js | 0 shared/{core-src => meting}/providers/base.js | 0 .../{core-src => meting}/providers/index.js | 0 .../{core-src => meting}/providers/kugou.js | 0 shared/{core-src => meting}/providers/kuwo.js | 0 .../{core-src => meting}/providers/netease.js | 0 .../{core-src => meting}/providers/tencent.js | 0 skills/meting-agent/README.md | 22 +++++++ skills/meting-agent/SKILL.md | 6 +- skills/meting-agent/scripts/meting-cli.mjs | 2 +- 21 files changed, 137 insertions(+), 76 deletions(-) create mode 100644 docs/CONTRIBUTING.md rename shared/{core-src => meting}/meting.js (100%) rename shared/{core-src => meting}/providers/baidu.js (100%) rename shared/{core-src => meting}/providers/base.js (100%) rename shared/{core-src => meting}/providers/index.js (100%) rename shared/{core-src => meting}/providers/kugou.js (100%) rename shared/{core-src => meting}/providers/kuwo.js (100%) rename shared/{core-src => meting}/providers/netease.js (100%) rename shared/{core-src => meting}/providers/tencent.js (100%) create mode 100644 skills/meting-agent/README.md diff --git a/.gitignore b/.gitignore index 5bafc95..a594fcd 100644 --- a/.gitignore +++ b/.gitignore @@ -4,8 +4,5 @@ node_modules/ mcp/.inspect/ mcp/.probe/ mcp/node_modules/ -mcp/src/meting.js -mcp/src/providers/ -skills/meting-agent/node_modules/ -skills/meting-agent/scripts/meting.js -skills/meting-agent/scripts/providers/ +mcp/src/meting/meting.js +mcp/src/meting/providers/ diff --git a/.prettierignore b/.prettierignore index 05e3923..23edf1b 100644 --- a/.prettierignore +++ b/.prettierignore @@ -5,8 +5,5 @@ node_modules/ mcp/.inspect/ mcp/.probe/ mcp/node_modules/ -mcp/src/meting.js -mcp/src/providers/ -skills/meting-agent/node_modules/ -skills/meting-agent/scripts/meting.js -skills/meting-agent/scripts/providers/ +mcp/src/meting/meting.js +mcp/src/meting/providers/ diff --git a/README.md b/README.md index 4f1e468..d176cc2 100644 --- a/README.md +++ b/README.md @@ -1,50 +1,35 @@ # Meting-Agent -`Meting-Agent` 维护两套运行时独立的交付物: +`Meting-Agent` 是基于 **[metowolf/Meting](https://github.com/metowolf/Meting)** 构建的多平台音乐能力封装,当前提供两类交付物: -- `mcp/`:可发布的 Node.js MCP Server,对外名称统一为 `Meting Agent`。 -- `skills/meting-agent/`:面向 Codex 的 skill 源文件,最终以 GitHub Release 资产分发。 +- [@eldment/meting-agent](https://www.npmjs.com/package/@eldment/meting-agent):发布到 npm 的 Node.js **MCP Server** +- [skills/meting-agent](https://github.com/ELDment/Meting-Agent/releases):可下载的通用 **Skills** 资产 -## 目录说明 +支持 [网易云音乐](https://music.163.com/)(`netease`)、[腾讯音乐](https://y.qq.com/)(`tencent`)、[酷狗音乐](https://www.kugou.com/)(`kugou`)、[千千音乐](https://music.taihe.com/)(`baidu`)、[酷我音乐](https://www.kuwo.cn/)(`kuwo`)等平台,提供搜索、歌曲、专辑、歌手、歌单、播放链接、歌词、封面等能力 -- `shared/core-src/`:唯一的核心源码来源,包含 `meting.js` 和全部 providers。 -- `mcp/`:MCP Server 运行时副本与发布配置。 -- `skills/meting-agent/`:skill 元数据与静态脚本源文件。 -- `scripts/sync-mcp-core.mjs`:把共享核心生成到 `mcp/src/`。 -- `scripts/build-skill-release.mjs`:构建可下载的 skill release bundle。 +## 能力 -## 常用命令 +- `search`:按关键字搜索歌曲、专辑、歌手或平台特定资源 +- `song`:按歌曲 ID 获取歌曲详情 +- `album`:按专辑 ID 获取专辑详情 +- `artist`:按歌手 ID 获取歌手作品列表 +- `playlist`:按歌单 ID 获取歌单详情 +- `url`:按歌曲 ID 获取可播放链接 +- `lyric`:按歌曲 ID 获取歌词内容 +- `pic`:按资源 ID 获取封面或图片链接 -同步共享核心到 MCP: +支持平台:`netease`、`tencent`、`kugou`、`baidu`、`kuwo` -```powershell -node scripts/sync-mcp-core.mjs -``` +## 文档 -构建 skill release bundle: +- MCP Server 安装方式、客户端配置示例、MCP 工具映射、Cookie 配置说明见 [mcp/README.md](./mcp/README.md) +- Skills 交付方式、目录组成和 CLI 用法见 [skills/meting-agent/README.md](./skills/meting-agent/README.md) +- 贡献流程与同步机制(编译时报错)说明见 [docs/CONTRIBUTING.md](./docs/CONTRIBUTING.md) -```powershell -node scripts/build-skill-release.mjs -``` +## 致谢 -验证 MCP 子项目: +感谢 **[metowolf/Meting](https://github.com/metowolf/Meting)** 提供跨平台统一接口与各平台 provider 实现 -```powershell -cd mcp -npm install -npm run verify -``` +--- -本地验证 skill release bundle: - -```powershell -node scripts/build-skill-release.mjs -node dist/meting-agent-skill/scripts/meting-cli.mjs platforms -node dist/meting-agent-skill/scripts/meting-cli.mjs search --platform netease --keyword "我怀念的" --limit 3 -``` - -## 维护原则 - -- 只在 `shared/core-src/` 修改核心实现。 -- `mcp/src/meting.js` 和 `mcp/src/providers/` 由同步脚本生成,不直接维护。 -- skill 的运行时副本只在 `dist/meting-agent-skill/` 构建时生成,用户从 GitHub Release 下载,不克隆源码仓库。 +关键词: MCP Server | Model Context Protocol | Music API | Node.js MCP | AI Tool Integration | AI Skills | Reusable Skills | NetEase Cloud Music | Tencent QQ Music | KuGou Music | Baidu Music | Taihe Music | Qianqian Music | Kuwo Music | Lyrics API | Playlist API diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md new file mode 100644 index 0000000..258259f --- /dev/null +++ b/docs/CONTRIBUTING.md @@ -0,0 +1,56 @@ +# CONTRIBUTING + +本仓库采用“共享源码 + 生成副本”的维护方式。提交修改前,请先理解同步关系,否则很容易改到错误位置 + +## 为什么刚克隆后不能直接编译 + +仓库默认只保留共享源码,不提交 skill release 产物,也不把 `shared/meting/` 的全部运行时副本长期维护在每个目标目录中 + +直接克隆后会遇到两个现实限制: + +- skill 的可分发产物位于 `dist/meting-agent-skill/scripts/meting/`,这个目录需要运行构建脚本后才会生成 +- `mcp/` 的构建虽然会在其 npm scripts 中自动执行 `sync:core`,但如果你跳过同步前置步骤,直接按生成文件视角排查问题,很容易得到错误结论 + +简化理解就是:这个仓库不是“每个交付物目录都自带完整、静态、可直接维护的源码副本”,而是先维护 `shared/meting/`,再生成给 `mcp/src/meting/` 和 skill 使用的副本 + +## 推荐修改流程 + +1. 先在 `shared/meting/` 修改核心逻辑 +2. 运行同步脚本生成 `mcp` 副本 +3. 运行构建脚本生成 skill release bundle +4. 分别验证 `mcp` 和 skill 产物 + +## 常用命令 + +同步共享核心到 `mcp/`: + +```powershell +node scripts/sync-mcp-core.mjs +``` + +构建 skill release bundle: + +```powershell +node scripts/build-skill-release.mjs +``` + +验证根目录文档格式: + +```powershell +npm run format:check +``` + +验证 `mcp` 子项目: + +```powershell +cd mcp +npm install +npm run verify +``` + +## 提交前检查 + +- 核心改动是否发生在 `shared/meting/` +- 是否重新生成了需要提交的副本或产物 +- 是否完成 `mcp` 验证 +- 如果改了文档,是否通过 `npm run format:check` diff --git a/mcp/README.md b/mcp/README.md index 1ea1239..15f2e83 100644 --- a/mcp/README.md +++ b/mcp/README.md @@ -1,18 +1,18 @@ -# Meting Agent +# Meting-Agent `Meting-Agent` 是基于 **[metowolf/Meting](https://github.com/metowolf/Meting)** 构建的 MCP Server,支持 [网易云音乐](https://music.163.com/)(`netease`)、[腾讯音乐](https://y.qq.com/)(`tencent`)、[酷狗音乐](https://www.kugou.com/)(`kugou`)、[千千音乐](https://music.taihe.com/)(`baidu`)、[酷我音乐](https://www.kuwo.cn/)(`kuwo`) 等音乐平台,提供搜索、歌曲、专辑、歌手、歌单、播放链接、歌词、封面等能力 ## MCP 工具 -- `platforms`:列出支持的平台和平台代号。 -- `search`:按关键字搜索歌曲、专辑、歌手或平台特定资源。 -- `song`:按歌曲 ID 获取详情。 -- `album`:按专辑 ID 获取详情。 -- `artist`:按歌手 ID 获取作品。 -- `playlist`:按歌单 ID 获取详情。 -- `url`:按歌曲 ID 获取播放地址。 -- `lyric`:按歌曲 ID 获取歌词。 -- `pic`:按资源 ID 获取封面地址。 +- `platforms`:列出支持的平台和平台代号 +- `search`:按关键字搜索歌曲、专辑、歌手或平台特定资源 +- `song`:按歌曲 ID 获取详情 +- `album`:按专辑 ID 获取详情 +- `artist`:按歌手 ID 获取作品 +- `playlist`:按歌单 ID 获取详情 +- `url`:按歌曲 ID 获取播放地址 +- `lyric`:按歌曲 ID 获取歌词 +- `pic`:按资源 ID 获取封面地址 ## 发布包名 @@ -23,7 +23,10 @@ Claude 配置示例: "mcpServers": { "meting": { "command": "npx", - "args": ["-y", "@eldment/meting-agent@latest"], + "args": [ + "-y", + "@eldment/meting-agent@latest" + ], "env": { "METING_NETEASE_COOKIE": "__csrf=...; MUSIC_U=...; NMTID=...; __remember_me=true;", "METING_TENCENT_COOKIE": "uin=...; qm_keyst=...; qqmusic_key=...;", @@ -55,7 +58,6 @@ env = { METING_KUWO_COOKIE = "...", } tool_timeout_sec = 60 -disabled = false ``` ## Cookie 优先级 @@ -73,4 +75,6 @@ disabled = false - `METING_KUGOU_COOKIE` - `METING_BAIDU_COOKIE` - `METING_KUWO_COOKIE` -- `METING_COOKIE` +- `METING_COOKIE`(通用) + +如果只需要给某一个平台带 cookie,优先使用对应的平台变量;如果想统一兜底,可以只设置 `METING_COOKIE` diff --git a/mcp/package.json b/mcp/package.json index 53adac0..be03550 100644 --- a/mcp/package.json +++ b/mcp/package.json @@ -18,7 +18,7 @@ "scripts": { "sync:core": "node ../scripts/sync-mcp-core.mjs", "start": "npm run sync:core && node src/index.js", - "lint": "npm run sync:core && node --check src/index.js && node --check src/mcp-server.js && node --check src/meting.js && node --check src/providers/index.js", + "lint": "npm run sync:core && node --check src/index.js && node --check src/mcp-server.js && node --check src/meting/meting.js && node --check src/meting/providers/index.js", "build": "npm run sync:core && npm pack --dry-run", "verify": "npm run lint && npm run build", "prepublishOnly": "npm run verify" diff --git a/mcp/src/mcp-server.js b/mcp/src/mcp-server.js index 49cfef9..3fcb674 100644 --- a/mcp/src/mcp-server.js +++ b/mcp/src/mcp-server.js @@ -1,7 +1,7 @@ import { createRequire } from "module"; import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import * as z from "zod/v4"; -import Meting from "./meting.js"; +import Meting from "./meting/meting.js"; const require = createRequire(import.meta.url); const { name: packageName, version } = require("../package.json"); diff --git a/scripts/build-skill-release.mjs b/scripts/build-skill-release.mjs index 390f413..39bf25e 100644 --- a/scripts/build-skill-release.mjs +++ b/scripts/build-skill-release.mjs @@ -6,7 +6,7 @@ import { fileURLToPath } from "node:url"; const generatedBanner = [ "/**", - " * Generated from shared/core-src by scripts/build-skill-release.mjs.", + " * Generated from shared/meting by scripts/build-skill-release.mjs.", " * Do not edit this copy directly.", " */", "", @@ -22,10 +22,10 @@ const staticFiles = Object.freeze([ const scriptPath = fileURLToPath(import.meta.url); const scriptDirectory = dirname(scriptPath); const repositoryRoot = resolve(scriptDirectory, ".."); -const sharedRoot = resolve(repositoryRoot, "shared", "core-src"); +const sharedRoot = resolve(repositoryRoot, "shared", "meting"); const skillSourceRoot = resolve(repositoryRoot, "skills", "meting-agent"); const outputRoot = resolve(repositoryRoot, "dist", "meting-agent-skill"); -const outputCoreRoot = resolve(outputRoot, "scripts", "core"); +const outputMetingRoot = resolve(outputRoot, "scripts", "meting"); async function PathExists(path) { try { @@ -71,7 +71,7 @@ async function CopyStaticFile(relativePath) { async function CopyGeneratedCoreFile(relativePath) { const sourcePath = resolve(sharedRoot, relativePath); - const targetPath = resolve(outputCoreRoot, relativePath); + const targetPath = resolve(outputMetingRoot, relativePath); const sourceContent = NormalizeContent(await readFile(sourcePath, "utf8")); const nextContent = `${generatedBanner}${sourceContent}`; @@ -84,7 +84,7 @@ async function CopyGeneratedCoreFile(relativePath) { async function Main() { if (!(await PathExists(sharedRoot))) { - throw new Error("shared/core-src not found."); + throw new Error("shared/meting not found."); } if (!(await PathExists(skillSourceRoot))) { diff --git a/scripts/sync-mcp-core.mjs b/scripts/sync-mcp-core.mjs index a3960e3..37c6bf7 100644 --- a/scripts/sync-mcp-core.mjs +++ b/scripts/sync-mcp-core.mjs @@ -6,7 +6,7 @@ import { fileURLToPath } from "node:url"; const generatedBanner = [ "/**", - " * Generated from shared/core-src by scripts/sync-mcp-core.mjs.", + " * Generated from shared/meting by scripts/sync-mcp-core.mjs.", " * Do not edit this copy directly.", " */", "", @@ -15,8 +15,8 @@ const generatedBanner = [ const scriptPath = fileURLToPath(import.meta.url); const scriptDirectory = dirname(scriptPath); const repositoryRoot = resolve(scriptDirectory, ".."); -const sharedRoot = resolve(repositoryRoot, "shared", "core-src"); -const targetRoot = resolve(repositoryRoot, "mcp", "src"); +const sharedRoot = resolve(repositoryRoot, "shared", "meting"); +const targetRoot = resolve(repositoryRoot, "mcp", "src", "meting"); async function PathExists(path) { try { @@ -71,7 +71,7 @@ async function WriteGeneratedCopy(sourceRoot, destinationRoot, relativePath) { async function Main() { if (!(await PathExists(sharedRoot))) { - process.stdout.write("[sync-mcp] shared/core-src not found, skipping sync.\n"); + process.stdout.write("[sync-mcp] shared/meting not found, skipping sync.\n"); return; } diff --git a/scripts/test-meting.js b/scripts/test-meting.js index 8eaf746..e86ecea 100644 --- a/scripts/test-meting.js +++ b/scripts/test-meting.js @@ -1,4 +1,4 @@ -import Meting from "../mcp/src/meting.js"; +import Meting from "../shared/meting/meting.js"; const supportedPlatforms = ["netease", "tencent", "kugou", "baidu", "kuwo"]; diff --git a/shared/core-src/meting.js b/shared/meting/meting.js similarity index 100% rename from shared/core-src/meting.js rename to shared/meting/meting.js diff --git a/shared/core-src/providers/baidu.js b/shared/meting/providers/baidu.js similarity index 100% rename from shared/core-src/providers/baidu.js rename to shared/meting/providers/baidu.js diff --git a/shared/core-src/providers/base.js b/shared/meting/providers/base.js similarity index 100% rename from shared/core-src/providers/base.js rename to shared/meting/providers/base.js diff --git a/shared/core-src/providers/index.js b/shared/meting/providers/index.js similarity index 100% rename from shared/core-src/providers/index.js rename to shared/meting/providers/index.js diff --git a/shared/core-src/providers/kugou.js b/shared/meting/providers/kugou.js similarity index 100% rename from shared/core-src/providers/kugou.js rename to shared/meting/providers/kugou.js diff --git a/shared/core-src/providers/kuwo.js b/shared/meting/providers/kuwo.js similarity index 100% rename from shared/core-src/providers/kuwo.js rename to shared/meting/providers/kuwo.js diff --git a/shared/core-src/providers/netease.js b/shared/meting/providers/netease.js similarity index 100% rename from shared/core-src/providers/netease.js rename to shared/meting/providers/netease.js diff --git a/shared/core-src/providers/tencent.js b/shared/meting/providers/tencent.js similarity index 100% rename from shared/core-src/providers/tencent.js rename to shared/meting/providers/tencent.js diff --git a/skills/meting-agent/README.md b/skills/meting-agent/README.md new file mode 100644 index 0000000..d840810 --- /dev/null +++ b/skills/meting-agent/README.md @@ -0,0 +1,22 @@ +# Meting-Agent + +`Meting-Agent` 是基于 **[metowolf/Meting](https://github.com/metowolf/Meting)** 构建的 Skills 封装,支持 [网易云音乐](https://music.163.com/)(`netease`)、[腾讯音乐](https://y.qq.com/)(`tencent`)、[酷狗音乐](https://www.kugou.com/)(`kugou`)、[千千音乐](https://music.taihe.com/)(`baidu`)、[酷我音乐](https://www.kuwo.cn/)(`kuwo`) 等音乐平台,提供搜索、歌曲、专辑、歌手、歌单、播放链接、歌词、封面等能力 + +skill 不通过 npm 发布,下载入口是 [GitHub Releases](https://github.com/ELDment/Meting-Agent/releases) + +## Cookie 规则 + +- 优先使用 `METING__COOKIE` +- 其次使用 `METING_COOKIE` +- 最后才使用命令参数 `--cookie` + +支持的平台环境变量: + +- `METING_NETEASE_COOKIE` +- `METING_TENCENT_COOKIE` +- `METING_KUGOU_COOKIE` +- `METING_BAIDU_COOKIE` +- `METING_KUWO_COOKIE` +- `METING_COOKIE`(通用) + +如果只需要给某一个平台带 cookie,优先使用对应的平台变量;如果想统一兜底,可以只设置 `METING_COOKIE` diff --git a/skills/meting-agent/SKILL.md b/skills/meting-agent/SKILL.md index 87bbbe7..2912c7e 100644 --- a/skills/meting-agent/SKILL.md +++ b/skills/meting-agent/SKILL.md @@ -7,10 +7,10 @@ description: Use when Codex needs direct music lookup capabilities through bundl ## Use The Bundled Implementation -- In the release bundle, treat `./scripts/core` as the implementation root. +- In the release bundle, treat `./scripts/meting` as the implementation root. - Use `./scripts/meting-cli.mjs` for normal lookup tasks. - Do not use `../../mcp` as a runtime dependency for this skill. -- In the repository source, `../../shared/core-src` is the source of truth and `../../scripts/build-skill-release.mjs` builds the downloadable bundle. +- Downloaded bundles are self-contained and should not rely on repository-relative paths. ## Core Workflow @@ -40,4 +40,4 @@ description: Use when Codex needs direct music lookup capabilities through bundl ## Sync Rules - Runtime release bundles stay independent, but repository maintenance uses one shared source. -- Update `shared/core-src` first, then run `node scripts/build-skill-release.mjs` from the repository root. +- In the source repository, update `shared/meting/` first, then run `node scripts/build-skill-release.mjs` from the repository root. diff --git a/skills/meting-agent/scripts/meting-cli.mjs b/skills/meting-agent/scripts/meting-cli.mjs index 85424a1..e9f3d5a 100644 --- a/skills/meting-agent/scripts/meting-cli.mjs +++ b/skills/meting-agent/scripts/meting-cli.mjs @@ -1,6 +1,6 @@ #!/usr/bin/env node -import Meting from "./core/meting.js"; +import Meting from "./meting/meting.js"; const platformCatalog = Object.freeze([ { name: "NetEase Cloud Music", code: "netease" }, From 8d26b7b043dbe59dba2f4eaa08bb5bb11cd2dfc4 Mon Sep 17 00:00:00 2001 From: Ambr0se Date: Mon, 16 Mar 2026 10:41:15 +0800 Subject: [PATCH 07/11] docs: Streamline documentation --- README.md | 12 +++++------- mcp/README.md | 6 +++++- skills/meting-agent/README.md | 4 +++- skills/meting-agent/SKILL.md | 6 ------ 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index d176cc2..89fc33c 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,8 @@ `Meting-Agent` 是基于 **[metowolf/Meting](https://github.com/metowolf/Meting)** 构建的多平台音乐能力封装,当前提供两类交付物: -- [@eldment/meting-agent](https://www.npmjs.com/package/@eldment/meting-agent):发布到 npm 的 Node.js **MCP Server** -- [skills/meting-agent](https://github.com/ELDment/Meting-Agent/releases):可下载的通用 **Skills** 资产 - -支持 [网易云音乐](https://music.163.com/)(`netease`)、[腾讯音乐](https://y.qq.com/)(`tencent`)、[酷狗音乐](https://www.kugou.com/)(`kugou`)、[千千音乐](https://music.taihe.com/)(`baidu`)、[酷我音乐](https://www.kuwo.cn/)(`kuwo`)等平台,提供搜索、歌曲、专辑、歌手、歌单、播放链接、歌词、封面等能力 +- [@eldment/meting-agent](https://www.npmjs.com/package/@eldment/meting-agent) —— 发布到 npm 的 Node.js **MCP Server** +- [skills/meting-agent](https://github.com/ELDment/Meting-Agent/releases) —— 可下载的通用 **Skills** 资产 ## 能力 @@ -18,12 +16,12 @@ - `lyric`:按歌曲 ID 获取歌词内容 - `pic`:按资源 ID 获取封面或图片链接 -支持平台:`netease`、`tencent`、`kugou`、`baidu`、`kuwo` +支持平台:[网易云音乐](https://music.163.com/)(`netease`)、[腾讯音乐](https://y.qq.com/)(`tencent`)、[酷狗音乐](https://www.kugou.com/)(`kugou`)、[千千音乐](https://music.taihe.com/)(`baidu`)、[酷我音乐](https://www.kuwo.cn/)(`kuwo`) ## 文档 -- MCP Server 安装方式、客户端配置示例、MCP 工具映射、Cookie 配置说明见 [mcp/README.md](./mcp/README.md) -- Skills 交付方式、目录组成和 CLI 用法见 [skills/meting-agent/README.md](./skills/meting-agent/README.md) +- MCP Server 配置说明见 [mcp/README.md](./mcp/README.md) +- Skills 配置说明见 [skills/meting-agent/README.md](./skills/meting-agent/README.md) - 贡献流程与同步机制(编译时报错)说明见 [docs/CONTRIBUTING.md](./docs/CONTRIBUTING.md) ## 致谢 diff --git a/mcp/README.md b/mcp/README.md index 15f2e83..ed1b36d 100644 --- a/mcp/README.md +++ b/mcp/README.md @@ -2,6 +2,10 @@ `Meting-Agent` 是基于 **[metowolf/Meting](https://github.com/metowolf/Meting)** 构建的 MCP Server,支持 [网易云音乐](https://music.163.com/)(`netease`)、[腾讯音乐](https://y.qq.com/)(`tencent`)、[酷狗音乐](https://www.kugou.com/)(`kugou`)、[千千音乐](https://music.taihe.com/)(`baidu`)、[酷我音乐](https://www.kuwo.cn/)(`kuwo`) 等音乐平台,提供搜索、歌曲、专辑、歌手、歌单、播放链接、歌词、封面等能力 +## 下载 + +通过 npm 发布,下载入口是 [@eldment/meting-agent](https://www.npmjs.com/package/@eldment/meting-agent) + ## MCP 工具 - `platforms`:列出支持的平台和平台代号 @@ -14,7 +18,7 @@ - `lyric`:按歌曲 ID 获取歌词 - `pic`:按资源 ID 获取封面地址 -## 发布包名 +## MCP 接入 Claude 配置示例: diff --git a/skills/meting-agent/README.md b/skills/meting-agent/README.md index d840810..f818d31 100644 --- a/skills/meting-agent/README.md +++ b/skills/meting-agent/README.md @@ -1,6 +1,8 @@ # Meting-Agent -`Meting-Agent` 是基于 **[metowolf/Meting](https://github.com/metowolf/Meting)** 构建的 Skills 封装,支持 [网易云音乐](https://music.163.com/)(`netease`)、[腾讯音乐](https://y.qq.com/)(`tencent`)、[酷狗音乐](https://www.kugou.com/)(`kugou`)、[千千音乐](https://music.taihe.com/)(`baidu`)、[酷我音乐](https://www.kuwo.cn/)(`kuwo`) 等音乐平台,提供搜索、歌曲、专辑、歌手、歌单、播放链接、歌词、封面等能力 +`Meting-Agent` 是基于 **[metowolf/Meting](https://github.com/metowolf/Meting)** 构建的 Skills 资产,支持 [网易云音乐](https://music.163.com/)(`netease`)、[腾讯音乐](https://y.qq.com/)(`tencent`)、[酷狗音乐](https://www.kugou.com/)(`kugou`)、[千千音乐](https://music.taihe.com/)(`baidu`)、[酷我音乐](https://www.kuwo.cn/)(`kuwo`) 等音乐平台,提供搜索、歌曲、专辑、歌手、歌单、播放链接、歌词、封面等能力 + +## 下载 skill 不通过 npm 发布,下载入口是 [GitHub Releases](https://github.com/ELDment/Meting-Agent/releases) diff --git a/skills/meting-agent/SKILL.md b/skills/meting-agent/SKILL.md index 2912c7e..2bc48c9 100644 --- a/skills/meting-agent/SKILL.md +++ b/skills/meting-agent/SKILL.md @@ -9,7 +9,6 @@ description: Use when Codex needs direct music lookup capabilities through bundl - In the release bundle, treat `./scripts/meting` as the implementation root. - Use `./scripts/meting-cli.mjs` for normal lookup tasks. -- Do not use `../../mcp` as a runtime dependency for this skill. - Downloaded bundles are self-contained and should not rely on repository-relative paths. ## Core Workflow @@ -36,8 +35,3 @@ description: Use when Codex needs direct music lookup capabilities through bundl - Prefer `METING__COOKIE` for per-platform cookies. - Use `METING_COOKIE` as a shared fallback. - Use `--cookie ` only when the call needs an explicit override. - -## Sync Rules - -- Runtime release bundles stay independent, but repository maintenance uses one shared source. -- In the source repository, update `shared/meting/` first, then run `node scripts/build-skill-release.mjs` from the repository root. From bec59e17b8e7d0da90a4ab248443657cd658137c Mon Sep 17 00:00:00 2001 From: Ambr0se Date: Mon, 16 Mar 2026 11:18:15 +0800 Subject: [PATCH 08/11] ci: Automate git-based releases for main and dev --- .github/workflows/release.yml | 246 ++++++++++++++++++++++------------ .prettierignore | 1 + README.md | 6 +- skills/meting-agent/README.md | 2 +- 4 files changed, 167 insertions(+), 88 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 36e0711..c61b44b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,8 +3,8 @@ name: release on: push: branches: - - main - - dev + - main + - dev concurrency: group: release-${{ github.ref }} @@ -17,6 +17,7 @@ jobs: versioning: runs-on: ubuntu-latest outputs: + shouldRelease: ${{ steps.versioning.outputs.should_release }} packageName: ${{ steps.versioning.outputs.package_name }} baseVersion: ${{ steps.versioning.outputs.base_version }} fullVersion: ${{ steps.versioning.outputs.full_version }} @@ -25,40 +26,110 @@ jobs: releaseTitle: ${{ steps.versioning.outputs.release_title }} assetName: ${{ steps.versioning.outputs.asset_name }} isPrerelease: ${{ steps.versioning.outputs.is_prerelease }} + notesStartTag: ${{ steps.versioning.outputs.notes_start_tag }} steps: - - uses: actions/checkout@v4 - - name: Compute release version - id: versioning - shell: bash - run: | - package_name=$(node -p "JSON.parse(require('fs').readFileSync('mcp/package.json', 'utf8')).name") - base_version=$(node -p "JSON.parse(require('fs').readFileSync('mcp/package.json', 'utf8')).version.split('-')[0]") - - if [[ "${GITHUB_REF_NAME}" == "dev" ]]; then - full_version="${base_version}-dev.${GITHUB_RUN_NUMBER}.${GITHUB_RUN_ATTEMPT}" - npm_tag="prerelease" - is_prerelease="true" + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Compute release version + id: versioning + shell: bash + run: | + package_name=$(node -p "JSON.parse(require('fs').readFileSync('mcp/package.json', 'utf8')).name") + stable_tag_pattern='^v[0-9]+\.[0-9]+\.[0-9]+$' + release_tag_pattern='^v[0-9]+\.[0-9]+\.[0-9]+([-.][0-9A-Za-z.-]+)?$' + default_start_version="0.1.0" + + latest_stable_tag=$( + git tag --merged HEAD --list 'v*' \ + | grep -E "$stable_tag_pattern" \ + | sort -V \ + | tail -n 1 \ + || true + ) + + if [[ -n "$latest_stable_tag" ]]; then + current_stable_version="${latest_stable_tag#v}" + else + current_stable_version="" + fi + + if [[ -n "$current_stable_version" ]]; then + IFS='.' read -r major_version minor_version patch_version <<< "$current_stable_version" + next_patch_version="${major_version}.${minor_version}.$((patch_version + 1))" + else + next_patch_version="$default_start_version" + fi + + should_release="true" + npm_tag="latest" + is_prerelease="false" + base_version="" + full_version="" + release_tag="" + release_title="" + asset_name="" + notes_start_tag="" + + if [[ "${GITHUB_REF_NAME}" == "dev" ]]; then + base_version="${next_patch_version}" + full_version="${base_version}-dev.${GITHUB_RUN_NUMBER}.${GITHUB_RUN_ATTEMPT}" + release_tag="v${full_version}" + release_title="Release ${release_tag}" + asset_name="meting-agent-skill-${full_version}.zip" + npm_tag="prerelease" + is_prerelease="true" + notes_start_tag=$( + git tag --merged HEAD --list 'v*' \ + | grep -E "$release_tag_pattern" \ + | sort -V \ + | tail -n 1 \ + || true + ) + else + head_stable_tag=$( + git tag --points-at HEAD --list 'v*' \ + | grep -E "$stable_tag_pattern" \ + | sort -V \ + | tail -n 1 \ + || true + ) + + if [[ -n "$head_stable_tag" ]]; then + base_version="${head_stable_tag#v}" + notes_start_tag=$( + git tag --merged HEAD --list 'v*' \ + | grep -E "$stable_tag_pattern" \ + | grep -vx "$head_stable_tag" \ + | sort -V \ + | tail -n 1 \ + || true + ) else - full_version="${base_version}" - npm_tag="latest" - is_prerelease="false" + base_version="${next_patch_version}" + notes_start_tag="${latest_stable_tag}" fi + full_version="${base_version}" release_tag="v${full_version}" release_title="Release ${release_tag}" asset_name="meting-agent-skill-${full_version}.zip" + fi - echo "package_name=$package_name" >> "$GITHUB_OUTPUT" - echo "base_version=$base_version" >> "$GITHUB_OUTPUT" - echo "full_version=$full_version" >> "$GITHUB_OUTPUT" - echo "npm_tag=$npm_tag" >> "$GITHUB_OUTPUT" - echo "release_tag=$release_tag" >> "$GITHUB_OUTPUT" - echo "release_title=$release_title" >> "$GITHUB_OUTPUT" - echo "asset_name=$asset_name" >> "$GITHUB_OUTPUT" - echo "is_prerelease=$is_prerelease" >> "$GITHUB_OUTPUT" + echo "should_release=$should_release" >> "$GITHUB_OUTPUT" + echo "package_name=$package_name" >> "$GITHUB_OUTPUT" + echo "base_version=$base_version" >> "$GITHUB_OUTPUT" + echo "full_version=$full_version" >> "$GITHUB_OUTPUT" + echo "npm_tag=$npm_tag" >> "$GITHUB_OUTPUT" + echo "release_tag=$release_tag" >> "$GITHUB_OUTPUT" + echo "release_title=$release_title" >> "$GITHUB_OUTPUT" + echo "asset_name=$asset_name" >> "$GITHUB_OUTPUT" + echo "is_prerelease=$is_prerelease" >> "$GITHUB_OUTPUT" + echo "notes_start_tag=$notes_start_tag" >> "$GITHUB_OUTPUT" publish-npm: needs: versioning + if: needs.versioning.outputs.shouldRelease == 'true' runs-on: ubuntu-latest permissions: contents: read @@ -67,71 +138,78 @@ jobs: run: working-directory: mcp steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: - node-version: 24 - registry-url: "https://registry.npmjs.org" - - run: npm ci - - run: npm run verify - - name: Set package version for publish - shell: bash - run: | - node -e 'const fs=require("node:fs"); const packagePath="package.json"; const packageJson=JSON.parse(fs.readFileSync(packagePath, "utf8")); packageJson.version=process.env.RELEASE_VERSION; fs.writeFileSync(packagePath, JSON.stringify(packageJson, null, 2) + "\n");' - env: - RELEASE_VERSION: ${{ needs.versioning.outputs.fullVersion }} - - name: Check npm version availability - id: npm-version-check - shell: bash - run: | - if npm view "${{ needs.versioning.outputs.packageName }}@${{ needs.versioning.outputs.fullVersion }}" version --registry https://registry.npmjs.org > /dev/null 2>&1; then - echo "should_publish=false" >> "$GITHUB_OUTPUT" - echo "Version ${{ needs.versioning.outputs.fullVersion }} already exists on npm, skipping publish." - else - echo "should_publish=true" >> "$GITHUB_OUTPUT" - fi - - name: Publish npm package - if: steps.npm-version-check.outputs.should_publish == 'true' - run: npm publish --provenance --access public --tag "${{ needs.versioning.outputs.npmTag }}" + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 24 + registry-url: "https://registry.npmjs.org" + - run: npm ci + - run: npm run verify + - name: Set package version for publish + shell: bash + run: | + node -e 'const fs=require("node:fs"); const packagePath="package.json"; const packageJson=JSON.parse(fs.readFileSync(packagePath, "utf8")); packageJson.version=process.env.RELEASE_VERSION; fs.writeFileSync(packagePath, JSON.stringify(packageJson, null, 2) + "\n");' + env: + RELEASE_VERSION: ${{ needs.versioning.outputs.fullVersion }} + - name: Check npm version availability + id: npm-version-check + shell: bash + run: | + if npm view "${{ needs.versioning.outputs.packageName }}@${{ needs.versioning.outputs.fullVersion }}" version --registry https://registry.npmjs.org > /dev/null 2>&1; then + echo "should_publish=false" >> "$GITHUB_OUTPUT" + echo "Version ${{ needs.versioning.outputs.fullVersion }} already exists on npm, skipping publish." + else + echo "should_publish=true" >> "$GITHUB_OUTPUT" + fi + - name: Publish npm package + if: steps.npm-version-check.outputs.should_publish == 'true' + run: npm publish --provenance --access public --tag "${{ needs.versioning.outputs.npmTag }}" release-skill: needs: versioning + if: needs.versioning.outputs.shouldRelease == 'true' runs-on: ubuntu-latest permissions: contents: write steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: - node-version: 24 - - name: Build skill release bundle - run: node scripts/build-skill-release.mjs - - name: Create zip archive - shell: bash - run: | - cd dist - zip -r "${{ needs.versioning.outputs.assetName }}" meting-agent-skill - - name: Publish GitHub release asset - env: - GH_TOKEN: ${{ github.token }} - shell: bash - run: | - asset="dist/${{ needs.versioning.outputs.assetName }}" - release_tag="${{ needs.versioning.outputs.releaseTag }}" - release_title="${{ needs.versioning.outputs.releaseTitle }}" - is_prerelease="${{ needs.versioning.outputs.isPrerelease }}" - - if gh release view "$release_tag" > /dev/null 2>&1; then - if [[ "$is_prerelease" == "true" ]]; then - gh release edit "$release_tag" --title "$release_title" --prerelease - else - gh release edit "$release_tag" --title "$release_title" --latest - fi - gh release upload "$release_tag" "$asset" --clobber + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 24 + - name: Build skill release bundle + run: node scripts/build-skill-release.mjs + - name: Create zip archive + shell: bash + run: | + cd dist + zip -r "${{ needs.versioning.outputs.assetName }}" meting-agent-skill + - name: Publish GitHub release asset + env: + GH_TOKEN: ${{ github.token }} + shell: bash + run: | + asset="dist/${{ needs.versioning.outputs.assetName }}" + release_tag="${{ needs.versioning.outputs.releaseTag }}" + release_title="${{ needs.versioning.outputs.releaseTitle }}" + is_prerelease="${{ needs.versioning.outputs.isPrerelease }}" + notes_start_tag="${{ needs.versioning.outputs.notesStartTag }}" + + if gh release view "$release_tag" > /dev/null 2>&1; then + if [[ "$is_prerelease" == "true" ]]; then + gh release edit "$release_tag" --title "$release_title" --prerelease + else + gh release edit "$release_tag" --title "$release_title" --latest + fi + gh release upload "$release_tag" "$asset" --clobber + else + notes_args=() + if [[ -n "$notes_start_tag" ]]; then + notes_args+=(--notes-start-tag "$notes_start_tag") + fi + + if [[ "$is_prerelease" == "true" ]]; then + gh release create "$release_tag" "$asset" --target "${GITHUB_SHA}" --title "$release_title" --prerelease --generate-notes "${notes_args[@]}" else - if [[ "$is_prerelease" == "true" ]]; then - gh release create "$release_tag" "$asset" --title "$release_title" --prerelease --generate-notes - else - gh release create "$release_tag" "$asset" --title "$release_title" --generate-notes - fi + gh release create "$release_tag" "$asset" --target "${GITHUB_SHA}" --title "$release_title" --generate-notes "${notes_args[@]}" fi + fi diff --git a/.prettierignore b/.prettierignore index 23edf1b..fd25ca2 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,6 +1,7 @@ dist/ node_modules/ *.md +.github/workflows/release.yml mcp/.inspect/ mcp/.probe/ diff --git a/README.md b/README.md index 89fc33c..70b7756 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ `Meting-Agent` 是基于 **[metowolf/Meting](https://github.com/metowolf/Meting)** 构建的多平台音乐能力封装,当前提供两类交付物: -- [@eldment/meting-agent](https://www.npmjs.com/package/@eldment/meting-agent) —— 发布到 npm 的 Node.js **MCP Server** -- [skills/meting-agent](https://github.com/ELDment/Meting-Agent/releases) —— 可下载的通用 **Skills** 资产 +- **MCP**:[@eldment/meting-agent](https://www.npmjs.com/package/@eldment/meting-agent) +- **Skill**:[skills/meting-agent](https://github.com/ELDment/Meting-Agent/releases) ## 能力 @@ -21,7 +21,7 @@ ## 文档 - MCP Server 配置说明见 [mcp/README.md](./mcp/README.md) -- Skills 配置说明见 [skills/meting-agent/README.md](./skills/meting-agent/README.md) +- Skill 配置说明见 [skills/meting-agent/README.md](./skills/meting-agent/README.md) - 贡献流程与同步机制(编译时报错)说明见 [docs/CONTRIBUTING.md](./docs/CONTRIBUTING.md) ## 致谢 diff --git a/skills/meting-agent/README.md b/skills/meting-agent/README.md index f818d31..44b1cd7 100644 --- a/skills/meting-agent/README.md +++ b/skills/meting-agent/README.md @@ -4,7 +4,7 @@ ## 下载 -skill 不通过 npm 发布,下载入口是 [GitHub Releases](https://github.com/ELDment/Meting-Agent/releases) +不通过 npm 发布,下载入口是 [GitHub Releases](https://github.com/ELDment/Meting-Agent/releases) ## Cookie 规则 From 8bbcc6df8f647987325e7622307fbfe0483faa8e Mon Sep 17 00:00:00 2001 From: Ambr0se Date: Mon, 16 Mar 2026 12:32:22 +0800 Subject: [PATCH 09/11] ci: Fix release version fallback and tag baseline --- .github/workflows/release.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c61b44b..f7436f9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -36,9 +36,9 @@ jobs: shell: bash run: | package_name=$(node -p "JSON.parse(require('fs').readFileSync('mcp/package.json', 'utf8')).name") + package_version=$(node -p "JSON.parse(require('fs').readFileSync('mcp/package.json', 'utf8')).version.split('-')[0]") stable_tag_pattern='^v[0-9]+\.[0-9]+\.[0-9]+$' release_tag_pattern='^v[0-9]+\.[0-9]+\.[0-9]+([-.][0-9A-Za-z.-]+)?$' - default_start_version="0.1.0" latest_stable_tag=$( git tag --merged HEAD --list 'v*' \ @@ -51,14 +51,13 @@ jobs: if [[ -n "$latest_stable_tag" ]]; then current_stable_version="${latest_stable_tag#v}" else - current_stable_version="" + current_stable_version="${package_version}" + echo "::warning::No stable git tag found on origin. Falling back to mcp/package.json version ${package_version} as the release base." fi if [[ -n "$current_stable_version" ]]; then IFS='.' read -r major_version minor_version patch_version <<< "$current_stable_version" next_patch_version="${major_version}.${minor_version}.$((patch_version + 1))" - else - next_patch_version="$default_start_version" fi should_release="true" From 09b6bcbb2edafd34f39cbc266478b138fd7d2d6d Mon Sep 17 00:00:00 2001 From: Ambr0se Date: Mon, 16 Mar 2026 12:46:15 +0800 Subject: [PATCH 10/11] docs: Fix minor typos --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 70b7756..7a10dfb 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ ## 文档 -- MCP Server 配置说明见 [mcp/README.md](./mcp/README.md) +- MCP 配置说明见 [mcp/README.md](./mcp/README.md) - Skill 配置说明见 [skills/meting-agent/README.md](./skills/meting-agent/README.md) - 贡献流程与同步机制(编译时报错)说明见 [docs/CONTRIBUTING.md](./docs/CONTRIBUTING.md) From 485ba53a204e38ab7c5dd772844006d49d460f28 Mon Sep 17 00:00:00 2001 From: Ambr0se Date: Mon, 16 Mar 2026 12:54:52 +0800 Subject: [PATCH 11/11] docs: Clarify cookie precedence and environment variable usage --- skills/meting-agent/README.md | 10 ++++++---- skills/meting-agent/SKILL.md | 11 ++++++----- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/skills/meting-agent/README.md b/skills/meting-agent/README.md index 44b1cd7..aa00967 100644 --- a/skills/meting-agent/README.md +++ b/skills/meting-agent/README.md @@ -8,11 +8,13 @@ ## Cookie 规则 -- 优先使用 `METING__COOKIE` -- 其次使用 `METING_COOKIE` -- 最后才使用命令参数 `--cookie` +运行时按以下优先级取 Cookie: -支持的平台环境变量: +1. `METING__COOKIE` +2. `METING_COOKIE` +3. 命令参数 `--cookie` + +支持的环境变量: - `METING_NETEASE_COOKIE` - `METING_TENCENT_COOKIE` diff --git a/skills/meting-agent/SKILL.md b/skills/meting-agent/SKILL.md index 2bc48c9..293b81d 100644 --- a/skills/meting-agent/SKILL.md +++ b/skills/meting-agent/SKILL.md @@ -1,6 +1,6 @@ --- name: meting-agent -description: Use when Codex needs direct music lookup capabilities through bundled Meting scripts in this skill, including song search, album lookup, artist lookup, playlist lookup, lyrics, cover URLs, and playback URLs, or when it needs to maintain this skill's standalone music client implementation. +description: Use when an AI agent needs direct music lookup capabilities through the bundled Meting scripts in this skill, including song search, album lookup, artist lookup, playlist lookup, lyrics, cover URLs, and playback URLs, or when it needs to maintain this skill's standalone music client implementation. --- # Meting Agent @@ -16,7 +16,7 @@ description: Use when Codex needs direct music lookup capabilities through bundl 1. Run `node scripts/meting-cli.mjs platforms` to inspect supported platforms. 2. Run `node scripts/meting-cli.mjs search --platform netease --keyword "我怀念的" --limit 3` for search tasks. 3. Run `node scripts/meting-cli.mjs song --platform netease --id ` and related commands for detail lookups. -4. Set `METING__COOKIE` or `METING_COOKIE` before commands that need authenticated access. +4. Remember the default cookie resolution order: environment variable `METING__COOKIE` first, environment variable `METING_COOKIE` second, and CLI option `--cookie` only as the final override. ## Commands @@ -32,6 +32,7 @@ description: Use when Codex needs direct music lookup capabilities through bundl ## Configuration Rules -- Prefer `METING__COOKIE` for per-platform cookies. -- Use `METING_COOKIE` as a shared fallback. -- Use `--cookie ` only when the call needs an explicit override. +- Follow the default cookie resolution order used by the program: `METING__COOKIE` first, `METING_COOKIE` second, and `--cookie ` last. +- Treat `METING__COOKIE` and `METING_COOKIE` as environment variable names, not CLI options. +- Available environment variables include `METING_NETEASE_COOKIE`, `METING_TENCENT_COOKIE`, `METING_KUGOU_COOKIE`, `METING_BAIDU_COOKIE`, `METING_KUWO_COOKIE`, and shared fallback `METING_COOKIE`. +- Use CLI option `--cookie ` only when the call needs an explicit one-off override.