skillfree 0.1.39 → 0.1.40

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
@@ -1,163 +1,161 @@
1
1
  ---
2
2
  name: skillfree
3
- description: SkillFree — 模型与技能严选平台。只保留每个任务类型最强的模型,按实际用量精确计费。写代码用Claude,文档分析用GPT,多模态用Gemini,生图用nano banana,生视频用Veo,文档OCR用合合,网页搜索用Tavily
3
+ description: SkillFree — 模型与技能严选平台。只保留每个任务类型最强的模型,按实际用量精确计费。写代码用Claude,文档分析用GPT,多模态用Gemini,生图用nano banana,生视频用Veo,文档OCR用合合,网页搜索用Tavily,做PPT用AI演示文稿。
4
4
  metadata: {"openclaw": {"primaryEnv": "SKILLFREE_API_KEY", "emoji": "🦞", "homepage": "https://skillfree.tech", "requires": {"bins": ["skillfree"], "env": ["SKILLFREE_API_KEY"]}}}
5
5
  ---
6
6
 
7
7
  # SkillFree — 模型与技能严选平台 🦞
8
8
 
9
- > 不堆模型数量,只选每个场景最好用的那一个。按实际 token / 秒精确计费,用多少扣多少。
9
+ > 不堆模型数量,只选每个场景最好用的那一个。按实际用量精确计费,用多少扣多少。
10
10
 
11
11
  ---
12
12
 
13
- ## 🧭 任务 → 模型 一览
13
+ ## 🧭 任务 → 严选工具 一览
14
14
 
15
- | 任务 | 严选模型 |
16
- |------|---------|
17
- | ✍️ 写代码 / 代码审查 / 重构 | Claude Sonnet 4.6 |
18
- | 📄 文档分析 / 报告生成 / 长文理解 | GPT-5.4 |
19
- | 🖼️ 图文混合 / 多模态理解 | Gemini 3.1 Pro |
20
- | 🎨 图像生成 | nano banana 2 / nano banana pro |
21
- | 🎬 视频生成 | Veo 3.1 Fast / Veo 3.1 |
22
- | 📑 扫描文档 OCR 解析 | 合合 图文混排 |
23
- | 🌐 实时网页搜索 | Tavily(独立技能) |
15
+ | 任务类型 | 严选工具 | 计费方式 |
16
+ |---------|---------|---------|
17
+ | ✍️ 写代码 / 重构 / 技术文档 | Claude Sonnet 4.6 | 按 token |
18
+ | 📄 文档分析 / 报告生成 | GPT-5.4 | 按 token |
19
+ | 🖼️ 图文多模态理解 | Gemini 3.1 Pro | 按 token |
20
+ | 🎨 图像生成 | nano banana 2 / pro | 按次 |
21
+ | 🎬 视频生成 | Veo 3.1 Fast / Veo 3.1 | 按秒 |
22
+ | 📑 扫描文档 OCR | 合合 图文混排 | 按量 |
23
+ | 🌐 实时网页搜索 | Tavily | 按次 |
24
+ | 📊 演示文稿 / PPT 生成 | **AI 演示文稿生成** ✨ | **按实际页数** |
24
25
 
25
26
  ---
26
27
 
27
- ## 💬 对话模型(chat)
28
-
29
- 计费方式:按实际消耗 token 精确结算(¥/百万 tokens)
28
+ ## 💬 模型严选(chat / image / video / ocr
30
29
 
31
30
  ### ✍️ 写代码 — Claude
32
31
 
33
- Claude 对代码的理解深度在所有模型中最强:能追踪跨文件上下文、精准定位 bug、给出可直接运行的重构方案。技术写作也是强项。
34
-
35
32
  | 模型ID | 名称 | 输入 | 输出 |
36
33
  |--------|------|------|------|
