skillfree 0.1.3 โ†’ 0.1.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillfree",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "๐Ÿฆž ไธ€ไธช API๏ผŒๆปก่ถณๆ‰€ๆœ‰้พ™่™พๆŠ€่ƒฝ้œ€ๆฑ‚",
5
5
  "main": "bin/skillfree.js",
6
6
  "bin": {
@@ -1,100 +1,271 @@
1
- const { post, postStream, BASE_URL } = require('../lib/client')
1
+ const { post, postStream, get, request, getApiKey, BASE_URL } = require('../lib/client')
2
2
  const fs = require('fs')
3
3
  const path = require('path')
4
4
 
5
+ // โ”€โ”€โ”€ ๅทฅๅ…ท๏ผšไธ‹่ฝฝๆ–‡ไปถๅนถไฟๅญ˜ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
6
+ async function downloadAndSave(url, output) {
7
+ const resp = await fetch(url)
8
+ if (!resp.ok) throw new Error(`ไธ‹่ฝฝๅคฑ่ดฅ: ${resp.status}`)
9
+ fs.writeFileSync(output, Buffer.from(await resp.arrayBuffer()))
10
+ console.log(`โœ… ๅทฒไฟๅญ˜ๅˆฐ ${output}`)
11
+ }
12
+
13
+ // โ”€โ”€โ”€ ๅทฅๅ…ท๏ผšๅฐ่ฃ… PCM ไธบ WAV โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
14
+ function pcmToWav(pcmBytes, sampleRate = 24000, channels = 1, bitsPerSample = 16) {
15
+ const dataSize = pcmBytes.length
16
+ const wav = Buffer.alloc(44 + dataSize)
17
+ wav.write('RIFF', 0); wav.writeUInt32LE(36 + dataSize, 4); wav.write('WAVE', 8)
18
+ wav.write('fmt ', 12); wav.writeUInt32LE(16, 16); wav.writeUInt16LE(1, 20)
19
+ wav.writeUInt16LE(channels, 22); wav.writeUInt32LE(sampleRate, 24)
20
+ wav.writeUInt32LE(sampleRate * channels * bitsPerSample / 8, 28)
21
+ wav.writeUInt16LE(channels * bitsPerSample / 8, 32); wav.writeUInt16LE(bitsPerSample, 34)
22
+ wav.write('data', 36); wav.writeUInt32LE(dataSize, 40)
23
+ pcmBytes.copy(wav, 44)
24
+ return wav
25
+ }
26
+
5
27
  async function pilot(flags) {
6
28
  const type = flags.type || 'chat'
7
29
  const prompt = flags.prompt || flags.text || ''
8
30
  const output = flags.output || null
31
+ const model = flags.model || null
9
32
 
10
- // ๆž„้€ ่ฏทๆฑ‚ไฝ“
11
- const body = { type, inputs: {} }
12
-
13
- switch (type) {
14
- case 'chat':
15
- body.model = flags.model
16
- body.inputs.messages = [{ role: 'user', content: prompt }]
17
- break
18
- case 'image':
19
- body.model = flags.model
20
- body.inputs.prompt = prompt
21
- if (flags.size) body.inputs.size = flags.size
22
- break
23
- case 'tts':
24
- body.model = flags.model
25
- body.inputs.text = prompt
26
- body.inputs.input = prompt
27
- body.inputs.voice = flags.voice || 'nova'
28
- break
29
- case 'stt':
30
- if (flags.file) {
31
- body.inputs.audio_data = fs.readFileSync(path.resolve(flags.file)).toString('base64')
32
- body.inputs.filename = path.basename(flags.file)
33
+ // โ”€โ”€ CHAT โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
34
+ if (type === 'chat') {
35
+ if (!output) {
36
+ // ๆตๅผ่พ“ๅ‡บ
37
+ const res = await postStream('/chat/completions', {
38
+ model: model || 'DeepSeek-V3.2-Fast',
39
+ messages: [{ role: 'user', content: prompt }],
40
+ stream: true,
41
+ })
42
+ const reader = res.body.getReader()
43
+ const decoder = new TextDecoder()
44
+ let buf = ''
45
+ process.stdout.write('\n')
46
+ while (true) {
47
+ const { done, value } = await reader.read()
48
+ if (done) break
49
+ buf += decoder.decode(value, { stream: true })
50
+ const lines = buf.split('\n')
51
+ buf = lines.pop()
52
+ for (const line of lines) {
53
+ if (line.startsWith('data: ')) {
54
+ const chunk = line.slice(6)
55
+ if (chunk === '[DONE]') { process.stdout.write('\n'); return }
56
+ try {
57
+ const d = JSON.parse(chunk)
58
+ const text = d.choices?.[0]?.delta?.content
59
+ if (text) process.stdout.write(text)
60
+ } catch {}
61
+ }
62
+ }
33
63
  }
34
- break
35
- case 'video':
36
- body.model = flags.model
37
- body.inputs.prompt = prompt
38
- body.inputs.duration = flags.duration ? parseInt(flags.duration) : 5
39
- break
40
- case 'music':
41
- body.model = flags.model
42
- body.inputs.prompt = prompt
43
- break
44
- default:
45
- body.inputs.prompt = prompt
64
+ return
65
+ }
66
+ const result = await post('/chat/completions', {
67
+ model: model || 'DeepSeek-V3.2-Fast',
68
+ messages: [{ role: 'user', content: prompt }],
69
+ })
70
+ const text = result.choices?.[0]?.message?.content || JSON.stringify(result, null, 2)
71
+ if (output) { fs.writeFileSync(output, text); console.log(`โœ… ๅทฒไฟๅญ˜ๅˆฐ ${output}`) }
72
+ else console.log(text)
73
+ return
46
74
  }
47
75
 
48
- // ๆตๅผ่พ“ๅ‡บ๏ผˆchat ้ป˜่ฎคๆตๅผ๏ผ‰
49
- if (type === 'chat' && !output) {
50
- const res = await postStream('/chat/completions', {
51
- model: flags.model || 'DeepSeek-V3.2-Fast',
52
- messages: [{ role: 'user', content: prompt }],
53
- stream: true,
76
+ // โ”€โ”€ IMAGE โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
77
+ if (type === 'image') {
78
+ const res = await post('/images/generations', {
79
+ model: model || 'gemini-3.1-flash-image-preview',
80
+ prompt,
81
+ n: 1,
82
+ size: flags.size || '1024x1024',
54
83
  })
84
+ if (res.error) throw new Error(res.error.message || JSON.stringify(res.error))
85
+ const url = res.data?.[0]?.url || res.data?.[0]?.b64_json
86
+ if (!url) throw new Error('ๆœช่ฟ”ๅ›žๅ›พๅƒๆ•ฐๆฎ: ' + JSON.stringify(res).slice(0, 200))
87
+ if (output) {
88
+ if (url.startsWith('http')) {
89
+ await downloadAndSave(url, output)
90
+ } else {
91
+ fs.writeFileSync(output, Buffer.from(url, 'base64'))
92
+ console.log(`โœ… ๅทฒไฟๅญ˜ๅˆฐ ${output}`)
93
+ }
94
+ } else {
95
+ console.log('ๅ›พๅƒ URL:', url)
96
+ }
97
+ return
98
+ }
55
99
 
56
- const reader = res.body.getReader()
57
- const decoder = new TextDecoder()
58
- let buf = ''
59
- process.stdout.write('\n')
60
- while (true) {
61
- const { done, value } = await reader.read()
62
- if (done) break
63
- buf += decoder.decode(value, { stream: true })
64
- const lines = buf.split('\n')
65
- buf = lines.pop()
66
- for (const line of lines) {
67
- if (line.startsWith('data: ')) {
68
- const chunk = line.slice(6)
69
- if (chunk === '[DONE]') { process.stdout.write('\n'); return }
70
- try {
71
- const d = JSON.parse(chunk)
72
- const text = d.choices?.[0]?.delta?.content
73
- if (text) process.stdout.write(text)
74
- } catch {}
75
- }
100
+ // โ”€โ”€ TTS โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
101
+ if (type === 'tts') {
102
+ const ttsModel = model || 'speech-2.6-hd'
103
+ const text = prompt
104
+
105
+ if (ttsModel === 'speech-2.8-hd' || ttsModel === 'minimax-clone-lastversion') {
106
+ // ่ตฐ /v1/responses๏ผŒ่ฟ”ๅ›ž hex ้Ÿณ้ข‘
107
+ const res = await request('/responses', {
108
+ method: 'POST',
109
+ body: JSON.stringify({
110
+ model: ttsModel,
111
+ input: text,
112
+ stream: false,
113
+ voice_setting: {
114
+ voice_id: flags.voice || 'female-shaonv',
115
+ speed: 1, vol: 1, pitch: 0, emotion: 'fluent',
116
+ },
117
+ audio_setting: { sample_rate: 32000, bitrate: 128000, format: 'mp3', channel: 1 },
118
+ output_format: 'hex',
119
+ }),
120
+ })
121
+ const data = await res.json()
122
+ if (!data.data?.audio) throw new Error(JSON.stringify(data).slice(0, 200))
123
+ const audioBuf = Buffer.from(data.data.audio, 'hex')
124
+ if (output) { fs.writeFileSync(output, audioBuf); console.log(`โœ… ๅทฒไฟๅญ˜ๅˆฐ ${output}๏ผˆ${audioBuf.length} bytes๏ผ‰`) }
125
+ else console.log(`โœ… TTS ๆˆๅŠŸ๏ผŒๆ—ถ้•ฟ็บฆ ${(data.extra_info?.audio_length / 1000).toFixed(1)} ็ง’`)
126
+
127
+ } else if (ttsModel === 'gemini-2.5-pro-preview-tts' || ttsModel === 'gemini-2.5-flash-preview-tts') {
128
+ // Gemini TTS๏ผš่ตฐ /v1beta/models/:id:generateContent๏ผŒ่ฎค่ฏๆ—  Bearer
129
+ const apiKey = getApiKey()
130
+ const res = await fetch(BASE_URL.replace('skillfree.tech', 'dmxapi.cn') + '/v1beta/models/' + ttsModel + ':generateContent', {
131
+ method: 'POST',
132
+ headers: { 'Authorization': apiKey, 'Content-Type': 'application/json' },
133
+ body: JSON.stringify({
134
+ contents: [{ parts: [{ text }] }],
135
+ generationConfig: {
136
+ responseModalities: ['AUDIO'],
137
+ speechConfig: {
138
+ voiceConfig: { prebuiltVoiceConfig: { voiceName: flags.voice || 'Kore' } }
139
+ }
140
+ }
141
+ }),
142
+ })
143
+ const data = await res.json()
144
+ if (data.error) throw new Error(data.error.message || JSON.stringify(data.error))
145
+ const pcm = Buffer.from(data.candidates[0].content.parts[0].inlineData.data, 'base64')
146
+ const ext = (output || '').endsWith('.mp3') ? 'mp3' : 'wav'
147
+ const finalBuf = ext === 'wav' ? pcmToWav(pcm) : pcm
148
+ if (output) { fs.writeFileSync(output, finalBuf); console.log(`โœ… ๅทฒไฟๅญ˜ๅˆฐ ${output}๏ผˆ${finalBuf.length} bytes๏ผ‰`) }
149
+ else console.log(`โœ… Gemini TTS ๆˆๅŠŸ๏ผŒๆ—ถ้•ฟ็บฆ ${(pcm.length / (24000 * 2)).toFixed(1)} ็ง’`)
150
+
151
+ } else {
152
+ // ๆ ‡ๅ‡† OpenAI ๅ…ผๅฎน๏ผˆspeech-2.6-hd ็ญ‰๏ผ‰
153
+ const res = await request('/audio/speech', {
154
+ method: 'POST',
155
+ body: JSON.stringify({
156
+ model: ttsModel,
157
+ input: text,
158
+ voice: flags.voice || 'female-shaonv',
159
+ }),
160
+ })
161
+ if (res.status !== 200) {
162
+ const err = await res.json()
163
+ throw new Error(err.error?.message || JSON.stringify(err))
76
164
  }
165
+ const buf = Buffer.from(await res.arrayBuffer())
166
+ if (output) { fs.writeFileSync(output, buf); console.log(`โœ… ๅทฒไฟๅญ˜ๅˆฐ ${output}๏ผˆ${buf.length} bytes๏ผ‰`) }
167
+ else console.log(`โœ… TTS ๆˆๅŠŸ๏ผŒ${buf.length} bytes`)
77
168
  }
78
169
  return
79
170
  }
80
171
 
81
- // ้žๆตๅผ
82
- const result = await post('/pilot', body)
172
+ // โ”€โ”€ MUSIC โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
173
+ if (type === 'music') {
174
+ const musicModel = model || 'chirp-v5'
175
+
176
+ if (musicModel === 'chirp-v5') {
177
+ // Suno ๅผ‚ๆญฅๆŽฅๅฃ
178
+ const res = await request('/suno/submit/music', {
179
+ method: 'POST',
180
+ headers: { 'Accept': 'application/json' },
181
+ body: JSON.stringify({
182
+ gpt_description_prompt: prompt,
183
+ make_instrumental: true,
184
+ mv: 'chirp-v5',
185
+ notify_hook: '',
186
+ }),
187
+ })
188
+ const submitData = await res.json()
189
+ if (submitData.code !== 'success') throw new Error(submitData.message || JSON.stringify(submitData))
190
+ const taskId = submitData.data
191
+ console.log(`๐ŸŽต Suno ไปปๅŠกๅทฒๆไบค๏ผŒtask_id: ${taskId}๏ผŒ็ญ‰ๅพ…็”Ÿๆˆ๏ผˆ็บฆ 60-90 ็ง’๏ผ‰...`)
192
+
193
+ // ่ฝฎ่ฏข็ป“ๆžœ
194
+ for (let i = 0; i < 15; i++) {
195
+ await new Promise(r => setTimeout(r, 10000))
196
+ const poll = await request('/suno/fetch/' + taskId, { method: 'GET' })
197
+ const result = await poll.json()
198
+ if (result.code !== 'success') throw new Error(result.message || JSON.stringify(result))
199
+ const status = result.data?.status
200
+ const progress = result.data?.progress || '0%'
201
+ process.stdout.write(`\r็Šถๆ€: ${status} ่ฟ›ๅบฆ: ${progress} `)
202
+ if (status === 'SUCCESS') {
203
+ const songs = result.data?.data || []
204
+ process.stdout.write('\n')
205
+ console.log(`โœ… ็”ŸๆˆๆˆๅŠŸ๏ผๅ…ฑ ${songs.length} ้ฆ–`)
206
+ songs.forEach((s, i) => {
207
+ console.log(` ๆญŒๆ›ฒ${i+1}: ${s.title} (${Math.floor(s.duration/60)}m${Math.floor(s.duration%60)}s)`)
208
+ console.log(` ้Ÿณ้ข‘: ${s.audio_url}`)
209
+ })
210
+ // ๅฆ‚ๆžœๆŒ‡ๅฎš output๏ผŒไธ‹่ฝฝ็ฌฌไธ€้ฆ–
211
+ if (output && songs[0]?.audio_url) await downloadAndSave(songs[0].audio_url, output)
212
+ return
213
+ }
214
+ if (status === 'FAILED') throw new Error('Suno ไปปๅŠกๅคฑ่ดฅ: ' + JSON.stringify(result.data))
215
+ }
216
+ throw new Error('Suno ็”Ÿๆˆ่ถ…ๆ—ถ๏ผˆ150s๏ผ‰๏ผŒ่ฏทๆ‰‹ๅŠจๆŸฅ่ฏข task_id: ' + taskId)
83
217
 
84
- // ๅฆ‚ๆžœๆœ‰ๅช’ไฝ“ URL ไธ”ๆŒ‡ๅฎšไบ†่พ“ๅ‡บๆ–‡ไปถ๏ผŒไธ‹่ฝฝไฟๅญ˜
85
- const mediaUrl = result.image_url || result.video_url || result.audio_url
86
- || result.data?.[0]?.url || result.data?.[0]
87
- if (output && mediaUrl) {
88
- const resp = await fetch(typeof mediaUrl === 'string' ? mediaUrl : mediaUrl.url)
89
- fs.writeFileSync(output, Buffer.from(await resp.arrayBuffer()))
90
- console.log(`โœ… ๅทฒไฟๅญ˜ๅˆฐ ${output}`)
218
+ } else {
219
+ // music-2.5๏ผˆMiniMax๏ผ‰ๆš‚ไธๆ”ฏๆŒ๏ผˆ้œ€่ฆ lyrics ๅ‚ๆ•ฐ๏ผŒๅปบ่ฎฎ็›ดๆŽฅ่ฐƒ /v1/responses๏ผ‰
220
+ throw new Error(`music-2.5 ้œ€่ฆ้€š่ฟ‡ /v1/responses ็›ดๆŽฅ่ฐƒ็”จ๏ผŒ่ฏทๅ‚่€ƒๆ–‡ๆกฃ`)
221
+ }
222
+ }
223
+
224
+ // โ”€โ”€ OCR โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
225
+ if (type === 'ocr') {
226
+ let input = prompt
227
+ // ๅฆ‚ๆžœๆ˜ฏๆœฌๅœฐๆ–‡ไปถ่ทฏๅพ„๏ผŒ่ฏปๅ–ไธบ base64
228
+ if (flags.file && fs.existsSync(path.resolve(flags.file))) {
229
+ input = fs.readFileSync(path.resolve(flags.file)).toString('base64')
230
+ }
231
+ const res = await request('/responses', {
232
+ method: 'POST',
233
+ body: JSON.stringify({
234
+ model: model || 'hehe-tywd',
235
+ input,
236
+ parse_mode: 'auto',
237
+ dpi: 144,
238
+ page_start: 0,
239
+ page_count: 1000,
240
+ markdown_details: 1,
241
+ page_details: 0,
242
+ table_flavor: 'html',
243
+ }),
244
+ })
245
+ const data = await res.json()
246
+ if (data.code !== 200) throw new Error(data.message || JSON.stringify(data).slice(0, 200))
247
+ const markdown = data.result?.markdown || ''
248
+ if (output) { fs.writeFileSync(output, markdown); console.log(`โœ… OCR ็ป“ๆžœๅทฒไฟๅญ˜ๅˆฐ ${output}`) }
249
+ else console.log(markdown)
250
+ return
251
+ }
252
+
253
+ // โ”€โ”€ STT โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
254
+ if (type === 'stt') {
255
+ if (!flags.file) throw new Error('--file ๆ˜ฏๅฟ…้œ€็š„๏ผˆ้Ÿณ้ข‘ๆ–‡ไปถ่ทฏๅพ„๏ผ‰')
256
+ const audioBase64 = fs.readFileSync(path.resolve(flags.file)).toString('base64')
257
+ const result = await post('/v1/audio/transcriptions', {
258
+ model: 'whisper-1',
259
+ file: audioBase64,
260
+ filename: path.basename(flags.file),
261
+ })
262
+ const text = result.text || JSON.stringify(result, null, 2)
263
+ if (output) { fs.writeFileSync(output, text); console.log(`โœ… ๅทฒไฟๅญ˜ๅˆฐ ${output}`) }
264
+ else console.log(text)
91
265
  return
92
266
  }
93
267
 
94
- // ๆ–‡ๆœฌ่พ“ๅ‡บ
95
- const text = result.choices?.[0]?.message?.content
96
- || result.text || result.transcript || JSON.stringify(result, null, 2)
97
- console.log(text)
268
+ throw new Error(`ไธๆ”ฏๆŒ็š„็ฑปๅž‹: ${type}๏ผŒๅฏ้€‰: chat | image | tts | stt | music | ocr | video`)
98
269
  }
99
270
 
100
271
  module.exports = { pilot }
Binary file