@zhin.js/plugin-music 1.0.26 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/commands/cookie/delete/[source].js +23 -0
- package/commands/cookie/delete/[source].ts +27 -0
- package/commands/cookie/get/[source].js +29 -0
- package/commands/cookie/get/[source].ts +33 -0
- package/commands/cookie/set/[source].js +23 -0
- package/commands/cookie/set/[source].ts +27 -0
- package/commands/cookie/status.js +25 -0
- package/commands/cookie/status.ts +25 -0
- package/commands/login/[platform].js +64 -0
- package/commands/login/[platform].ts +76 -0
- package/commands//347/202/271/346/255/214/[keyword].js +62 -0
- package/commands//347/202/271/346/255/214/[keyword].ts +74 -0
- package/components/share-music.js +24 -0
- package/components/share-music.ts +29 -0
- package/lib/config.d.ts +3 -3
- package/lib/config.d.ts.map +1 -1
- package/lib/config.js +36 -7
- package/lib/config.js.map +1 -1
- package/lib/credential-store.d.ts +34 -0
- package/lib/credential-store.d.ts.map +1 -0
- package/lib/credential-store.js +111 -0
- package/lib/credential-store.js.map +1 -0
- package/lib/index.d.ts +7 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +6 -112
- package/lib/index.js.map +1 -1
- package/lib/login/index.d.ts +20 -0
- package/lib/login/index.d.ts.map +1 -0
- package/lib/login/index.js +100 -0
- package/lib/login/index.js.map +1 -0
- package/lib/login/netease.d.ts +6 -0
- package/lib/login/netease.d.ts.map +1 -0
- package/lib/login/netease.js +58 -0
- package/lib/login/netease.js.map +1 -0
- package/lib/login/qq.d.ts +7 -0
- package/lib/login/qq.d.ts.map +1 -0
- package/lib/login/qq.js +139 -0
- package/lib/login/qq.js.map +1 -0
- package/lib/login/types.d.ts +19 -0
- package/lib/login/types.d.ts.map +1 -0
- package/lib/login/types.js +11 -0
- package/lib/login/types.js.map +1 -0
- package/lib/music-lib.d.ts +31 -0
- package/lib/music-lib.d.ts.map +1 -0
- package/lib/music-lib.js +59 -0
- package/lib/music-lib.js.map +1 -0
- package/lib/session.d.ts +29 -0
- package/lib/session.d.ts.map +1 -0
- package/lib/session.js +39 -0
- package/lib/session.js.map +1 -0
- package/lib/sources/index.d.ts +2 -1
- package/lib/sources/index.d.ts.map +1 -1
- package/lib/sources/index.js +6 -2
- package/lib/sources/index.js.map +1 -1
- package/lib/sources/kugou.d.ts +6 -0
- package/lib/sources/kugou.d.ts.map +1 -0
- package/lib/sources/kugou.js +54 -0
- package/lib/sources/kugou.js.map +1 -0
- package/lib/sources/kuwo.d.ts +7 -0
- package/lib/sources/kuwo.d.ts.map +1 -0
- package/lib/sources/kuwo.js +69 -0
- package/lib/sources/kuwo.js.map +1 -0
- package/lib/sources/netease.d.ts +1 -1
- package/lib/sources/netease.d.ts.map +1 -1
- package/lib/sources/netease.js +22 -6
- package/lib/sources/netease.js.map +1 -1
- package/lib/sources/qq.d.ts +1 -14
- package/lib/sources/qq.d.ts.map +1 -1
- package/lib/sources/qq.js +114 -82
- package/lib/sources/qq.js.map +1 -1
- package/lib/types.d.ts +4 -24
- package/lib/types.d.ts.map +1 -1
- package/lib/types.js +0 -1
- package/lib/types.js.map +1 -1
- package/middlewares/music-selection.js +46 -0
- package/middlewares/music-selection.ts +56 -0
- package/package.json +62 -13
- package/plugin.js +31 -0
- package/schema.json +6 -0
- package/src/config.ts +54 -30
- package/src/credential-store.ts +169 -0
- package/src/index.ts +45 -0
- package/src/login/index.ts +135 -0
- package/src/login/netease.ts +69 -0
- package/src/login/qq.ts +154 -0
- package/src/login/types.ts +27 -0
- package/src/music-lib.ts +70 -0
- package/src/session.ts +60 -0
- package/src/sources/index.ts +12 -8
- package/src/sources/kugou.ts +78 -0
- package/src/sources/kuwo.ts +94 -0
- package/src/sources/netease.ts +29 -11
- package/src/sources/qq.ts +158 -126
- package/src/types.ts +6 -28
- package/tools/music-search.js +21 -0
- package/tools/music-search.ts +27 -0
- package/tools/music-share.js +20 -0
- package/tools/music-share.ts +24 -0
- package/lib/async-jsx.d.ts +0 -19
- package/lib/async-jsx.d.ts.map +0 -1
- package/lib/async-jsx.js +0 -48
- package/lib/async-jsx.js.map +0 -1
- package/src/async-jsx.ts +0 -54
- package/src/index.tsx +0 -130
- /package/{skills/music/SKILL.md → agent/skills/music.md} +0 -0
package/src/sources/qq.ts
CHANGED
|
@@ -1,148 +1,180 @@
|
|
|
1
|
-
|
|
2
|
-
import type { MusicSearchService, MusicDetail,MusicInfo
|
|
1
|
+
import * as crypto from 'node:crypto';
|
|
2
|
+
import type { MusicSearchService, MusicDetail, MusicInfo } from '../types.js';
|
|
3
|
+
import { getCredential } from '../credential-store.js';
|
|
4
|
+
|
|
5
|
+
const md5 = (text: string) => crypto.createHash('md5').update(text).digest('hex');
|
|
6
|
+
|
|
7
|
+
const MUSICU_API = 'https://u.y.qq.com/cgi-bin/musicu.fcg';
|
|
8
|
+
|
|
9
|
+
function getHeaders(cookie?: string | null) {
|
|
10
|
+
return {
|
|
11
|
+
Referer: 'https://y.qq.com',
|
|
12
|
+
Cookie: cookie ?? '',
|
|
13
|
+
'Content-Type': 'application/json; charset=UTF-8',
|
|
14
|
+
};
|
|
15
|
+
}
|
|
3
16
|
|
|
4
|
-
/** QQ 音乐搜索服务 */
|
|
5
17
|
export class QQMusicService implements MusicSearchService {
|
|
6
18
|
async search(keyword: string, limit = 10): Promise<MusicInfo[]> {
|
|
7
19
|
try {
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
20
|
+
const cookie = await getCredential('qq', 'cookie');
|
|
21
|
+
const response = await fetch(MUSICU_API, {
|
|
22
|
+
method: 'POST',
|
|
23
|
+
headers: getHeaders(cookie),
|
|
24
|
+
body: JSON.stringify({
|
|
25
|
+
comm: { uin: '0', authst: '', ct: 29 },
|
|
26
|
+
search: {
|
|
27
|
+
method: 'DoSearchForQQMusicMobile',
|
|
28
|
+
module: 'music.search.SearchCgiService',
|
|
29
|
+
param: {
|
|
30
|
+
query: keyword,
|
|
31
|
+
grp: 1,
|
|
32
|
+
num_per_page: limit,
|
|
33
|
+
page_num: 1,
|
|
34
|
+
search_type: 0,
|
|
35
|
+
searchid: String(Date.now()),
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
}),
|
|
39
|
+
});
|
|
27
40
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
41
|
+
const data = (await response.json()) as { code?: number; search?: { data?: { body?: { song?: { list?: any[] }; item_song?: any[] } } } };
|
|
42
|
+
if (data.code !== undefined && +data.code !== 0) return [];
|
|
43
|
+
|
|
44
|
+
const body = data.search?.data?.body ?? {};
|
|
45
|
+
const list = (body.song?.list ?? body.item_song ?? []) as any[];
|
|
46
|
+
|
|
47
|
+
return list.slice(0, limit).map((e) => {
|
|
48
|
+
const mid = e?.mid ?? '';
|
|
49
|
+
const albumMid = e?.album?.pmid ?? e?.album?.mid ?? '';
|
|
50
|
+
const singerMid = e?.singer?.[0]?.pmid ?? e?.singer?.[0]?.mid ?? '';
|
|
51
|
+
const cover = albumMid
|
|
52
|
+
? `https://y.gtimg.cn/music/photo_new/T002R300x300M000${albumMid}.jpg`
|
|
53
|
+
: singerMid
|
|
54
|
+
? `https://y.qq.com/music/photo_new/T001R300x300M000${singerMid}.jpg`
|
|
55
|
+
: '';
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
id: String(e.id),
|
|
59
|
+
source: 'qq' as const,
|
|
60
|
+
title: e.name,
|
|
61
|
+
artist: (e.singer ?? []).map((s: any) => s.name).join('/'),
|
|
62
|
+
album: e.album?.name,
|
|
63
|
+
url: `https://y.qq.com/n/ryqq/songDetail/${mid}`,
|
|
64
|
+
image: cover,
|
|
65
|
+
duration: e.interval,
|
|
66
|
+
_mid: mid,
|
|
67
|
+
_mediaMid: e.file?.media_mid,
|
|
68
|
+
};
|
|
69
|
+
});
|
|
53
70
|
} catch (error) {
|
|
54
|
-
console.error('QQ Music
|
|
55
|
-
return
|
|
71
|
+
console.error('QQ Music search failed:', error);
|
|
72
|
+
return [];
|
|
56
73
|
}
|
|
57
74
|
}
|
|
58
75
|
|
|
59
76
|
async getDetail(id: string): Promise<MusicDetail> {
|
|
60
|
-
const url = new URL(
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
77
|
+
const url = new URL(MUSICU_API);
|
|
78
|
+
url.searchParams.set('format', 'json');
|
|
79
|
+
url.searchParams.set('data', JSON.stringify({
|
|
80
|
+
comm: { ct: 24, cv: 0 },
|
|
81
|
+
songinfo: {
|
|
82
|
+
method: 'get_song_detail_yqq',
|
|
83
|
+
param: { song_type: 0, song_mid: '', song_id: parseInt(id) },
|
|
84
|
+
module: 'music.pf_song_detail_svr',
|
|
85
|
+
},
|
|
86
|
+
}));
|
|
87
|
+
|
|
88
|
+
const response = await fetch(url, { method: 'GET' });
|
|
89
|
+
const result = await response.json() as any;
|
|
90
|
+
|
|
91
|
+
const trackInfo = result?.songinfo?.data?.track_info;
|
|
92
|
+
if (!trackInfo) throw new Error('Music not found');
|
|
93
|
+
|
|
94
|
+
const albumMid = trackInfo.album?.mid;
|
|
95
|
+
const image = albumMid
|
|
96
|
+
? `https://y.gtimg.cn/music/photo_new/T002R300x300M000${albumMid}.jpg`
|
|
97
|
+
: '';
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
id,
|
|
101
|
+
source: 'qq',
|
|
102
|
+
title: trackInfo.name,
|
|
103
|
+
url: `https://y.qq.com/n/ryqq/songDetail/${trackInfo.mid}`,
|
|
104
|
+
audio: await this.getAudioUrl(id, trackInfo.mid, trackInfo.file?.media_mid),
|
|
105
|
+
image,
|
|
106
|
+
duration: trackInfo.interval,
|
|
107
|
+
};
|
|
91
108
|
}
|
|
92
109
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
110
|
+
async getAudioUrl(id: string, mid?: string, mediaMid?: string): Promise<string> {
|
|
111
|
+
const cookie = await getCredential('qq', 'cookie');
|
|
112
|
+
if (cookie) {
|
|
113
|
+
try {
|
|
114
|
+
const uin = /uin=o?(\d+)/.exec(cookie)?.[1] ?? '0';
|
|
115
|
+
const songMid = mid ?? id;
|
|
116
|
+
const response = await fetch(MUSICU_API, {
|
|
117
|
+
method: 'POST',
|
|
118
|
+
headers: getHeaders(cookie),
|
|
119
|
+
body: JSON.stringify({
|
|
120
|
+
comm: {
|
|
121
|
+
ct: 19,
|
|
122
|
+
cv: '1891',
|
|
123
|
+
uin: '0',
|
|
124
|
+
guid: md5(`${uin}music`),
|
|
125
|
+
tmeAppID: 'qqmusic',
|
|
126
|
+
tmeLoginType: 2,
|
|
127
|
+
},
|
|
128
|
+
vkey: {
|
|
129
|
+
module: 'vkey.GetVkeyServer',
|
|
130
|
+
method: 'CgiGetVkey',
|
|
131
|
+
param: {
|
|
132
|
+
guid: md5(String(Date.now())),
|
|
133
|
+
songmid: [songMid],
|
|
134
|
+
songtype: [0],
|
|
135
|
+
uin: '0',
|
|
136
|
+
ctx: 1,
|
|
137
|
+
filename: [`C400${id}${mediaMid ?? songMid}.m4a`],
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
}),
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
const result = await response.json() as any;
|
|
144
|
+
if (result.vkey && +result.vkey.code === 0) {
|
|
145
|
+
const purl = result.vkey.data?.midurlinfo?.[0]?.purl;
|
|
146
|
+
if (purl) return `https://ws.stream.qqmusic.qq.com/${purl}`;
|
|
147
|
+
}
|
|
148
|
+
} catch {
|
|
149
|
+
// fall through to public fallback
|
|
108
150
|
}
|
|
109
|
-
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// 无 cookie 的公开 fallback:md5 签名直链
|
|
154
|
+
const songMid = mid ?? id;
|
|
155
|
+
const code = md5(`${songMid}q;z(&l~sdf2!nK`).substring(0, 5).toUpperCase();
|
|
156
|
+
return `https://c6.y.qq.com/rsc/fcgi-bin/fcg_pyq_play.fcg?songmid=${songMid}&code=${code}`;
|
|
110
157
|
}
|
|
111
158
|
|
|
112
|
-
/**
|
|
113
|
-
* 获取歌词
|
|
114
|
-
* @param id 音乐 ID
|
|
115
|
-
* @returns 歌词文本
|
|
116
|
-
*/
|
|
117
159
|
async getLyric(id: string): Promise<string | null> {
|
|
118
160
|
try {
|
|
119
|
-
const url = new URL('https://c.y.qq.com/lyric/fcgi-bin/fcg_query_lyric_new.fcg')
|
|
120
|
-
url.searchParams.set('songmid', id)
|
|
121
|
-
url.searchParams.set('g_tk', '5381')
|
|
122
|
-
url.searchParams.set('format', 'json')
|
|
123
|
-
url.searchParams.set('inCharset', 'utf8')
|
|
124
|
-
url.searchParams.set('outCharset', 'utf-8')
|
|
125
|
-
url.searchParams.set('nobase64', '1')
|
|
126
|
-
|
|
127
|
-
const response = await fetch(url, {
|
|
161
|
+
const url = new URL('https://c.y.qq.com/lyric/fcgi-bin/fcg_query_lyric_new.fcg');
|
|
162
|
+
url.searchParams.set('songmid', id);
|
|
163
|
+
url.searchParams.set('g_tk', '5381');
|
|
164
|
+
url.searchParams.set('format', 'json');
|
|
165
|
+
url.searchParams.set('inCharset', 'utf8');
|
|
166
|
+
url.searchParams.set('outCharset', 'utf-8');
|
|
167
|
+
url.searchParams.set('nobase64', '1');
|
|
168
|
+
|
|
169
|
+
const response = await fetch(url, {
|
|
128
170
|
method: 'GET',
|
|
129
|
-
headers: {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const data = await response.json() as { lyric?: string }
|
|
134
|
-
|
|
135
|
-
if (!data.lyric) return null
|
|
136
|
-
|
|
137
|
-
// QQ 音乐返回的歌词需要 base64 解码
|
|
138
|
-
try {
|
|
139
|
-
return Buffer.from(data.lyric, 'base64').toString('utf-8')
|
|
140
|
-
} catch {
|
|
141
|
-
return data.lyric
|
|
142
|
-
}
|
|
171
|
+
headers: { Referer: 'https://y.qq.com' },
|
|
172
|
+
});
|
|
173
|
+
const data = (await response.json()) as { lyric?: string };
|
|
174
|
+
return data.lyric ?? null;
|
|
143
175
|
} catch (error) {
|
|
144
|
-
console.error('QQ Music get lyric failed:', error)
|
|
145
|
-
return null
|
|
176
|
+
console.error('QQ Music get lyric failed:', error);
|
|
177
|
+
return null;
|
|
146
178
|
}
|
|
147
179
|
}
|
|
148
180
|
}
|
package/src/types.ts
CHANGED
|
@@ -1,24 +1,20 @@
|
|
|
1
|
-
|
|
1
|
+
export type MusicSource = 'qq' | 'netease' | 'kuwo' | 'kugou';
|
|
2
2
|
|
|
3
|
-
/** 音乐源类型 */
|
|
4
|
-
export type MusicSource = "qq" | "netease";
|
|
5
|
-
|
|
6
|
-
/** 音乐分享内容 */
|
|
7
3
|
export interface MusicInfo {
|
|
8
4
|
id: string;
|
|
9
5
|
source: MusicSource;
|
|
10
|
-
/** 跳转链接 */
|
|
11
6
|
url: string;
|
|
12
|
-
/** 音乐标题 */
|
|
13
7
|
title: string;
|
|
8
|
+
artist?: string;
|
|
9
|
+
album?: string;
|
|
10
|
+
duration?: number;
|
|
14
11
|
}
|
|
12
|
+
|
|
15
13
|
export interface MusicDetail extends MusicInfo {
|
|
16
14
|
image: string;
|
|
17
|
-
audio:string;
|
|
18
|
-
duration?: number;
|
|
15
|
+
audio: string;
|
|
19
16
|
}
|
|
20
17
|
|
|
21
|
-
/** 音乐源配置 */
|
|
22
18
|
export interface MusicSourceConfig {
|
|
23
19
|
appid: number;
|
|
24
20
|
package: string;
|
|
@@ -27,21 +23,6 @@ export interface MusicSourceConfig {
|
|
|
27
23
|
version: string;
|
|
28
24
|
}
|
|
29
25
|
|
|
30
|
-
/** QQ 音乐 API 响应 */
|
|
31
|
-
export interface MusicQQ {
|
|
32
|
-
id: string;
|
|
33
|
-
mid: string;
|
|
34
|
-
name: string;
|
|
35
|
-
docid: string;
|
|
36
|
-
singer: string;
|
|
37
|
-
album?: {
|
|
38
|
-
mid: string;
|
|
39
|
-
name: string;
|
|
40
|
-
};
|
|
41
|
-
interval?: number;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/** 网易云音乐 API 响应 */
|
|
45
26
|
export interface Music163 {
|
|
46
27
|
id: string;
|
|
47
28
|
name: string;
|
|
@@ -58,10 +39,7 @@ export interface Music163 {
|
|
|
58
39
|
};
|
|
59
40
|
}
|
|
60
41
|
|
|
61
|
-
/** 音乐搜索服务接口 */
|
|
62
42
|
export interface MusicSearchService {
|
|
63
|
-
/** 搜索音乐 */
|
|
64
43
|
search(keyword: string, limit?: number): Promise<MusicInfo[]>;
|
|
65
|
-
/** 获取音乐详情 */
|
|
66
44
|
getDetail(id: string): Promise<MusicDetail>;
|
|
67
45
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
+
import { defineAgentTool } from '@zhin.js/tool';
|
|
3
|
+
import { searchMusic } from "../lib/music-lib.js";
|
|
4
|
+
export default defineAgentTool({
|
|
5
|
+
description: '搜索音乐并返回结果列表(支持QQ/网易云/酷我/酷狗)',
|
|
6
|
+
inputSchema: {
|
|
7
|
+
type: 'object',
|
|
8
|
+
properties: {
|
|
9
|
+
keyword: { type: 'string', description: '搜索关键词' },
|
|
10
|
+
source: {
|
|
11
|
+
type: 'string',
|
|
12
|
+
enum: ['qq', 'netease', 'kuwo', 'kugou'],
|
|
13
|
+
description: '音乐源(默认 qq)',
|
|
14
|
+
},
|
|
15
|
+
limit: { type: 'number', description: '返回结果数量(默认 5)' },
|
|
16
|
+
},
|
|
17
|
+
required: ['keyword'],
|
|
18
|
+
},
|
|
19
|
+
approval: 'never',
|
|
20
|
+
execute: ({ keyword, source, limit }) => searchMusic(String(keyword), source, typeof limit === 'number' ? limit : 5),
|
|
21
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { defineAgentTool } from '@zhin.js/tool';
|
|
2
|
+
import { searchMusic } from '../src/music-lib.js';
|
|
3
|
+
import type { MusicSource } from '../src/types.js';
|
|
4
|
+
|
|
5
|
+
export default defineAgentTool<{
|
|
6
|
+
keyword: string;
|
|
7
|
+
source?: MusicSource;
|
|
8
|
+
limit?: number;
|
|
9
|
+
}>({
|
|
10
|
+
description: '搜索音乐并返回结果列表(支持QQ/网易云/酷我/酷狗)',
|
|
11
|
+
inputSchema: {
|
|
12
|
+
type: 'object',
|
|
13
|
+
properties: {
|
|
14
|
+
keyword: { type: 'string', description: '搜索关键词' },
|
|
15
|
+
source: {
|
|
16
|
+
type: 'string',
|
|
17
|
+
enum: ['qq', 'netease', 'kuwo', 'kugou'],
|
|
18
|
+
description: '音乐源(默认 qq)',
|
|
19
|
+
},
|
|
20
|
+
limit: { type: 'number', description: '返回结果数量(默认 5)' },
|
|
21
|
+
},
|
|
22
|
+
required: ['keyword'],
|
|
23
|
+
},
|
|
24
|
+
approval: 'never',
|
|
25
|
+
execute: ({ keyword, source, limit }) =>
|
|
26
|
+
searchMusic(String(keyword), source, typeof limit === 'number' ? limit : 5),
|
|
27
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
+
import { defineAgentTool } from '@zhin.js/tool';
|
|
3
|
+
import { shareMusicDetail } from "../lib/music-lib.js";
|
|
4
|
+
export default defineAgentTool({
|
|
5
|
+
description: '分享指定的音乐(支持QQ/网易云/酷我/酷狗)',
|
|
6
|
+
inputSchema: {
|
|
7
|
+
type: 'object',
|
|
8
|
+
properties: {
|
|
9
|
+
id: { type: 'string', description: '音乐 ID' },
|
|
10
|
+
source: {
|
|
11
|
+
type: 'string',
|
|
12
|
+
enum: ['qq', 'netease', 'kuwo', 'kugou'],
|
|
13
|
+
description: '音乐源',
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
required: ['id', 'source'],
|
|
17
|
+
},
|
|
18
|
+
approval: 'never',
|
|
19
|
+
execute: ({ id, source }) => shareMusicDetail(String(id), source),
|
|
20
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { defineAgentTool } from '@zhin.js/tool';
|
|
2
|
+
import { shareMusicDetail } from '../src/music-lib.js';
|
|
3
|
+
import type { MusicSource } from '../src/types.js';
|
|
4
|
+
|
|
5
|
+
export default defineAgentTool<{
|
|
6
|
+
id: string;
|
|
7
|
+
source: MusicSource;
|
|
8
|
+
}>({
|
|
9
|
+
description: '分享指定的音乐(支持QQ/网易云/酷我/酷狗)',
|
|
10
|
+
inputSchema: {
|
|
11
|
+
type: 'object',
|
|
12
|
+
properties: {
|
|
13
|
+
id: { type: 'string', description: '音乐 ID' },
|
|
14
|
+
source: {
|
|
15
|
+
type: 'string',
|
|
16
|
+
enum: ['qq', 'netease', 'kuwo', 'kugou'],
|
|
17
|
+
description: '音乐源',
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
required: ['id', 'source'],
|
|
21
|
+
},
|
|
22
|
+
approval: 'never',
|
|
23
|
+
execute: ({ id, source }) => shareMusicDetail(String(id), source),
|
|
24
|
+
});
|
package/lib/async-jsx.d.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { SendContent } from "zhin.js";
|
|
2
|
-
/**
|
|
3
|
-
* 异步组件包装器
|
|
4
|
-
* 类似 Next.js 的异步组件支持
|
|
5
|
-
*/
|
|
6
|
-
export declare function AsyncComponent<P = any>(Component: (props: P) => Promise<SendContent>): (props: P) => SendContent;
|
|
7
|
-
/**
|
|
8
|
-
* 渲染异步内容
|
|
9
|
-
* 检查是否是异步标记,如果是则执行
|
|
10
|
-
*/
|
|
11
|
-
export declare function renderAsync(content: any): Promise<SendContent>;
|
|
12
|
-
/**
|
|
13
|
-
* Suspense 组件工厂
|
|
14
|
-
* 用于包装异步组件并提供 fallback
|
|
15
|
-
*/
|
|
16
|
-
export declare function createSuspense(fallback?: string): (props: {
|
|
17
|
-
children: any;
|
|
18
|
-
}) => Promise<SendContent>;
|
|
19
|
-
//# sourceMappingURL=async-jsx.d.ts.map
|
package/lib/async-jsx.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"async-jsx.d.ts","sourceRoot":"","sources":["../src/async-jsx.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAEtC;;;GAGG;AACH,wBAAgB,cAAc,CAAC,CAAC,GAAG,GAAG,EACpC,SAAS,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,OAAO,CAAC,WAAW,CAAC,GAC5C,CAAC,KAAK,EAAE,CAAC,KAAK,WAAW,CAc3B;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,GAAG,GAAG,OAAO,CAAC,WAAW,CAAC,CAQpE;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,GAAE,MAAiB,IACzB,OAAO;IAAE,QAAQ,EAAE,GAAG,CAAA;CAAE,KAAG,OAAO,CAAC,WAAW,CAAC,CAQ/E"}
|
package/lib/async-jsx.js
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
// async-jsx.ts - 异步 JSX 增强工具
|
|
2
|
-
/**
|
|
3
|
-
* 异步组件包装器
|
|
4
|
-
* 类似 Next.js 的异步组件支持
|
|
5
|
-
*/
|
|
6
|
-
export function AsyncComponent(Component) {
|
|
7
|
-
// 返回一个同步函数,但内部处理是异步的
|
|
8
|
-
return (props) => {
|
|
9
|
-
// 创建一个特殊的标记对象
|
|
10
|
-
const asyncMarker = {
|
|
11
|
-
__async: true,
|
|
12
|
-
component: Component,
|
|
13
|
-
props,
|
|
14
|
-
execute: () => Component(props)
|
|
15
|
-
};
|
|
16
|
-
// TypeScript 类型断言
|
|
17
|
-
return asyncMarker;
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* 渲染异步内容
|
|
22
|
-
* 检查是否是异步标记,如果是则执行
|
|
23
|
-
*/
|
|
24
|
-
export async function renderAsync(content) {
|
|
25
|
-
if (content && typeof content === 'object' && content.__async) {
|
|
26
|
-
return await content.execute();
|
|
27
|
-
}
|
|
28
|
-
if (content && typeof content.then === 'function') {
|
|
29
|
-
return await content;
|
|
30
|
-
}
|
|
31
|
-
return content;
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Suspense 组件工厂
|
|
35
|
-
* 用于包装异步组件并提供 fallback
|
|
36
|
-
*/
|
|
37
|
-
export function createSuspense(fallback = '加载中...') {
|
|
38
|
-
return async function Suspense(props) {
|
|
39
|
-
try {
|
|
40
|
-
return await renderAsync(props.children);
|
|
41
|
-
}
|
|
42
|
-
catch (error) {
|
|
43
|
-
console.error('Suspense error:', error);
|
|
44
|
-
return fallback;
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
//# sourceMappingURL=async-jsx.js.map
|
package/lib/async-jsx.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"async-jsx.js","sourceRoot":"","sources":["../src/async-jsx.ts"],"names":[],"mappings":"AAAA,6BAA6B;AAI7B;;;GAGG;AACH,MAAM,UAAU,cAAc,CAC5B,SAA6C;IAE7C,qBAAqB;IACrB,OAAO,CAAC,KAAQ,EAAE,EAAE;QAClB,cAAc;QACd,MAAM,WAAW,GAAG;YAClB,OAAO,EAAE,IAAI;YACb,SAAS,EAAE,SAAS;YACpB,KAAK;YACL,OAAO,EAAE,GAAG,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC;SAChC,CAAC;QAEF,kBAAkB;QAClB,OAAO,WAAiC,CAAC;IAC3C,CAAC,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAAY;IAC5C,IAAI,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QAC9D,OAAO,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;IACjC,CAAC;IACD,IAAI,OAAO,IAAI,OAAO,OAAO,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;QAClD,OAAO,MAAM,OAAO,CAAC;IACvB,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,WAAmB,QAAQ;IACxD,OAAO,KAAK,UAAU,QAAQ,CAAC,KAAwB;QACrD,IAAI,CAAC;YACH,OAAO,MAAM,WAAW,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC3C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,iBAAiB,EAAE,KAAK,CAAC,CAAC;YACxC,OAAO,QAAQ,CAAC;QAClB,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
|
package/src/async-jsx.ts
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
// async-jsx.ts - 异步 JSX 增强工具
|
|
2
|
-
|
|
3
|
-
import { SendContent } from "zhin.js";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* 异步组件包装器
|
|
7
|
-
* 类似 Next.js 的异步组件支持
|
|
8
|
-
*/
|
|
9
|
-
export function AsyncComponent<P = any>(
|
|
10
|
-
Component: (props: P) => Promise<SendContent>
|
|
11
|
-
): (props: P) => SendContent {
|
|
12
|
-
// 返回一个同步函数,但内部处理是异步的
|
|
13
|
-
return (props: P) => {
|
|
14
|
-
// 创建一个特殊的标记对象
|
|
15
|
-
const asyncMarker = {
|
|
16
|
-
__async: true,
|
|
17
|
-
component: Component,
|
|
18
|
-
props,
|
|
19
|
-
execute: () => Component(props)
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
// TypeScript 类型断言
|
|
23
|
-
return asyncMarker as any as SendContent;
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* 渲染异步内容
|
|
29
|
-
* 检查是否是异步标记,如果是则执行
|
|
30
|
-
*/
|
|
31
|
-
export async function renderAsync(content: any): Promise<SendContent> {
|
|
32
|
-
if (content && typeof content === 'object' && content.__async) {
|
|
33
|
-
return await content.execute();
|
|
34
|
-
}
|
|
35
|
-
if (content && typeof content.then === 'function') {
|
|
36
|
-
return await content;
|
|
37
|
-
}
|
|
38
|
-
return content;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Suspense 组件工厂
|
|
43
|
-
* 用于包装异步组件并提供 fallback
|
|
44
|
-
*/
|
|
45
|
-
export function createSuspense(fallback: string = '加载中...') {
|
|
46
|
-
return async function Suspense(props: { children: any }): Promise<SendContent> {
|
|
47
|
-
try {
|
|
48
|
-
return await renderAsync(props.children);
|
|
49
|
-
} catch (error) {
|
|
50
|
-
console.error('Suspense error:', error);
|
|
51
|
-
return fallback;
|
|
52
|
-
}
|
|
53
|
-
};
|
|
54
|
-
}
|