skillfree 0.1.13 → 0.1.15

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/bin/skillfree.js CHANGED
@@ -39,6 +39,7 @@ program
39
39
  .option('--voice <voice>', 'TTS 声音(默认 nova)')
40
40
  .option('--size <size>', '图像尺寸(默认 1024x1024)')
41
41
  .option('--duration <sec>', '视频/音乐时长(秒)')
42
+ .option('--lyrics <text>', '歌词(music-2.5 用,支持 [verse][chorus] 标签)')
42
43
  .action(async (flags) => {
43
44
  const { pilot } = require('../scripts/commands/pilot')
44
45
  await pilot(flags).catch(e => { console.error('❌', e.message); process.exit(1) })
@@ -54,6 +55,16 @@ program
54
55
  await pilot({ type: 'chat', prompt, ...flags }).catch(e => { console.error('❌', e.message); process.exit(1) })
55
56
  })
56
57
 
58
+ // ── models ────────────────────────────────────────────────────────────────────
59
+ program
60
+ .command('models')
61
+ .description('查看所有可用模型')
62
+ .option('--type <type>', '按类型过滤: chat | image | tts | video | music | ocr | embedding')
63
+ .action(async (flags) => {
64
+ const { listModels } = require('../scripts/commands/models')
65
+ await listModels(flags.type).catch(e => { console.error('❌', e.message); process.exit(1) })
66
+ })
67
+
57
68
  // ── balance ───────────────────────────────────────────────────────────────────
58
69
  program
59
70
  .command('balance')
@@ -75,7 +86,11 @@ program
75
86
  // Suno 任务用 suno:xxx 格式区分
76
87
  if (taskId.startsWith('suno:')) {
77
88
  const sunoId = taskId.slice(5)
78
- const poll = await request('/suno/fetch/' + sunoId, { method: 'GET' })
89
+ const { BASE_URL, getApiKey } = require('../scripts/lib/client')
90
+ const apiKey = getApiKey()
91
+ const poll = await fetch(`${BASE_URL}/suno/fetch/${sunoId}`, {
92
+ headers: { 'Authorization': `Bearer ${apiKey}` }
93
+ })
79
94
  const result = await poll.json()
80
95
  if (result.code !== 'success') throw new Error(result.message || JSON.stringify(result))
81
96
  const status = result.data?.status
@@ -100,7 +115,7 @@ program
100
115
  }
101
116
 
102
117
  // 视频任务
103
- const res = await request(`/v1/tasks/${taskId}`, { method: 'GET' })
118
+ const res = await request(`/tasks/${taskId}`, { method: 'GET' })
104
119
  const task = await res.json()
105
120
  console.log(`\n📋 任务 ${taskId}`)
106
121
  console.log(` 状态:${task.status}`)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillfree",
3
- "version": "0.1.13",
3
+ "version": "0.1.15",
4
4
  "description": "🦞 一个 API,满足所有龙虾技能需求",
5
5
  "main": "bin/skillfree.js",
