skillfree 0.1.25 โ 0.1.37
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/SKILL.md +100 -147
- package/bin/skillfree.js +27 -78
- package/install.sh +32 -15
- package/package.json +1 -1
- package/scripts/commands/auth.js +29 -20
- package/scripts/commands/models.js +21 -13
- package/scripts/commands/pilot.js +43 -320
- package/scripts/commands/video.js +14 -90
- package/skillfree-0.1.37.tgz +0 -0
- package/scripts/commands/music.js +0 -28
- package/scripts/commands/stt.js +0 -47
- package/scripts/commands/tts.js +0 -67
|
@@ -10,27 +10,35 @@ async function listModels(type) {
|
|
|
10
10
|
const t = m.type || 'chat'
|
|
11
11
|
if (type && t !== type) continue
|
|
12
12
|
if (!groups[t]) groups[t] = []
|
|
13
|
-
groups[t].push(m
|
|
13
|
+
groups[t].push(m)
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
const typeLabel = {
|
|
17
|
-
chat:
|
|
18
|
-
image:
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
ocr: '๐ OCR',
|
|
23
|
-
embedding: '๐ข Embedding',
|
|
17
|
+
chat: '๐ฌ ๅฏน่ฏ',
|
|
18
|
+
image: '๐จ ๅพๅ',
|
|
19
|
+
video: '๐ฌ ่ง้ข',
|
|
20
|
+
ocr: '๐ OCR ่งฃๆ',
|
|
21
|
+
search: '๐ ๆ็ดข',
|
|
24
22
|
}
|
|
25
23
|
|
|
26
|
-
console.log('\n๐ฆ SkillFree
|
|
27
|
-
for (const [t,
|
|
24
|
+
console.log('\n๐ฆ SkillFree ไธฅ้ๆจกๅ\n')
|
|
25
|
+
for (const [t, models] of Object.entries(groups)) {
|
|
28
26
|
console.log(`${typeLabel[t] || t}`)
|
|
29
|
-
|
|
27
|
+
for (const m of models) {
|
|
28
|
+
let price = ''
|
|
29
|
+
if (m.tokenBased) {
|
|
30
|
+
price = `่พๅ
ฅ ยฅ${((m.inputPrice || 0) / 100).toFixed(0)}/1M ยท ่พๅบ ยฅ${((m.outputPrice || 0) / 100).toFixed(0)}/1M`
|
|
31
|
+
} else if (m.pricePerSec) {
|
|
32
|
+
price = `720p ยฅ${((m.pricePerSec['1280x720'] || 0) / 100).toFixed(1)}/s ยท 1080p ยฅ${((m.pricePerSec['1920x1080'] || 0) / 100).toFixed(1)}/s ยท 4K ยฅ${((m.pricePerSec['3840x2160'] || 0) / 100).toFixed(1)}/s`
|
|
33
|
+
} else if (m.estimatedCredits) {
|
|
34
|
+
price = `ยฅ${(m.estimatedCredits / 100).toFixed(2)}/ๆฌก`
|
|
35
|
+
}
|
|
36
|
+
const label = m.label ? ` ${m.label}` : ''
|
|
37
|
+
console.log(` ${m.id}${label ? ' (' + m.label + ')' : ''} ${price}`)
|
|
38
|
+
}
|
|
30
39
|
console.log()
|
|
31
40
|
}
|
|
32
|
-
console.log(
|
|
33
|
-
console.log(`\n็จๆณ็คบไพ๏ผskillfree pilot --type chat --model DeepSeek-V3.2-Fast --prompt "ไฝ ๅฅฝ"`)
|
|
41
|
+
console.log(`\n็จๆณ๏ผskillfree pilot --type chat --model claude-sonnet-4-6 --prompt "ไฝ ๅฅฝ"`)
|
|
34
42
|
}
|
|
35
43
|
|
|
36
44
|
module.exports = { listModels }
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const { post, postStream,
|
|
1
|
+
const { post, postStream, request, getApiKey, BASE_URL, checkCredits } = require('../lib/client')
|
|
2
2
|
const fs = require('fs')
|
|
3
3
|
const path = require('path')
|
|
4
4
|
|
|
@@ -10,35 +10,23 @@ async function downloadAndSave(url, output) {
|
|
|
10
10
|
console.log(`โ
ๅทฒไฟๅญๅฐ ${output}`)
|
|
11
11
|
}
|
|
12
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
|
-
|
|
27
13
|
async function pilot(flags) {
|
|
28
|
-
const type
|
|
14
|
+
const type = flags.type || 'chat'
|
|
29
15
|
const prompt = flags.prompt || flags.text || ''
|
|
30
16
|
const output = flags.output || null
|
|
31
|
-
const model
|
|
17
|
+
const model = flags.model || null
|
|
32
18
|
|
|
33
|
-
// โโ
|
|
19
|
+
// โโ ไฝ้ข้ขๆฃ๏ผ< 100 ็งฏๅๆถๆฆๆชๅนถๆ็คบๅ
ๅผ๏ผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
34
20
|
await checkCredits()
|
|
35
21
|
|
|
36
22
|
// โโ CHAT โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
37
23
|
if (type === 'chat') {
|
|
24
|
+
const chatModel = model || 'claude-sonnet-4-6'
|
|
25
|
+
|
|
38
26
|
if (!output) {
|
|
39
27
|
// ๆตๅผ่พๅบ
|
|
40
28
|
const res = await postStream('/chat/completions', {
|
|
41
|
-
model:
|
|
29
|
+
model: chatModel,
|
|
42
30
|
messages: [{ role: 'user', content: prompt }],
|
|
43
31
|
stream: true,
|
|
44
32
|
})
|
|
@@ -66,13 +54,14 @@ async function pilot(flags) {
|
|
|
66
54
|
}
|
|
67
55
|
return
|
|
68
56
|
}
|
|
57
|
+
|
|
69
58
|
const result = await post('/chat/completions', {
|
|
70
|
-
model:
|
|
59
|
+
model: chatModel,
|
|
71
60
|
messages: [{ role: 'user', content: prompt }],
|
|
72
61
|
})
|
|
73
62
|
const text = result.choices?.[0]?.message?.content || JSON.stringify(result, null, 2)
|
|
74
|
-
|
|
75
|
-
|
|
63
|
+
fs.writeFileSync(output, text)
|
|
64
|
+
console.log(`โ
ๅทฒไฟๅญๅฐ ${output}`)
|
|
76
65
|
return
|
|
77
66
|
}
|
|
78
67
|
|
|
@@ -80,26 +69,6 @@ async function pilot(flags) {
|
|
|
80
69
|
if (type === 'image') {
|
|
81
70
|
const imageModel = model || 'gemini-3.1-flash-image-preview'
|
|
82
71
|
|
|
83
|
-
// qwen-image-edit-plus๏ผๅพ็็ผ่พ๏ผ่ตฐ /v1/images/edits๏ผmultipart/form-data
|
|
84
|
-
if (imageModel.startsWith('qwen-image')) {
|
|
85
|
-
if (!flags.file) throw new Error('qwen-image-edit ้่ฆ --file ๆๅฎๅๅงๅพ็่ทฏๅพ')
|
|
86
|
-
const imgBuf = fs.readFileSync(path.resolve(flags.file))
|
|
87
|
-
const form = new FormData()
|
|
88
|
-
form.append('model', imageModel)
|
|
89
|
-
form.append('prompt', prompt)
|
|
90
|
-
form.append('size', flags.size || '1024x1024')
|
|
91
|
-
form.append('image', new Blob([imgBuf], { type: 'image/png' }), path.basename(flags.file))
|
|
92
|
-
const res = await request('/images/edits', { method: 'POST', body: form })
|
|
93
|
-
const data = await res.json()
|
|
94
|
-
if (data.error) throw new Error(data.error.message || JSON.stringify(data.error))
|
|
95
|
-
const url = data.data?.[0]?.url
|
|
96
|
-
if (!url) throw new Error('ๆช่ฟๅๅพๅURL: ' + JSON.stringify(data).slice(0, 200))
|
|
97
|
-
if (output) await downloadAndSave(url, output)
|
|
98
|
-
else console.log('ๅพๅ URL:', url)
|
|
99
|
-
return
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// doubao-seedream-5.0-lite / ๅ
ถไปๆ ๅๅพๅๆจกๅ
|
|
103
72
|
const res = await post('/images/generations', {
|
|
104
73
|
model: imageModel,
|
|
105
74
|
prompt,
|
|
@@ -107,8 +76,10 @@ async function pilot(flags) {
|
|
|
107
76
|
size: flags.size || '1024x1024',
|
|
108
77
|
})
|
|
109
78
|
if (res.error) throw new Error(res.error.message || JSON.stringify(res.error))
|
|
79
|
+
|
|
110
80
|
const url = res.data?.[0]?.url || res.data?.[0]?.b64_json
|
|
111
81
|
if (!url) throw new Error('ๆช่ฟๅๅพๅๆฐๆฎ: ' + JSON.stringify(res).slice(0, 200))
|
|
82
|
+
|
|
112
83
|
if (output) {
|
|
113
84
|
if (url.startsWith('http')) await downloadAndSave(url, output)
|
|
114
85
|
else { fs.writeFileSync(output, Buffer.from(url, 'base64')); console.log(`โ
ๅทฒไฟๅญๅฐ ${output}`) }
|
|
@@ -118,156 +89,48 @@ async function pilot(flags) {
|
|
|
118
89
|
return
|
|
119
90
|
}
|
|
120
91
|
|
|
121
|
-
// โโ
|
|
122
|
-
if (type === '
|
|
123
|
-
|
|
124
|
-
const
|
|
92
|
+
// โโ VIDEO๏ผVeo ๅผๆญฅ่ฝฎ่ฏข๏ผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
93
|
+
if (type === 'video') {
|
|
94
|
+
if (!prompt) throw new Error('--prompt ๆฏๅฟ
้็๏ผ่ง้ขๆ่ฟฐ๏ผ')
|
|
95
|
+
const videoModel = model || 'veo-3.1-fast-generate-preview'
|
|
96
|
+
const seconds = flags.seconds || '8'
|
|
97
|
+
const size = flags.size || '1920x1080'
|
|
125
98
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
const res = await request('/responses', {
|
|
129
|
-
method: 'POST',
|
|
130
|
-
body: JSON.stringify({
|
|
131
|
-
model: ttsModel,
|
|
132
|
-
input: text,
|
|
133
|
-
stream: false,
|
|
134
|
-
voice_setting: {
|
|
135
|
-
voice_id: flags.voice || 'female-shaonv',
|
|
136
|
-
speed: 1, vol: 1, pitch: 0, emotion: 'fluent',
|
|
137
|
-
},
|
|
138
|
-
audio_setting: { sample_rate: 32000, bitrate: 128000, format: 'mp3', channel: 1 },
|
|
139
|
-
output_format: 'hex',
|
|
140
|
-
}),
|
|
141
|
-
})
|
|
142
|
-
const data = await res.json()
|
|
143
|
-
if (!data.data?.audio) throw new Error(JSON.stringify(data).slice(0, 200))
|
|
144
|
-
const audioBuf = Buffer.from(data.data.audio, 'hex')
|
|
145
|
-
if (output) { fs.writeFileSync(output, audioBuf); console.log(`โ
ๅทฒไฟๅญๅฐ ${output}๏ผ${audioBuf.length} bytes๏ผ`) }
|
|
146
|
-
else console.log(`โ
TTS ๆๅ๏ผๆถ้ฟ็บฆ ${(data.extra_info?.audio_length / 1000).toFixed(1)} ็ง`)
|
|
99
|
+
console.log(`๐ฌ ๆไบค Veo ่ง้ขไปปๅก๏ผ${videoModel}๏ผ${size}๏ผ${seconds}s๏ผ...`)
|
|
100
|
+
console.log('โณ ่ง้ข็ๆไธญ๏ผ้ข่ฎก้่ฆ 1-3 ๅ้...\n')
|
|
147
101
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' },
|
|
155
|
-
body: JSON.stringify({
|
|
156
|
-
contents: [{ parts: [{ text }] }],
|
|
157
|
-
generationConfig: {
|
|
158
|
-
responseModalities: ['AUDIO'],
|
|
159
|
-
speechConfig: {
|
|
160
|
-
voiceConfig: { prebuiltVoiceConfig: { voiceName: flags.voice || 'Kore' } }
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}),
|
|
164
|
-
})
|
|
165
|
-
const data = await res.json()
|
|
166
|
-
if (data.error) throw new Error(data.error.message || JSON.stringify(data.error))
|
|
167
|
-
const pcm = Buffer.from(data.candidates[0].content.parts[0].inlineData.data, 'base64')
|
|
168
|
-
const ext = (output || '').endsWith('.mp3') ? 'mp3' : 'wav'
|
|
169
|
-
const finalBuf = ext === 'wav' ? pcmToWav(pcm) : pcm
|
|
170
|
-
if (output) { fs.writeFileSync(output, finalBuf); console.log(`โ
ๅทฒไฟๅญๅฐ ${output}๏ผ${finalBuf.length} bytes๏ผ`) }
|
|
171
|
-
else console.log(`โ
Gemini TTS ๆๅ๏ผๆถ้ฟ็บฆ ${(pcm.length / (24000 * 2)).toFixed(1)} ็ง`)
|
|
102
|
+
const apiKey = getApiKey()
|
|
103
|
+
const res = await fetch(`${BASE_URL}/v1/video/veo`, {
|
|
104
|
+
method: 'POST',
|
|
105
|
+
headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' },
|
|
106
|
+
body: JSON.stringify({ model: videoModel, input: prompt, seconds, size }),
|
|
107
|
+
})
|
|
172
108
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
method: 'POST',
|
|
177
|
-
body: JSON.stringify({
|
|
178
|
-
model: ttsModel,
|
|
179
|
-
input: text,
|
|
180
|
-
voice: flags.voice || 'female-shaonv',
|
|
181
|
-
}),
|
|
182
|
-
})
|
|
183
|
-
if (res.status !== 200) {
|
|
184
|
-
const err = await res.json()
|
|
185
|
-
throw new Error(err.error?.message || JSON.stringify(err))
|
|
186
|
-
}
|
|
187
|
-
const buf = Buffer.from(await res.arrayBuffer())
|
|
188
|
-
if (output) { fs.writeFileSync(output, buf); console.log(`โ
ๅทฒไฟๅญๅฐ ${output}๏ผ${buf.length} bytes๏ผ`) }
|
|
189
|
-
else console.log(`โ
TTS ๆๅ๏ผ${buf.length} bytes`)
|
|
109
|
+
if (!res.ok) {
|
|
110
|
+
const err = await res.json().catch(() => ({}))
|
|
111
|
+
throw new Error(err.error || `HTTP ${res.status}`)
|
|
190
112
|
}
|
|
191
|
-
return
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// โโ MUSIC โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
195
|
-
if (type === 'music') {
|
|
196
|
-
const musicModel = model || 'chirp-v5'
|
|
197
|
-
|
|
198
|
-
if (musicModel === 'chirp-v5') {
|
|
199
|
-
// Suno ๅผๆญฅๆฅๅฃ๏ผ่ทฏ็ฑๅจ /suno/๏ผไธๅจ /v1/ ไธ๏ผ้็จ BASE_URL ็ดๆฅ่ฏทๆฑ๏ผ
|
|
200
|
-
const apiKey = getApiKey()
|
|
201
|
-
const res = await fetch(`${BASE_URL}/suno/submit/music`, {
|
|
202
|
-
method: 'POST',
|
|
203
|
-
headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', 'Authorization': `Bearer ${apiKey}` },
|
|
204
|
-
body: JSON.stringify({
|
|
205
|
-
gpt_description_prompt: prompt,
|
|
206
|
-
make_instrumental: true,
|
|
207
|
-
mv: 'chirp-v5',
|
|
208
|
-
notify_hook: '',
|
|
209
|
-
}),
|
|
210
|
-
})
|
|
211
|
-
const submitData = await res.json()
|
|
212
|
-
if (submitData.code !== 'success') throw new Error(submitData.message || JSON.stringify(submitData))
|
|
213
|
-
const taskId = submitData.data
|
|
214
|
-
console.log(`๐ต Suno ไปปๅกๅทฒๆไบค๏ผtask_id: ${taskId}๏ผ็ญๅพ
็ๆ๏ผ็บฆ 60-90 ็ง๏ผ...`)
|
|
215
113
|
|
|
216
|
-
|
|
217
|
-
for (let i = 0; i < 15; i++) {
|
|
218
|
-
await new Promise(r => setTimeout(r, 10000))
|
|
219
|
-
const poll = await fetch(`${BASE_URL}/suno/fetch/${taskId}`, {
|
|
220
|
-
headers: { 'Authorization': `Bearer ${apiKey}` }
|
|
221
|
-
})
|
|
222
|
-
const result = await poll.json()
|
|
223
|
-
if (result.code !== 'success') throw new Error(result.message || JSON.stringify(result))
|
|
224
|
-
const status = result.data?.status
|
|
225
|
-
const progress = result.data?.progress || '0%'
|
|
226
|
-
process.stdout.write(`\r็ถๆ: ${status} ่ฟๅบฆ: ${progress} `)
|
|
227
|
-
if (status === 'SUCCESS') {
|
|
228
|
-
const songs = result.data?.data || []
|
|
229
|
-
process.stdout.write('\n')
|
|
230
|
-
console.log(`โ
็ๆๆๅ๏ผๅ
ฑ ${songs.length} ้ฆ`)
|
|
231
|
-
songs.forEach((s, i) => {
|
|
232
|
-
console.log(` ๆญๆฒ${i+1}: ${s.title} (${Math.floor(s.duration/60)}m${Math.floor(s.duration%60)}s)`)
|
|
233
|
-
console.log(` ้ณ้ข: ${s.audio_url}`)
|
|
234
|
-
})
|
|
235
|
-
// ๅฆๆๆๅฎ output๏ผไธ่ฝฝ็ฌฌไธ้ฆ
|
|
236
|
-
if (output && songs[0]?.audio_url) await downloadAndSave(songs[0].audio_url, output)
|
|
237
|
-
return
|
|
238
|
-
}
|
|
239
|
-
if (status === 'FAILED') throw new Error('Suno ไปปๅกๅคฑ่ดฅ: ' + JSON.stringify(result.data))
|
|
240
|
-
}
|
|
241
|
-
throw new Error(`Suno ็ๆ่ถ
ๆถ๏ผ150s๏ผ๏ผไปปๅกไปๅจๅๅฐ่ฟ่ก\n๐ ็จๅๆๅจๆฅ่ฏข๏ผskillfree task suno:${taskId}`)
|
|
114
|
+
const data = await res.json()
|
|
242
115
|
|
|
116
|
+
// ๆๅก็ซฏๅทฒ่ฝฎ่ฏขๅฎๆ๏ผ่ฟๅ video_base64 ๆ url
|
|
117
|
+
if (data.video_base64) {
|
|
118
|
+
const videoBytes = Buffer.from(data.video_base64, 'base64')
|
|
119
|
+
const savePath = output || `./veo_${Date.now()}.mp4`
|
|
120
|
+
fs.writeFileSync(savePath, videoBytes)
|
|
121
|
+
console.log(`โ
่ง้ขๅทฒไฟๅญๅฐ ${savePath}๏ผ${(videoBytes.length / 1024 / 1024).toFixed(1)} MB๏ผ`)
|
|
122
|
+
} else if (data.url) {
|
|
123
|
+
if (output) await downloadAndSave(data.url, output)
|
|
124
|
+
else console.log('๐ ่ง้ข้พๆฅ:', data.url)
|
|
243
125
|
} else {
|
|
244
|
-
|
|
245
|
-
const res = await request('/responses', {
|
|
246
|
-
method: 'POST',
|
|
247
|
-
body: JSON.stringify({
|
|
248
|
-
model: 'music-2.5',
|
|
249
|
-
input: prompt,
|
|
250
|
-
lyrics: flags.lyrics || `[verse]\n${prompt}`,
|
|
251
|
-
audio_setting: { sample_rate: 44100, bitrate: 256000, format: 'mp3' },
|
|
252
|
-
output_format: 'url',
|
|
253
|
-
stream: false,
|
|
254
|
-
}),
|
|
255
|
-
})
|
|
256
|
-
const data = await res.json()
|
|
257
|
-
if (data.error) throw new Error(data.error.message || JSON.stringify(data.error))
|
|
258
|
-
const audioUrl = data.output?.[0]?.content?.[0]?.audio
|
|
259
|
-
if (!audioUrl) throw new Error('ๆช่ฟๅ้ณ้ข: ' + JSON.stringify(data).slice(0, 200))
|
|
260
|
-
const duration = ((data.extra_info?.music_duration || 0) / 1000).toFixed(1)
|
|
261
|
-
console.log(`โ
music-2.5 ็ๆๆๅ๏ผๆถ้ฟ็บฆ ${duration} ็ง`)
|
|
262
|
-
console.log('้ณ้ข URL:', audioUrl)
|
|
263
|
-
if (output) await downloadAndSave(audioUrl, output)
|
|
126
|
+
throw new Error('ๆช่ฟๅ่ง้ขๆฐๆฎ: ' + JSON.stringify(data).slice(0, 200))
|
|
264
127
|
}
|
|
128
|
+
return
|
|
265
129
|
}
|
|
266
130
|
|
|
267
131
|
// โโ OCR โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
268
132
|
if (type === 'ocr') {
|
|
269
133
|
let input = prompt
|
|
270
|
-
// ๅฆๆๆฏๆฌๅฐๆไปถ่ทฏๅพ๏ผ่ฏปๅไธบ base64
|
|
271
134
|
if (flags.file && fs.existsSync(path.resolve(flags.file))) {
|
|
272
135
|
input = fs.readFileSync(path.resolve(flags.file)).toString('base64')
|
|
273
136
|
}
|
|
@@ -293,147 +156,7 @@ async function pilot(flags) {
|
|
|
293
156
|
return
|
|
294
157
|
}
|
|
295
158
|
|
|
296
|
-
|
|
297
|
-
if (type === 'stt') {
|
|
298
|
-
if (!flags.file) throw new Error('--file ๆฏๅฟ
้็๏ผ้ณ้ขๆไปถ่ทฏๅพ๏ผ')
|
|
299
|
-
const audioBase64 = fs.readFileSync(path.resolve(flags.file)).toString('base64')
|
|
300
|
-
const result = await post('/v1/audio/transcriptions', {
|
|
301
|
-
model: 'whisper-1',
|
|
302
|
-
file: audioBase64,
|
|
303
|
-
filename: path.basename(flags.file),
|
|
304
|
-
})
|
|
305
|
-
const text = result.text || JSON.stringify(result, null, 2)
|
|
306
|
-
if (output) { fs.writeFileSync(output, text); console.log(`โ
ๅทฒไฟๅญๅฐ ${output}`) }
|
|
307
|
-
else console.log(text)
|
|
308
|
-
return
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
// โโ EMBEDDING โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
312
|
-
if (type === 'embedding') {
|
|
313
|
-
// doubao-embedding-vision๏ผ่ตฐ /v1/responses๏ผๅคๆจกๆ๏ผ๏ผinput ไธบๆฐ็ป
|
|
314
|
-
// ็ฎๅๆๆฌ embedding ไนๅฏ็จ
|
|
315
|
-
const inputData = flags.file
|
|
316
|
-
? [{ type: 'image_url', image_url: { url: 'data:image/png;base64,' + fs.readFileSync(path.resolve(flags.file)).toString('base64') } }]
|
|
317
|
-
: [{ type: 'text', text: prompt }]
|
|
318
|
-
const res = await request('/embeddings', {
|
|
319
|
-
method: 'POST',
|
|
320
|
-
body: JSON.stringify({
|
|
321
|
-
model: model || 'doubao-embedding-vision-251215',
|
|
322
|
-
input: inputData,
|
|
323
|
-
encoding_format: 'float',
|
|
324
|
-
dimensions: 1024,
|
|
325
|
-
sparse_embedding: { type: 'disabled' },
|
|
326
|
-
}),
|
|
327
|
-
})
|
|
328
|
-
const data = await res.json()
|
|
329
|
-
if (data.error) throw new Error(data.error.message || JSON.stringify(data.error))
|
|
330
|
-
const embedding = data.data?.[0]?.embedding
|
|
331
|
-
if (!embedding) throw new Error('ๆช่ฟๅๅ้: ' + JSON.stringify(data).slice(0, 200))
|
|
332
|
-
console.log(`โ
Embedding ๆๅ๏ผ็ปดๅบฆ: ${embedding.length}`)
|
|
333
|
-
if (output) {
|
|
334
|
-
fs.writeFileSync(output, JSON.stringify(embedding))
|
|
335
|
-
console.log(`ๅทฒไฟๅญๅฐ ${output}`)
|
|
336
|
-
} else {
|
|
337
|
-
console.log('ๅ5็ปด:', embedding.slice(0, 5))
|
|
338
|
-
}
|
|
339
|
-
return
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
// โโ VIDEO โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
343
|
-
if (type === 'video') {
|
|
344
|
-
if (!prompt) throw new Error('--prompt ๆฏๅฟ
้็๏ผ่ง้ขๆ่ฟฐ๏ผ')
|
|
345
|
-
const videoModel = model || 'kling-v2-6-text2video'
|
|
346
|
-
|
|
347
|
-
// 1. ๆไบคไปปๅก
|
|
348
|
-
process.stdout.write(`๐ฌ ๆไบค่ง้ขไปปๅก๏ผ${videoModel}๏ผ...`)
|
|
349
|
-
const res = await request('/video/generations', {
|
|
350
|
-
method: 'POST',
|
|
351
|
-
body: JSON.stringify({ model: videoModel, prompt }),
|
|
352
|
-
})
|
|
353
|
-
const submitData = await res.json()
|
|
354
|
-
if (!submitData.task_id) throw new Error('ๆไบคๅคฑ่ดฅ: ' + JSON.stringify(submitData).slice(0, 200))
|
|
355
|
-
|
|
356
|
-
const taskId = submitData.task_id
|
|
357
|
-
console.log(` โ
\n๐ task_id: ${taskId}`)
|
|
358
|
-
console.log('โณ ่ง้ข็ๆไธญ๏ผ้ข่ฎก้่ฆ 1-3 ๅ้...\n')
|
|
359
|
-
|
|
360
|
-
// 2. ่ฝฎ่ฏข็ถๆ๏ผๆๅค็ญ 5 ๅ้๏ผ
|
|
361
|
-
const maxWait = 300000
|
|
362
|
-
const interval = 5000
|
|
363
|
-
const start = Date.now()
|
|
364
|
-
|
|
365
|
-
while (Date.now() - start < maxWait) {
|
|
366
|
-
await new Promise(r => setTimeout(r, interval))
|
|
367
|
-
const pollRes = await request(`/tasks/${taskId}`, { method: 'GET' })
|
|
368
|
-
const task = await pollRes.json()
|
|
369
|
-
const status = task.status
|
|
370
|
-
|
|
371
|
-
process.stdout.write(`\r็ถๆ: ${status} ๅทฒ็ญๅพ
: ${Math.round((Date.now() - start) / 1000)}s `)
|
|
372
|
-
|
|
373
|
-
if (status === 'completed') {
|
|
374
|
-
console.log('\n')
|
|
375
|
-
const videoUrl = task.result_url || task.output_url
|
|
376
|
-
if (!videoUrl) throw new Error('ไปปๅกๅฎๆไฝๆช่ฟๅ่ง้ข URL: ' + JSON.stringify(task))
|
|
377
|
-
|
|
378
|
-
if (output) {
|
|
379
|
-
await downloadAndSave(videoUrl, output)
|
|
380
|
-
} else {
|
|
381
|
-
console.log(`โ
่ง้ข็ๆๅฎๆ๏ผ`)
|
|
382
|
-
console.log(`๐ ไธ่ฝฝ้พๆฅ๏ผ${videoUrl}`)
|
|
383
|
-
console.log(`\n๐ก ๅ --output ./video.mp4 ๅฏ่ชๅจไธ่ฝฝๅฐๆฌๅฐ`)
|
|
384
|
-
}
|
|
385
|
-
return
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
if (status === 'failed') {
|
|
389
|
-
throw new Error('่ง้ข็ๆๅคฑ่ดฅ: ' + (task.error || JSON.stringify(task)))
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
// ่ถ
ๆถ๏ผ็ปๅบๆๅจๆฅ่ฏขๅฝไปค
|
|
394
|
-
console.log(`\nโฐ ็ญๅพ
่ถ
ๆถ๏ผ5ๅ้๏ผ๏ผไปปๅกไปๅจๅๅฐ่ฟ่ก`)
|
|
395
|
-
console.log(`๐ ็จๅๅฏๆๅจๆฅ่ฏข่ฟๅบฆ๏ผ`)
|
|
396
|
-
console.log(` skillfree task ${taskId}`)
|
|
397
|
-
return
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
// โโ SEARCH โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
401
|
-
if (type === 'search') {
|
|
402
|
-
if (!prompt) throw new Error('--prompt ๆฏๅฟ
้็๏ผๆ็ดข่ฏ๏ผ')
|
|
403
|
-
const searchModel = model || 'tavily-search'
|
|
404
|
-
const res = await request('/search', {
|
|
405
|
-
method: 'POST',
|
|
406
|
-
body: JSON.stringify({
|
|
407
|
-
model: searchModel,
|
|
408
|
-
query: prompt,
|
|
409
|
-
include_answer: true,
|
|
410
|
-
max_results: flags.maxResults || 5,
|
|
411
|
-
}),
|
|
412
|
-
})
|
|
413
|
-
const data = await res.json()
|
|
414
|
-
if (data.error) throw new Error(data.error.message || JSON.stringify(data.error))
|
|
415
|
-
|
|
416
|
-
// ่พๅบๆ ผๅผๅ็ปๆ
|
|
417
|
-
if (data.answer) {
|
|
418
|
-
console.log('\n๐ ๆ่ฆ็ญๆก๏ผ')
|
|
419
|
-
console.log(data.answer)
|
|
420
|
-
}
|
|
421
|
-
if (data.results?.length) {
|
|
422
|
-
console.log(`\n๐ ๆ็ดข็ปๆ๏ผๅ
ฑ ${data.results.length} ๆก๏ผ๏ผ`)
|
|
423
|
-
data.results.forEach((r, i) => {
|
|
424
|
-
console.log(`\n${i+1}. ${r.title}`)
|
|
425
|
-
console.log(` ${r.url}`)
|
|
426
|
-
if (r.content) console.log(` ${r.content.slice(0, 150)}...`)
|
|
427
|
-
})
|
|
428
|
-
}
|
|
429
|
-
if (output) {
|
|
430
|
-
fs.writeFileSync(output, JSON.stringify(data, null, 2))
|
|
431
|
-
console.log(`\nโ
ๅฎๆด็ปๆๅทฒไฟๅญๅฐ ${output}`)
|
|
432
|
-
}
|
|
433
|
-
return
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
throw new Error(`ไธๆฏๆ็็ฑปๅ: ${type}๏ผๅฏ้: chat | image | tts | stt | music | ocr | video | embedding | search`)
|
|
159
|
+
throw new Error(`ไธๆฏๆ็็ฑปๅ: ${type}\nๅฏ้: chat | image | video | ocr`)
|
|
437
160
|
}
|
|
438
161
|
|
|
439
162
|
module.exports = { pilot }
|
|
@@ -1,97 +1,21 @@
|
|
|
1
|
-
const { run } = require('./run')
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
|
-
* Video generation
|
|
5
|
-
* @param {object} params
|
|
6
|
-
* @param {string} params.model -
|
|
7
|
-
* @param {string} params.prompt
|
|
8
|
-
* @param {string} [params.
|
|
9
|
-
* @
|
|
2
|
+
* Video generation via Veo๏ผๅผๆญฅ๏ผๆๅก็ซฏ่ฝฎ่ฏขๅฎๆๅ่ฟๅ๏ผ
|
|
3
|
+
* @param {object} params
|
|
4
|
+
* @param {string} params.model - veo-3.1-fast-generate-preview | veo-3.1-generate-preview
|
|
5
|
+
* @param {string} params.prompt - ่ง้ขๆ่ฟฐๆๆฌ
|
|
6
|
+
* @param {string} [params.seconds] - ๆถ้ฟ๏ผ4 / 6 / 8๏ผ้ป่ฎค 8๏ผ
|
|
7
|
+
* @param {string} [params.size] - ๅ่พจ็๏ผ1280x720 / 1920x1080 / 3840x2160๏ผ้ป่ฎค 1920x1080๏ผ
|
|
8
|
+
* @param {string} [params.output] - ๆฌๅฐไฟๅญ่ทฏๅพ๏ผ.mp4๏ผ
|
|
10
9
|
*/
|
|
11
10
|
async function video(params) {
|
|
12
|
-
if (!params.prompt)
|
|
13
|
-
throw new Error('--prompt is required for video generation')
|
|
14
|
-
}
|
|
11
|
+
if (!params.prompt) throw new Error('--prompt is required for video generation')
|
|
15
12
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
inputs.instances = [{ prompt: params.prompt }]
|
|
22
|
-
inputs.parameters = {}
|
|
23
|
-
} else if (vendor === 'mm') {
|
|
24
|
-
// MM video models: t2v (text-to-video), i2v (image-to-video)
|
|
25
|
-
inputs.prompt = params.prompt
|
|
26
|
-
if (params.size) {
|
|
27
|
-
// Convert "1280x720" to "1280*720" if needed
|
|
28
|
-
inputs.size = params.size.replace('x', '*')
|
|
29
|
-
}
|
|
30
|
-
if (params.duration) {
|
|
31
|
-
inputs.duration = parseInt(params.duration)
|
|
32
|
-
}
|
|
33
|
-
if (params.image) {
|
|
34
|
-
// i2v mode: image-to-video
|
|
35
|
-
inputs.image_url = params.image
|
|
36
|
-
}
|
|
37
|
-
} else {
|
|
38
|
-
// MiniMax and others use 'prompt'
|
|
39
|
-
inputs.prompt = params.prompt
|
|
13
|
+
return {
|
|
14
|
+
model: params.model || 'veo-3.1-fast-generate-preview',
|
|
15
|
+
input: params.prompt,
|
|
16
|
+
seconds: params.seconds || '8',
|
|
17
|
+
size: params.size || '1920x1080',
|
|
40
18
|
}
|
|
41
|
-
|
|
42
|
-
return run({ model: params.model, inputs, output: params.output })
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Multimodal understanding command (video/image/audio analysis)
|
|
47
|
-
* @param {object} params - Multimodal parameters
|
|
48
|
-
* @param {string} params.model - Model in "vendor/model" format (e.g., mm/qwen3-vl-plus)
|
|
49
|
-
* @param {string} params.prompt - Text prompt/question about the media
|
|
50
|
-
* @param {string} [params.video] - Video URL to analyze
|
|
51
|
-
* @param {string} [params.image] - Image URL to analyze
|
|
52
|
-
* @param {string} [params.audio] - Audio URL to analyze/transcribe
|
|
53
|
-
* @returns {Promise<object>} Multimodal analysis result
|
|
54
|
-
*/
|
|
55
|
-
async function multimodal(params) {
|
|
56
|
-
if (!params.prompt) {
|
|
57
|
-
throw new Error('--prompt is required for multimodal')
|
|
58
|
-
}
|
|
59
|
-
if (!params.video && !params.image && !params.audio) {
|
|
60
|
-
throw new Error('At least one of --video, --image, or --audio is required')
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const [vendor] = params.model.split('/')
|
|
64
|
-
const inputs = {}
|
|
65
|
-
|
|
66
|
-
if (vendor === 'mm') {
|
|
67
|
-
// MM multimodal models use messages format
|
|
68
|
-
const content = []
|
|
69
|
-
if (params.video) {
|
|
70
|
-
content.push({ video: params.video })
|
|
71
|
-
if (params.fps) {
|
|
72
|
-
content[content.length - 1].fps = parseInt(params.fps)
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
if (params.image) {
|
|
76
|
-
content.push({ image: params.image })
|
|
77
|
-
}
|
|
78
|
-
if (params.audio) {
|
|
79
|
-
content.push({ audio: params.audio })
|
|
80
|
-
}
|
|
81
|
-
content.push({ text: params.prompt })
|
|
82
|
-
|
|
83
|
-
inputs.input = {
|
|
84
|
-
messages: [{ role: 'user', content }]
|
|
85
|
-
}
|
|
86
|
-
} else {
|
|
87
|
-
// Generic format
|
|
88
|
-
inputs.prompt = params.prompt
|
|
89
|
-
if (params.video) inputs.video_url = params.video
|
|
90
|
-
if (params.image) inputs.image_url = params.image
|
|
91
|
-
if (params.audio) inputs.audio_url = params.audio
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
return run({ model: params.model, inputs })
|
|
95
19
|
}
|
|
96
20
|
|
|
97
|
-
module.exports = { video
|
|
21
|
+
module.exports = { video }
|
|
Binary file
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
const { run } = require('./run')
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Music generation command
|
|
5
|
-
* @param {object} params - Music parameters
|
|
6
|
-
* @param {string} params.model - Model in "vendor/model" format
|
|
7
|
-
* @param {string} params.prompt - Music generation prompt
|
|
8
|
-
* @param {number} [params.duration] - Duration in seconds
|
|
9
|
-
* @param {string} [params.output] - Output file path
|
|
10
|
-
* @returns {Promise<object>} Music generation result
|
|
11
|
-
*/
|
|
12
|
-
async function music(params) {
|
|
13
|
-
if (!params.prompt) {
|
|
14
|
-
throw new Error('--prompt is required for music generation')
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const inputs = {
|
|
18
|
-
prompt: params.prompt,
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
if (params.duration) {
|
|
22
|
-
inputs.duration = parseInt(params.duration)
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return run({ model: params.model, inputs, output: params.output })
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
module.exports = { music }
|