37
- | `claude-sonnet-4-6` | Claude Sonnet 4.6 ⭐ | ¥28/1M | ¥140/1M |
38
- | `claude-sonnet-4-6-cc` | Claude Sonnet 4.6 CC | ¥28/1M | ¥140/1M |
39
- | `claude-opus-4-6` | Claude Opus 4.6 | ¥140/1M | ¥700/1M |
40
- | `claude-opus-4-6-cc` | Claude Opus 4.6 CC | ¥140/1M | ¥700/1M |
41
-
42
- ```bash
43
- skillfree pilot --type chat --model claude-sonnet-4-6 \
44
- --prompt "重构这段代码,提升可读性并添加类型注解"
45
- ```
34
+ | `claude-sonnet-4-6` | Claude Sonnet 4.6 ⭐ | ¥30/1M | ¥150/1M |
35
+ | `claude-sonnet-4-6-cc` | Claude Sonnet 4.6 CC | ¥30/1M | ¥150/1M |
36
+ | `claude-opus-4-6` | Claude Opus 4.6 | ¥150/1M | ¥750/1M |
46
37
 
47
- **Claude 适合:** 代码生成、bug 定位、重构、架构设计、技术文档
48
- **Claude 不适合:** 实时联网信息、图片理解
38
+ **适合:** 代码生成、bug 定位、重构、架构设计、技术文档
49
39
 
50
40
  ---
51
41
 
52
42
  ### 📄 文档分析 — GPT
53
43
 
54
- GPT 擅长结构化文档处理:读懂复杂格式后输出层次清晰的分析,在商业报告、合同条款提炼、数据表格解读上表现最稳。
55
-
56
44
  | 模型ID | 名称 | 输入 | 输出 |
57
45
  |--------|------|------|------|
58
- | `gpt-5.4` | GPT-5.4 ⭐ | ¥23/1M | ¥140/1M |
59
- | `gpt-5.4-mini` | GPT-5.4 Mini | ¥7/1M | ¥42/1M |
46
+ | `gpt-5.4` | GPT-5.4 ⭐ | ¥33/1M | ¥200/1M |
47
+ | `gpt-5.4-mini` | GPT-5.4 Mini | ¥10/1M | ¥60/1M |
60
48
 
61
- ```bash
62
- skillfree pilot --type chat --model gpt-5.4 \
63
- --prompt "提取这份投资协议中的核心条款和风险点"
64
- ```
65
-
66
- **GPT 适合:** 商业文档、合同分析、结构化摘要、数据提炼、报告生成
67
- **GPT-mini 适合:** 大批量简单文档处理,成本敏感场景
49
+ **适合:** 商业文档、合同分析、结构化摘要、数据提炼、报告生成
68
50
 
69
51
  ---
70
52
 
71
53
  ### 🖼️ 多模态理解 — Gemini
72
54
 
73
- Gemini 原生多模态架构,图片、PDF、表格、文字混合输入时理解最准。超长上下文窗口,适合需要"同时看多份材料"的复杂分析场景。
74
-
75
55
  | 模型ID | 名称 | 输入 | 输出 |
76
56
  |--------|------|------|------|
77
- | `gemini-3.1-pro-preview` | Gemini 3.1 Pro ⭐ | ¥23/1M | ¥94/1M |
78
- | `gemini-3.1-flash-lite-preview` | Gemini 3.1 Flash Lite | ¥1/1M | ¥4/1M |
79
-
80
- ```bash
81
- skillfree pilot --type chat --model gemini-3.1-pro-preview \
82
- --prompt "分析这张财务图表的关键趋势" --file chart.png
83
- ```
57
+ | `gemini-3.1-pro-preview` | Gemini 3.1 Pro ⭐ | ¥33/1M | ¥134/1M |
58
+ | `gemini-3.1-flash-lite-preview` | Gemini 3.1 Flash Lite | ¥1.1/1M | ¥4.3/1M |
84
59
 
85
- **Gemini Pro 适合:** 图文混合分析、多模态推理、超长文档、多文件联合分析
86
- **Gemini Flash Lite 适合:** 大批量简单图文识别,极低成本
60
+ **适合:** 图文混合分析、多模态推理、超长文档
87
61
 
88
62
  ---
89
63
 
