skillfree 0.1.9 → 0.1.11

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 CHANGED
@@ -108,10 +108,20 @@ skillfree pilot --type tts --text "你好世界" --output hello.mp3
108
108
 
109
109
  ## 🎬 视频生成(video)
110
110
 
111
- > 视频生成为异步任务,提交后后台处理,通过task_id查询进度
111
+ > 视频为异步任务:提交后自动轮询,完成后返回下载链接或自动保存本地。
112
112
 
113
113
  ```bash
114
- skillfree pilot --type video --prompt "波浪翻涌的海面,黄昏时分" --output ./video.mp4
114
+ # 文生视频(自动轮询,完成后显示下载链接)
115
+ skillfree pilot --type video --model wan2.6-t2v --prompt "波浪翻涌的海面,黄昏时分"
116
+
117
+ # 自动下载到本地
118
+ skillfree pilot --type video --model wan2.6-t2v --prompt "波浪翻涌的海面" --output ./video.mp4
119
+
120
+ # 超时后手动查询进度(使用提交时返回的 task_id)
121
+ skillfree task <task_id>
122
+
123
+ # 查询并下载
124
+ skillfree task <task_id> --output ./video.mp4
115
125
  ```
116
126
 
117
127
  | 模型ID | 名称 | 积分 | 最适合的场景 |
package/bin/skillfree.js CHANGED
@@ -63,4 +63,62 @@ program
63
63
  await authStatus()
64
64
  })
65
65
 
66
+ // ── task(查询异步任务状态)────────────────────────────────────────────────────
67
+ program
68
+ .command('task <taskId>')
69
+ .description('查询异步任务状态(视频/音乐等)')
70
+ .option('--output <path>', '任务完成后自动下载到本地')
71
+ .action(async (taskId, flags) => {
72
+ const { request } = require('../scripts/lib/client')
73
+ const fs = require('fs')
74
+
75
+ // Suno 任务用 suno:xxx 格式区分
76
+ if (taskId.startsWith('suno:')) {
77
+ const sunoId = taskId.slice(5)
78
+ const poll = await request('/suno/fetch/' + sunoId, { method: 'GET' })
79
+ const result = await poll.json()
80
+ if (result.code !== 'success') throw new Error(result.message || JSON.stringify(result))
81
+ const status = result.data?.status
82
+ const songs = result.data?.data || []
83
+ console.log(`\n📋 Suno 任务 ${sunoId}`)
84
+ console.log(` 状态:${status}`)
85
+ if (status === 'SUCCESS') {
86
+ songs.forEach((s, i) => {
87
+ console.log(` 歌曲${i+1}:${s.title} ${s.audio_url}`)
88
+ })
89
+ if (flags.output && songs[0]?.audio_url) {
90
+ const resp = await fetch(songs[0].audio_url)
91
+ fs.writeFileSync(flags.output, Buffer.from(await resp.arrayBuffer()))
92
+ console.log(`✅ 已下载到 ${flags.output}`)
93
+ } else {
94
+ console.log(`\n💡 下载:skillfree task ${taskId} --output ./music.mp3`)
95
+ }
96
+ } else {
97
+ console.log(`\n⏳ 仍在生成中,稍后再查...`)
98
+ }
99
+ return
100
+ }
101
+
102
+ // 视频任务
103
+ const res = await request(`/v1/tasks/${taskId}`, { method: 'GET' })
104
+ const task = await res.json()
105
+ console.log(`\n📋 任务 ${taskId}`)
106
+ console.log(` 状态:${task.status}`)
107
+ console.log(` 模型:${task.model}`)
108
+ if (task.status === 'completed' && task.output_url) {
109
+ console.log(` 链接:${task.output_url}`)
110
+ if (flags.output) {
111
+ const resp = await fetch(task.output_url)
112
+ fs.writeFileSync(flags.output, Buffer.from(await resp.arrayBuffer()))
113
+ console.log(`✅ 已下载到 ${flags.output}`)
114
+ } else {
115
+ console.log(`\n💡 下载:skillfree task ${taskId} --output ./video.mp4`)
116
+ }
117
+ } else if (task.status === 'failed') {
118
+ console.log(` 错误:${task.error || '未知错误'}`)
119
+ } else {
120
+ console.log(`\n⏳ 仍在生成中,稍后再查...`)
121
+ }
122
+ })
123
+
66
124
  program.parse(process.argv)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillfree",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "description": "🦞 一个 API,满足所有龙虾技能需求",
