Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
222 changes: 190 additions & 32 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,132 @@ name: release
on:
push:
branches:
- main
- main
- dev

concurrency:
group: release-${{ github.ref }}
cancel-in-progress: true

env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true

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 }}
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 }}
notesStartTag: ${{ steps.versioning.outputs.notes_start_tag }}
steps:
- 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")
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.-]+)?$'

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="${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))"
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
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 "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
Expand All @@ -15,42 +137,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: node scripts/sync-mcp-core.mjs
- run: npm ci
- run: npm run verify
- run: npm publish --provenance --access public
- 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: 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
- name: Create zip archive
run: |
cd dist
zip -r "meting-agent-skill-${GITHUB_REF_NAME}.zip" meting-agent-skill
- name: Publish GitHub release asset
env:
GH_TOKEN: ${{ github.token }}
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
- 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
gh release create "${GITHUB_REF_NAME}" "$asset" --title "${GITHUB_REF_NAME}" --generate-notes
gh release create "$release_tag" "$asset" --target "${GITHUB_SHA}" --title "$release_title" --generate-notes "${notes_args[@]}"
fi
fi
7 changes: 2 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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/
8 changes: 3 additions & 5 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
dist/
node_modules/
*.md
.github/workflows/release.yml

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/
59 changes: 21 additions & 38 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,50 +1,33 @@
# 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 资产分发。
- **MCP**:[@eldment/meting-agent](https://www.npmjs.com/package/@eldment/meting-agent)
- **Skill**:[skills/meting-agent](https://github.com/ELDment/Meting-Agent/releases)

## 目录说明
## 能力

- `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 获取封面或图片链接

## 常用命令
支持平台:[网易云音乐](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:
## 文档

```powershell
node scripts/sync-mcp-core.mjs
```
- MCP 配置说明见 [mcp/README.md](./mcp/README.md)
- Skill 配置说明见 [skills/meting-agent/README.md](./skills/meting-agent/README.md)
- 贡献流程与同步机制(编译时报错)说明见 [docs/CONTRIBUTING.md](./docs/CONTRIBUTING.md)

构建 skill release bundle:
## 致谢

```powershell
node scripts/build-skill-release.mjs
```
感谢 **[metowolf/Meting](https://github.com/metowolf/Meting)** 提供跨平台统一接口与各平台 provider 实现

验证 MCP 子项目:
---

```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
56 changes: 56 additions & 0 deletions docs/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -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`
Loading
Loading