90
- ## 🎨 图像生成 — nano banana 系列
91
-
92
- 基于 Google Gemini 图像模型。对自然语言描述理解准确,风格多样,中英文 prompt 均可,生成速度快。
64
+ ### 🎨 图像生成 — nano banana
93
65
 
94
66
  | 模型ID | 展示名 | 价格 |
95
67
  |--------|--------|------|
96
68
  | `gemini-3.1-flash-image-preview` | **nano banana 2** ⭐ | ¥0.40/次 |
97
69
  | `gemini-3-pro-image-preview` | **nano banana pro** | ¥1.40/次 |
98
70
 
99
- ```bash
100
- # 快速生成(推荐日常使用)
101
- skillfree pilot --type image --model gemini-3.1-flash-image-preview \
102
- --prompt "赛博朋克上海夜景,霓虹灯反射在雨后街道" --output ./image.png
103
-
104
- # 旗舰品质(商业级创作)
105
- skillfree pilot --type image --model gemini-3-pro-image-preview \
106
- --prompt "产品包装图,极简风格,白底" --output ./image.png
107
- ```
108
-
109
- **nano banana 2:** 日常创作、概念图、插画,速度快性价比高
110
- **nano banana pro:** 复杂场景构图、艺术风格精控、商业级输出
111
-
112
71
  ---
113
72
 
114
- ## 🎬 视频生成 — Google Veo
115
-
116
- Google Veo 系列,物理运动自然,画质细腻。文字描述直接生成视频,异步生成自动轮询返回结果,支持三档分辨率,**成功生成后才扣费**。
73
+ ### 🎬 视频生成 — Google Veo
117
74
 
118
75
  | 模型ID | 名称 | 720p | 1080p | 4K |
119
76
  |--------|------|------|-------|-----|
120
77
  | `veo-3.1-fast-generate-preview` | **Veo 3.1 Fast** ⭐ | ¥1.5/s | ¥1.5/s | ¥3.5/s |
121
78
  | `veo-3.1-generate-preview` | **Veo 3.1** | ¥3.5/s | ¥3.5/s | ¥5.5/s |
122
79
 
123
- ```bash
124
- # 快速版(推荐)
125
- skillfree pilot --type video --model veo-3.1-fast-generate-preview \
126
- --prompt "樱花花瓣在微风中缓缓飘落,日式庭院,电影感" \
127
- --seconds 8 --size 1920x1080 --output ./video.mp4
128
-
129
- # 旗舰版(商业级)
130
- skillfree pilot --type video --model veo-3.1-generate-preview \
131
- --prompt "产品展示视频,极简白色空间" \
132
- --seconds 6 --size 3840x2160 --output ./video.mp4
133
- ```
134
-
135
- **分辨率:** `1280x720`(720p)· `1920x1080`(1080p)· `3840x2160`(4K)
136
- **时长:** `--seconds 4 / 6 / 8`(默认 8 秒)
80
+ 异步生成,成功后才扣费,支持 4~8 秒时长。
137
81
 
138
82
  ---
139
83
 
140
- ## 📑 文档 OCR 解析 — 合合信息
141
-
142
- 合合(hehe)专注文档智能解析,扫描版 PDF、图片文字、复杂排版(表格、公式、图文混排)识别率业界顶级,输出标准 Markdown,是纸质文档数字化的最优选择。
84
+ ### 📑 OCR 解析 — 合合信息
143
85
 
144
86
  | 模型ID | 名称 | 计费 |
145
87
  |--------|------|------|
146
88
  | `hehe-tywd` | **合合 图文混排解析** | 按量计费 |
147
89
 
148
- ```bash
149
- skillfree pilot --type ocr --model hehe-tywd --file ./contract_scan.pdf
150
- ```
90
+ **适合:** 扫描版合同、学术论文、含表格财务文档
91
+
92
+ ---
93
+
94
+ ### 🌐 实时搜索 — Tavily
95
+
96
+ 作为独立技能安装,可与对话模型组合构建搜索增强工作流。
97
+
98
+ ---
99
+
100
+ ## ✨ 技能严选
151
101
 