5
5
  "main": "bin/skillfree.js",
6
6
  "bin": {
@@ -231,7 +231,7 @@ async function pilot(flags) {
231
231
  }
232
232
  if (status === 'FAILED') throw new Error('Suno 任务失败: ' + JSON.stringify(result.data))
233
233
  }
234
- throw new Error('Suno 生成超时(150s),请手动查询 task_id: ' + taskId)
234
+ throw new Error(`Suno 生成超时(150s),任务仍在后台运行\n📋 稍后手动查询:skillfree task suno:${taskId}`)
235
235
 
236
236
  } else {
237
237
  // music-2.5(MiniMax),走 /v1/responses,需要 lyrics
@@ -332,6 +332,64 @@ async function pilot(flags) {
332
332
  return
333
333
  }
334
334
 
335
+ // ── VIDEO ─────────────────────────────────────────────────────────────────────
336
+ if (type === 'video') {
337
+ if (!prompt) throw new Error('--prompt 是必需的(视频描述)')
338
+ const videoModel = model || 'wan2.6-t2v'
339
+
340
+ // 1. 提交任务
341
+ process.stdout.write(`🎬 提交视频任务(${videoModel})...`)
342
+ const res = await request('/v1/video/generations', {
343
+ method: 'POST',
344
+ body: JSON.stringify({ model: videoModel, prompt }),
345
+ })
346
+ const submitData = await res.json()
347
+ if (!submitData.task_id) throw new Error('提交失败: ' + JSON.stringify(submitData).slice(0, 200))
348
+
349
+ const taskId = submitData.task_id
350
+ console.log(` ✅\n📋 task_id: ${taskId}`)
351
+ console.log('⏳ 视频生成中,预计需要 1-3 分钟...\n')
352
+
353
+ // 2. 轮询状态(最多等 5 分钟)
354
+ const maxWait = 300000
355
+ const interval = 5000
356
+ const start = Date.now()
357
+
358
+ while (Date.now() - start < maxWait) {
359
+ await new Promise(r => setTimeout(r, interval))
360
+ const pollRes = await request(`/v1/tasks/${taskId}`, { method: 'GET' })
361
+ const task = await pollRes.json()
362
+ const status = task.status
363
+
364
+ process.stdout.write(`\r状态: ${status} 已等待: ${Math.round((Date.now() - start) / 1000)}s `)
365
+
366
+ if (status === 'completed') {
367
+ console.log('\n')
368
+ const videoUrl = task.output_url
369
+ if (!videoUrl) throw new Error('任务完成但未返回视频 URL: ' + JSON.stringify(task))
370
+
371
+ if (output) {
372
+ await downloadAndSave(videoUrl, output)
373
+ } else {
374
+ console.log(`✅ 视频生成完成!`)
375
+ console.log(`🔗 下载链接:${videoUrl}`)
376
+ console.log(`\n💡 加 --output ./video.mp4 可自动下载到本地`)
377
+ }
378
+ return
379
+ }
380
+
381
+ if (status === 'failed') {
382
+ throw new Error('视频生成失败: ' + (task.error || JSON.stringify(task)))
383
+ }
384
+ }
385
+
386
+ // 超时,给出手动查询命令
387
+ console.log(`\n⏰ 等待超时(5分钟),任务仍在后台运行`)
388
+ console.log(`📋 稍后可手动查询进度:`)
389
+ console.log(` skillfree task ${taskId}`)
390
+ return
391
+ }
392
+
335
393
  throw new Error(`不支持的类型: ${type},可选: chat | image | tts | stt | music | ocr | video | embedding`)
336
394
  }
337
395