6
6
  "bin": {
@@ -1,32 +1,36 @@
1
- const { apiHubGet } = require('../lib/client')
1
+ const { request } = require('../lib/client')
2
2
 
3
- /**
4
- * List available models from API Hub
5
- * @param {object} [params] - List parameters
6
- * @param {string} [params.type] - Filter by category (chat, tts, image, video, scraping, etc.)
7
- * @param {string} [params.vendor] - Filter by vendor
8
- * @returns {Promise<object>} Models list
9
- */
10
- async function listModels(params = {}) {
11
- const response = await apiHubGet('/v1/models')
12
- let models = response.models || []
3
+ async function listModels(type) {
4
+ const res = await request('/models', { method: 'GET' })
5
+ const data = await res.json()
6
+ const all = data.data || []
13
7
 
14
- // Filter by category/type
15
- if (params.type) {
16
- const typeFilter = params.type.toLowerCase()
17
- models = models.filter(m =>
18
- m.category?.toLowerCase() === typeFilter ||
19
- m.type?.toLowerCase() === typeFilter
20
- )
8
+ const groups = {}
9
+ for (const m of all) {
10
+ const t = m.type || 'chat'
11
+ if (type && t !== type) continue
12
+ if (!groups[t]) groups[t] = []
13
+ groups[t].push(m.id)
21
14
  }
22
15
 
23
- // Filter by vendor
24
- if (params.vendor) {
25
- const vendorFilter = params.vendor.toLowerCase()
26
- models = models.filter(m => m.vendor?.toLowerCase() === vendorFilter)
16
+ const typeLabel = {
17
+ chat: '💬 对话',
18
+ image: '🎨 图像',
19
+ tts: '🔊 语音合成',
20
+ video: '🎬 视频',
21
+ music: '🎵 音乐',
22
+ ocr: '📄 OCR',
23
+ embedding: '🔢 Embedding',
27
24
  }
28
25
 
29
- return { count: models.length, models }
26
+ console.log('\n🦞 SkillFree 可用模型\n')
27
+ for (const [t, ids] of Object.entries(groups)) {
28
+ console.log(`${typeLabel[t] || t}`)
29
+ ids.forEach(id => console.log(` ${id}`))
30
+ console.log()
31
+ }
32
+ console.log(`共 ${all.length} 个模型`)
33
+ console.log(`\n用法示例:skillfree pilot --type chat --model DeepSeek-V3.2-Fast --prompt "你好"`)
30
34
  }
31
35
 
32
36
  module.exports = { listModels }
@@ -143,11 +143,12 @@ async function pilot(flags) {
143
143
  else console.log(`✅ TTS 成功,时长约 ${(data.extra_info?.audio_length / 1000).toFixed(1)} 秒`)
144
144
 
145
145
  } else if (ttsModel === 'gemini-2.5-pro-preview-tts' || ttsModel === 'gemini-2.5-flash-preview-tts') {
146
- // Gemini TTS:走 /v1beta/models/:id:generateContent,认证无 Bearer
146
+ // Gemini TTS:走 skillfree.tech 后端 /v1beta 路由
147
+ // 注意:用 fetch+BASE_URL 而非 request(),因为 request() 会自动加 /v1 前缀
147
148
  const apiKey = getApiKey()
148
- const res = await fetch(BASE_URL.replace('skillfree.tech', 'dmxapi.cn') + '/v1beta/models/' + ttsModel + ':generateContent', {
149
+ const res = await fetch(BASE_URL + '/v1beta/models/' + ttsModel + ':generateContent', {
149
150
  method: 'POST',
150
- headers: { 'Authorization': apiKey, 'Content-Type': 'application/json' },
151
+ headers: { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json' },
151
152
  body: JSON.stringify({
152
153
  contents: [{ parts: [{ text }] }],
153
154
  generationConfig: {
@@ -192,10 +193,11 @@ async function pilot(flags) {
192
193
  const musicModel = model || 'chirp-v5'
193
194
 
194
195
  if (musicModel === 'chirp-v5') {
195
- // Suno 异步接口
196
- const res = await request('/suno/submit/music', {
196
+ // Suno 异步接口(路由在 /suno/,不在 /v1/ 下,需用 BASE_URL 直接请求)
197
+ const apiKey = getApiKey()
198
+ const res = await fetch(`${BASE_URL}/suno/submit/music`, {
197
199
  method: 'POST',
198
- headers: { 'Accept': 'application/json' },
200
+ headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', 'Authorization': `Bearer ${apiKey}` },
199
201
  body: JSON.stringify({
200
202
  gpt_description_prompt: prompt,
201
203
  make_instrumental: true,
@@ -211,7 +213,9 @@ async function pilot(flags) {
211
213
  // 轮询结果
212
214
  for (let i = 0; i < 15; i++) {
213
215
  await new Promise(r => setTimeout(r, 10000))
214
- const poll = await request('/suno/fetch/' + taskId, { method: 'GET' })
216
+ const poll = await fetch(`${BASE_URL}/suno/fetch/${taskId}`, {
217
+ headers: { 'Authorization': `Bearer ${apiKey}` }
218
+ })
215
219
  const result = await poll.json()
216
220
  if (result.code !== 'success') throw new Error(result.message || JSON.stringify(result))
217
221
  const status = result.data?.status
@@ -339,7 +343,7 @@ async function pilot(flags) {
339
343
 
340
344
  // 1. 提交任务
341
345
  process.stdout.write(`🎬 提交视频任务(${videoModel})...`)
342
- const res = await request('/v1/video/generations', {
346
+ const res = await request('/video/generations', {
343
347
  method: 'POST',
344
348
  body: JSON.stringify({ model: videoModel, prompt }),
345
349
  })
@@ -357,7 +361,7 @@ async function pilot(flags) {
357
361
 
358
362
  while (Date.now() - start < maxWait) {
359
363
  await new Promise(r => setTimeout(r, interval))
360
- const pollRes = await request(`/v1/tasks/${taskId}`, { method: 'GET' })
364
+ const pollRes = await request(`/tasks/${taskId}`, { method: 'GET' })
361
365
  const task = await pollRes.json()
362
366
  const status = task.status
363
367