152
- **适合:** 扫描版合同 / 报告、学术论文、含表格的财务文档、复杂排版文件
102
+ > 技能 = 多步骤自动化工作流,比单一模型调用更强大。按任务结果计费,不按 token。
153
103
 
154
104
  ---
155
105
 
156
- ## 🌐 实时搜索 — Tavily
106
+ ### 📊 AI 演示文稿生成
107
+
108
+ 输入主题或大纲,自动生成 **8-12 页专业演示文稿**,返回在线预览 + PDF 导出,无需任何设计功底。
109
+
110
+ **✅ 适合场景**
111
+ - 投资路演 BP、融资材料
112
+ - 产品发布 / 项目汇报 / 周会材料
113
+ - 提案文档、课件、培训材料
114
+
115
+ **💰 定价策略**
116
+
117
+ | 页数规模 | 预估费用 | 说明 |
118
+ |---------|---------|------|
119
+ | 标准(8-10页)| **约 ¥2.6~3.5** | 普通主题描述 |
120
+ | 丰富(10-12页)| **约 ¥3.5~4.5** | 详细大纲输入 |
121
+
122
+ > **按实际生成量计费**,不是固定价格。生成页数越多、内容越丰富,费用越高。
123
+ > 生成失败自动退款,不收任何费用。
124
+
125
+ **⏱ 生成时长:** 约 30~60 秒(异步任务,提交后轮询结果)
126
+
127
+ **🔌 API 调用**
128
+
129
+ ```bash
130
+ # 第一步:提交任务(立即返回,< 2 秒)
131
+ curl -X POST https://skillfree.tech/v1/presentations \
132
+ -H "Authorization: Bearer YOUR_API_KEY" \
133
+ -H "Content-Type: application/json" \
134
+ -d '{
135
+ "model": "ai-presentation",
136
+ "inputs": {
137
+ "text": "百图生科融资路演BP,AI驱动蛋白质大模型药物发现平台,xTrimoPGLM千亿参数,已融资2亿美元",
138
+ "language": "zh-cn"
139
+ }
140
+ }'
141
+ # 返回:{ "task_id": "xxx", "status": "processing", "estimated_credits": 300 }
142
+
143
+ # 第二步:轮询结果(每隔 8~10 秒查一次)
144
+ curl https://skillfree.tech/v1/tasks/{task_id} \
145
+ -H "Authorization: Bearer YOUR_API_KEY"
146
+ # 完成:{ "status": "completed", "result_url": "https://...", "credits_used": 273 }
147
+ # 失败:{ "status": "failed", "error": "...", "credits_used": 0 }
148
+ ```
149
+
150
+ **📋 参数说明**
157
151
 
158
- Tavily 是专为 AI 优化的搜索引擎,返回结构化摘要和来源,而非原始网页链接。适合实时信息查询、竞品调研、新闻追踪,配合对话模型使用效果最佳。
152
+ | 参数 | 必填 | 说明 |
153
+ |------|------|------|
154
+ | `inputs.text` | ✅ | 主题描述或大纲,越详细生成质量越高 |
155
+ | `inputs.language` | 否 | 语言代码,默认 `zh-cn`;英文用 `en` |
156
+ | `inputs.format` | 否 | 固定为 `presentation`(演示文稿)|
159
157
 
160
- Tavily 作为独立技能安装使用,可与 SkillFree 对话模型组合构建搜索增强工作流。
158
+ **🌍 支持语言:** `zh-cn`(简体中文)· `zh-tw`(繁体)· `en`(英文)· `ja`(日文)· `ko`(韩文)及 50+ 语言
161
159
 
162
160
  ---
163
161
 
package/bin/skillfree.js CHANGED
@@ -36,14 +36,15 @@ auth
36
36
  // ── pilot ─────────────────────────────────────────────────────────────────────
37
37
  program
38
38
  .command('pilot')
39
- .description('AI 智能调度:chat | image | video | ocr')
40
- .option('--type <type>', '调用类型: chat | image | video | ocr', 'chat')
41
- .option('--prompt <text>', '输入提示词')
42
- .option('--file <path>', '输入文件路径(ocr 用)')
43
- .option('--output <path>', '输出文件路径')
44
- .option('--model <model>', '指定模型(可选,不填自动选最优)')
45
- .option('--size <size>', '图像尺寸 或 视频分辨率(默认 1024x1024 / 1920x1080)')
46
- .option('--seconds <sec>', '视频时长:4 | 6 | 8(默认 8)', '8')
39
+ .description('AI 智能调度:chat | image | video | ocr | presentation')
40
+ .option('--type <type>', '调用类型: chat | image | video | ocr | presentation', 'chat')
41
+ .option('--prompt <text>', '输入提示词')
42
+ .option('--file <path>', '输入文件路径(ocr 用)')
43
+ .option('--output <path>', '输出文件路径')
44
+ .option('--model <model>', '指定模型(可选,不填自动选最优)')
45
+ .option('--size <size>', '图像尺寸 或 视频分辨率(默认 1024x1024 / 1920x1080)')
46
+ .option('--seconds <sec>', '视频时长:4 | 6 | 8(默认 8)', '8')
47
+ .option('--language <lang>', '演示文稿语言(默认 zh-cn)', 'zh-cn')
47
48
  .action(async (flags) => {
48
49
  const { pilot } = require('../scripts/commands/pilot')
49
50
  await pilot(flags).catch(e => { console.error('❌', e.message); process.exit(1) })
@@ -59,6 +60,27 @@ program
59
60
  await pilot({ type: 'chat', prompt, ...flags }).catch(e => { console.error('❌', e.message); process.exit(1) })
60
61
  })
61
62
 
63
+ // ── ppt(演示文稿快捷方式)──────────────────────────────────────────────────
64
+ program
65
+ .command('ppt <prompt>')
66
+ .description('AI 演示文稿生成(约 30-60 秒,按实际页数计费)')
67
+ .option('--language <lang>', '语言代码(默认 zh-cn;英文用 en)', 'zh-cn')
68
+ .action(async (prompt, flags) => {
69
+ const { createPresentation } = require('../scripts/commands/presentation')
70
+ await createPresentation({ prompt, language: flags.language })
71
+ .catch(e => { console.error('❌', e.message); process.exit(1) })
72
+ })
73
+
74
+ // ── tasks ─────────────────────────────────────────────────────────────────────
75
+ const tasks = program.command('tasks').description('任务管理')
76
+ tasks
77
+ .command('get <taskId>')
78
+ .description('查询异步任务状态(视频、演示文稿等)')
79
+ .action(async (taskId) => {
80
+ const { getTask } = require('../scripts/commands/presentation')
81
+ await getTask(taskId).catch(e => { console.error('❌', e.message); process.exit(1) })
82
+ })
83
+
62
84
  // ── models ────────────────────────────────────────────────────────────────────
63
85
  program
64
86
  .command('models')
package/install.sh CHANGED
@@ -33,7 +33,7 @@ cat << 'EOF'
33
33
  EOF
34
34
  echo -e "${NC}"
35
35
  echo -e " ${DIM}🦞 模型与技能严选平台 — 只选每个场景最好用的那一个${NC}"
36
- echo -e " ${DIM} Claude · GPT · Gemini · nano banana · Veo · 合合 OCR${NC}"
36
+ echo -e " ${DIM} Claude · GPT · Gemini · nano banana · Veo · 合合 OCR · AI演示文稿${NC}"
37
37
  echo ""
38
38
  echo -e " ${DIM}────────────────────────────────────────────────────${NC}"
39
39
  echo ""
@@ -62,13 +62,13 @@ echo -e " ${DIM}正在从 npm 安装 skillfree...${NC}"
62
62
  echo ""
63
63
 
64
64
  INSTALL_OK=false
65
- if npm install -g skillfree 2>&1 | sed 's/^/ /'; then
65
+ if npm install -g skillfree@latest 2>&1 | sed 's/^/ /'; then
66
66
  INSTALL_OK=true
67
67
  else
68
68
  echo ""
69
69
  warn "权限不足,尝试 sudo..."
70
70
  echo ""
71
- if sudo npm install -g skillfree 2>&1 | sed 's/^/ /'; then
71
+ if sudo npm install -g skillfree@latest 2>&1 | sed 's/^/ /'; then
72
72
  INSTALL_OK=true
73
73
  fi
74
74
  fi
@@ -146,6 +146,10 @@ echo -e " ${CYAN}做一个视频:${NC}"
146
146
  echo -e " ${DIM} $ skillfree pilot --type video --prompt \"一只未来感龙虾在霓虹都市中行走,电影感运镜\" --seconds 8 --size 1920x1080 --output ./video.mp4${NC}"
147
147
  echo ""
148
148
 
149
+ echo -e " ${CYAN}生成一份演示文稿(30-60秒):${NC}"
150
+ echo -e " ${DIM} $ skillfree ppt \"百图生科融资路演BP,AI蛋白质大模型药物发现平台,已融资2亿美元\"${NC}"
151
+ echo ""
152
+
149
153
  echo -e " ${CYAN}解析一份合同/文档:${NC}"
150
154
  echo -e " ${DIM} $ skillfree pilot --type ocr --file ./contract.pdf --output ./result.md${NC}"
151
155
  echo ""
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skillfree",
3
- "version": "0.1.39",
3
+ "version": "0.1.40",
4
4
  "description": "🦞 一个 API,满足所有龙虾技能需求",
5
5
  "main": "bin/skillfree.js",
6
6
  "bin": {
@@ -12,14 +12,9 @@ function injectToOpenclaw(apiKey) {
12
12
  // 1. 写入 openclaw.json
13
13
  const openclawDir = path.join(os.homedir(), '.openclaw')
14
14
  const cfgPath = path.join(openclawDir, 'openclaw.json')
15
- if (fs.existsSync(openclawDir) && fs.existsSync(cfgPath)) {
15
+ if (fs.existsSync(openclawDir)) {
16
16
  let cfg = {}
17
- try {
18
- cfg = JSON.parse(fs.readFileSync(cfgPath, 'utf8'))
19
- } catch (e) {
20
- console.warn('⚠️ openclaw.json 解析失败,跳过自动配置')
21
- return
22
- }
17
+ try { cfg = JSON.parse(fs.readFileSync(cfgPath, 'utf8')) } catch {}
23
18
  cfg.skills = cfg.skills || {}
24
19
  cfg.skills.entries = cfg.skills.entries || {}
25
20
  cfg.skills.entries['skillfree'] = {
@@ -156,7 +156,14 @@ async function pilot(flags) {
156
156
  return
157
157
  }
158
158
 
159
- throw new Error(`不支持的类型: ${type}\n可选: chat | image | video | ocr`)
159
+ // ── PRESENTATION(AI 演示文稿,异步轮询)────────────────────────────────────
160
+ if (type === 'presentation') {
161
+ const { createPresentation } = require('./presentation')
162
+ await createPresentation({ prompt, language: flags.language, format: flags.format })
163
+ return
164
+ }
165
+
166
+ throw new Error(`不支持的类型: ${type}\n可选: chat | image | video | ocr | presentation`)
160
167
  }
161
168
 
162
169
  module.exports = { pilot }
@@ -0,0 +1,100 @@
1
+ /**
2
+ * AI 演示文稿生成(异步任务 + 轮询)
3
+ * 底层走 /v1/presentations → 拿 task_id → 轮询 /v1/tasks/:taskId
4
+ */
5
+ const { getApiKey, BASE_URL, checkCredits } = require('../lib/client')
6
+
7
+ const POLL_INTERVAL_MS = 5000 // 每 5 秒轮询一次
8
+ const MAX_WAIT_MS = 120000 // 最多等 2 分钟
9
+
10
+ async function createPresentation(params) {
11
+ const apiKey = getApiKey()
12
+
13
+ if (!params.prompt && !params.text) {
14
+ throw new Error('--prompt 是必需的(主题描述或大纲)')
15
+ }
16
+
17
+ const inputText = params.prompt || params.text
18
+ const language = params.language || 'zh-cn'
19
+ const format = params.format || 'presentation'
20
+
21
+ await checkCredits()
22
+
23
+ // Step 1: 提交任务
24
+ console.log('📊 提交演示文稿生成任务...')
25
+ const submitRes = await fetch(`${BASE_URL}/v1/presentations`, {
26
+ method: 'POST',
27
+ headers: {
28
+ 'Authorization': `Bearer ${apiKey}`,
29
+ 'Content-Type': 'application/json',
30
+ },
31
+ body: JSON.stringify({
32
+ model: 'ai-presentation',
33
+ inputs: { text: inputText, language, format },
34
+ }),
35
+ })
36
+
37
+ const submitData = await submitRes.json()
38
+ if (!submitRes.ok || !submitData.task_id) {
39
+ throw new Error(submitData.error || submitData.message || `提交失败:HTTP ${submitRes.status}`)
40
+ }
41
+
42
+ const taskId = submitData.task_id
43
+ const estimated = submitData.estimated_credits || 300
44
+ console.log(`⏳ 生成中(预计约 30-60 秒)...`)
45
+ console.log(` task_id: ${taskId}`)
46
+ console.log(` 预计消耗: ${estimated} 积分(¥${(estimated / 100).toFixed(2)})\n`)
47
+
48
+ // Step 2: 轮询结果
49
+ const deadline = Date.now() + MAX_WAIT_MS
50
+ while (Date.now() < deadline) {
51
+ await new Promise(r => setTimeout(r, POLL_INTERVAL_MS))
52
+
53
+ const pollRes = await fetch(`${BASE_URL}/v1/tasks/${taskId}`, {
54
+ headers: { 'Authorization': `Bearer ${apiKey}` },
55
+ })
56
+ const task = await pollRes.json()
57
+
58
+ if (task.status === 'completed') {
59
+ const credits = task.credits_used ?? estimated
60
+ console.log(`✅ 生成完成!(实际消耗 ${credits} 积分,¥${(credits / 100).toFixed(2)})\n`)
61
+ console.log(`🔗 在线预览:${task.result_url}`)
62
+ if (task.upstream_id && task.upstream_id.startsWith('http')) {
63
+ console.log(`📄 PDF 导出:${task.upstream_id}`)
64
+ }
65
+ return task
66
+ }
67
+
68
+ if (task.status === 'failed') {
69
+ throw new Error(`生成失败:${task.error || '未知错误'}(积分已自动退还)`)
70
+ }
71
+
72
+ // processing → 继续等待
73
+ process.stdout.write('.')
74
+ }
75
+
76
+ throw new Error('生成超时(超过 2 分钟),请稍后用以下命令查询进度:\n skillfree tasks get ' + taskId)
77
+ }
78
+
79
+ async function getTask(taskId) {
80
+ const apiKey = getApiKey()
81
+ const res = await fetch(`${BASE_URL}/v1/tasks/${taskId}`, {
82
+ headers: { 'Authorization': `Bearer ${apiKey}` },
83
+ })
84
+ const task = await res.json()
85
+
86
+ if (!res.ok) throw new Error(task.error || `HTTP ${res.status}`)
87
+
88
+ const statusIcon = { completed: '✅', failed: '❌', processing: '⏳' }[task.status] || '❓'
89
+ console.log(`\n${statusIcon} 任务 ${task.task_id}`)
90
+ console.log(` 状态:${task.status}`)
91
+ console.log(` 模型:${task.model}`)
92
+ if (task.result_url) console.log(` 在线预览:${task.result_url}`)
93
+ if (task.upstream_id?.startsWith('http')) console.log(` PDF 导出:${task.upstream_id}`)
94
+ if (task.error) console.log(` 错误:${task.error}`)
95
+ console.log(` 创建:${task.created_at}`)
96
+ console.log(` 更新:${task.updated_at}`)
97
+ return task
98
+ }
99
+
100
+ module.exports = { createPresentation, getTask }