@optima-chat/comfy-cli 0.1.0

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.
Files changed (64) hide show
  1. package/.claude/settings.local.json +20 -0
  2. package/LICENSE +21 -0
  3. package/README.md +228 -0
  4. package/dist/commands/config.d.ts +3 -0
  5. package/dist/commands/config.d.ts.map +1 -0
  6. package/dist/commands/config.js +87 -0
  7. package/dist/commands/config.js.map +1 -0
  8. package/dist/commands/edit.d.ts +3 -0
  9. package/dist/commands/edit.d.ts.map +1 -0
  10. package/dist/commands/edit.js +63 -0
  11. package/dist/commands/edit.js.map +1 -0
  12. package/dist/commands/generate.d.ts +3 -0
  13. package/dist/commands/generate.d.ts.map +1 -0
  14. package/dist/commands/generate.js +51 -0
  15. package/dist/commands/generate.js.map +1 -0
  16. package/dist/commands/interrupt.d.ts +3 -0
  17. package/dist/commands/interrupt.d.ts.map +1 -0
  18. package/dist/commands/interrupt.js +19 -0
  19. package/dist/commands/interrupt.js.map +1 -0
  20. package/dist/commands/model.d.ts +3 -0
  21. package/dist/commands/model.d.ts.map +1 -0
  22. package/dist/commands/model.js +44 -0
  23. package/dist/commands/model.js.map +1 -0
  24. package/dist/commands/node.d.ts +3 -0
  25. package/dist/commands/node.d.ts.map +1 -0
  26. package/dist/commands/node.js +70 -0
  27. package/dist/commands/node.js.map +1 -0
  28. package/dist/commands/queue.d.ts +3 -0
  29. package/dist/commands/queue.d.ts.map +1 -0
  30. package/dist/commands/queue.js +97 -0
  31. package/dist/commands/queue.js.map +1 -0
  32. package/dist/commands/system.d.ts +3 -0
  33. package/dist/commands/system.d.ts.map +1 -0
  34. package/dist/commands/system.js +47 -0
  35. package/dist/commands/system.js.map +1 -0
  36. package/dist/commands/video.d.ts +3 -0
  37. package/dist/commands/video.d.ts.map +1 -0
  38. package/dist/commands/video.js +79 -0
  39. package/dist/commands/video.js.map +1 -0
  40. package/dist/commands/workflow.d.ts +3 -0
  41. package/dist/commands/workflow.d.ts.map +1 -0
  42. package/dist/commands/workflow.js +131 -0
  43. package/dist/commands/workflow.js.map +1 -0
  44. package/dist/index.d.ts +3 -0
  45. package/dist/index.d.ts.map +1 -0
  46. package/dist/index.js +31 -0
  47. package/dist/index.js.map +1 -0
  48. package/dist/services/api.d.ts +17 -0
  49. package/dist/services/api.d.ts.map +1 -0
  50. package/dist/services/api.js +62 -0
  51. package/dist/services/api.js.map +1 -0
  52. package/dist/services/config.d.ts +12 -0
  53. package/dist/services/config.d.ts.map +1 -0
  54. package/dist/services/config.js +21 -0
  55. package/dist/services/config.js.map +1 -0
  56. package/dist/utils/logger.d.ts +5 -0
  57. package/dist/utils/logger.d.ts.map +1 -0
  58. package/dist/utils/logger.js +14 -0
  59. package/dist/utils/logger.js.map +1 -0
  60. package/dist/utils/workflow.d.ts +17 -0
  61. package/dist/utils/workflow.d.ts.map +1 -0
  62. package/dist/utils/workflow.js +78 -0
  63. package/dist/utils/workflow.js.map +1 -0
  64. package/package.json +64 -0
@@ -0,0 +1,20 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "WebFetch(domain:github.com)",
5
+ "WebFetch(domain:raw.githubusercontent.com)",
6
+ "Bash(mkdir:*)",
7
+ "Bash(git init:*)",
8
+ "Bash(gh repo create:*)",
9
+ "Bash(git push:*)",
10
+ "Bash(git add:*)",
11
+ "Bash(git commit:*)",
12
+ "Bash(gh api:*)",
13
+ "Bash(npm install)",
14
+ "Bash(npm run build:*)",
15
+ "Bash(npm publish:*)"
16
+ ],
17
+ "deny": [],
18
+ "ask": []
19
+ }
20
+ }
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Optima-Chat
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,228 @@
1
+ # ComfyUI CLI
2
+
3
+ 面向 [ComfyUI](https://github.com/comfyanonymous/ComfyUI) 的命令行工具,专为 LLM 交互设计,特别适配 Claude Code。
4
+
5
+ ## 特性
6
+
7
+ - 🚀 **简单易用**:所有 ComfyUI 操作的简单命令
8
+ - 🤖 **LLM 友好**:优化自然语言交互体验
9
+ - 📊 **实时监控**:WebSocket 支持实时进度更新
10
+ - 🎨 **图像处理**:无缝上传和下载图像
11
+ - 📦 **模型管理**:列出和查询可用模型
12
+ - ⚡ **队列控制**:管理工作流执行队列
13
+
14
+ ## 安装
15
+
16
+ ```bash
17
+ npm install -g @optima-chat/comfy-cli
18
+ ```
19
+
20
+ ## 快速开始
21
+
22
+ ```bash
23
+ # 检查 ComfyUI 是否运行
24
+ comfy system stats
25
+
26
+ # 提交工作流
27
+ comfy workflow submit workflow.json
28
+
29
+ # 查看队列状态
30
+ comfy queue status
31
+
32
+ # 列出可用模型
33
+ comfy model list --type checkpoints
34
+ ```
35
+
36
+ ## 命令
37
+
38
+ ### ✨ 快捷功能命令
39
+
40
+ #### 图像生成(文生图)
41
+
42
+ ```bash
43
+ comfy generate "a beautiful sunset over mountains"
44
+ comfy generate "cyberpunk city" --width 1024 --height 768 --steps 30
45
+ ```
46
+
47
+ **选项**:
48
+ - `--width <number>`: 图像宽度(默认:1024)
49
+ - `--height <number>`: 图像高度(默认:1024)
50
+ - `--steps <number>`: 采样步数(默认:20)
51
+ - `--cfg <number>`: CFG scale(默认:7.0)
52
+ - `--seed <number>`: 随机种子
53
+ - `--json`: 输出 JSON 格式
54
+
55
+ #### 图像编辑(图生图)
56
+
57
+ ```bash
58
+ comfy edit input.png "add more details"
59
+ comfy edit photo.jpg "make it anime style" --strength 0.8
60
+ ```
61
+
62
+ **选项**:
63
+ - `--strength <number>`: 变换强度 0-1(默认:0.75)
64
+ - `--steps`, `--cfg`, `--seed`, `--json`: 同上
65
+
66
+ #### 视频生成(图生视频)
67
+
68
+ ```bash
69
+ comfy video portrait.png --prompt "smooth motion" --frames 60 --fps 30
70
+ comfy video scene.jpg --width 512 --height 512
71
+ ```
72
+
73
+ **选项**:
74
+ - `-p, --prompt <text>`: 运动描述提示词
75
+ - `-n, --negative <text>`: 负向提示词
76
+ - `--width`, `--height`: 视频尺寸(默认:512x512)
77
+ - `--frames <number>`: 帧数(默认:60)
78
+ - `--fps <number>`: 帧率(默认:30)
79
+ - `--steps`, `--cfg`, `--seed`, `--json`: 同上
80
+
81
+ ---
82
+
83
+ ### 🔧 核心命令
84
+
85
+ #### 工作流管理
86
+
87
+ ```bash
88
+ # 提交自定义工作流
89
+ comfy workflow submit workflow.json
90
+
91
+ # 查看历史记录
92
+ comfy workflow list --limit 10
93
+
94
+ # 获取执行结果
95
+ comfy workflow get <prompt_id>
96
+ ```
97
+
98
+ #### 队列管理
99
+
100
+ ```bash
101
+ # 查看队列状态
102
+ comfy queue status
103
+
104
+ # 清空队列
105
+ comfy queue clear --confirm
106
+
107
+ # 删除指定任务
108
+ comfy queue delete <item_id>
109
+
110
+ # 中断当前执行
111
+ comfy interrupt
112
+ ```
113
+
114
+ #### 模型管理
115
+
116
+ ```bash
117
+ # 列出所有模型
118
+ comfy model list
119
+
120
+ # 列出特定类型模型
121
+ comfy model list --type checkpoints
122
+ comfy model list --type loras --json
123
+ ```
124
+
125
+ #### 节点信息
126
+
127
+ ```bash
128
+ # 列出所有节点类型
129
+ comfy node list
130
+
131
+ # 过滤节点
132
+ comfy node list --filter sampler
133
+
134
+ # 查看节点详情
135
+ comfy node info KSampler --json
136
+ ```
137
+
138
+ #### 系统信息
139
+
140
+ ```bash
141
+ # 查看系统状态
142
+ comfy system stats
143
+
144
+ # JSON 格式输出
145
+ comfy system stats --json
146
+ ```
147
+
148
+ #### 配置管理
149
+
150
+ ```bash
151
+ # 设置服务器地址
152
+ comfy config set server http://localhost:8188
153
+
154
+ # 查看当前配置
155
+ comfy config list
156
+
157
+ # 重置配置
158
+ comfy config reset --confirm
159
+ ```
160
+
161
+ ## 配置
162
+
163
+ 默认配置存储在 `~/.comfy-cli/config.json`:
164
+
165
+ ```json
166
+ {
167
+ "server": "http://dev.optima.chat:8188",
168
+ "timeout": 30000,
169
+ "autoConnect": true,
170
+ "outputDir": "./comfy-output"
171
+ }
172
+ ```
173
+
174
+ ## 在 Claude Code 中使用
175
+
176
+ 在你的 `~/.claude/CLAUDE.md` 中添加:
177
+
178
+ ```markdown
179
+ ## ComfyUI CLI
180
+
181
+ **常见需求映射**:
182
+ - "提交工作流 XX" → `comfy workflow submit XX`
183
+ - "查看队列" → `comfy queue status`
184
+ - "列出所有 checkpoint 模型" → `comfy model list --type checkpoints`
185
+ - "上传图片到 ComfyUI" → `comfy image upload <path>`
186
+ - "中断当前任务" → `comfy interrupt`
187
+ ```
188
+
189
+ ## 系统要求
190
+
191
+ - Node.js >= 18.0.0
192
+ - ComfyUI 实例运行中(默认:http://dev.optima.chat:8188)
193
+
194
+ ## 开发
195
+
196
+ ```bash
197
+ # 安装依赖
198
+ npm install
199
+
200
+ # 开发模式运行
201
+ npm run dev
202
+
203
+ # 构建
204
+ npm run build
205
+
206
+ # 本地测试
207
+ npm link
208
+ comfy --help
209
+ ```
210
+
211
+ ## 文档
212
+
213
+ - [技术设计](./docs/technical-design.md) - 架构和实现细节
214
+ - [API 参考](./docs/api.md) - API 文档(即将推出)
215
+ - [使用示例](./docs/examples.md) - 使用示例(即将推出)
216
+
217
+ ## 许可证
218
+
219
+ MIT
220
+
221
+ ## 贡献
222
+
223
+ 欢迎贡献!请随时提交 Pull Request。
224
+
225
+ ## 相关链接
226
+
227
+ - [ComfyUI](https://github.com/comfyanonymous/ComfyUI)
228
+ - [Optima CLI](https://github.com/Optima-Chat/optima-cli) - 本项目的灵感来源
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerConfigCommand(program: Command): void;
3
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,QA4FrD"}
@@ -0,0 +1,87 @@
1
+ import { config, getConfig, setConfig, resetConfig } from '../services/config';
2
+ import { success, error, info } from '../utils/logger';
3
+ import Table from 'cli-table3';
4
+ export function registerConfigCommand(program) {
5
+ const configCmd = program
6
+ .command('config')
7
+ .description('配置管理');
8
+ // comfy config set
9
+ configCmd
10
+ .command('set')
11
+ .description('设置配置项')
12
+ .argument('<key>', '配置键 (server, timeout, autoConnect, outputDir)')
13
+ .argument('<value>', '配置值')
14
+ .action((key, value) => {
15
+ try {
16
+ if (!['server', 'timeout', 'autoConnect', 'outputDir'].includes(key)) {
17
+ error(`无效的配置键: ${key}`);
18
+ info('可用配置: server, timeout, autoConnect, outputDir');
19
+ process.exit(1);
20
+ }
21
+ let parsedValue = value;
22
+ // 类型转换
23
+ if (key === 'timeout') {
24
+ parsedValue = parseInt(value);
25
+ if (isNaN(parsedValue)) {
26
+ error('timeout 必须是数字');
27
+ process.exit(1);
28
+ }
29
+ }
30
+ else if (key === 'autoConnect') {
31
+ parsedValue = value === 'true';
32
+ }
33
+ setConfig(key, parsedValue);
34
+ success(`已设置 ${key} = ${parsedValue}`);
35
+ }
36
+ catch (err) {
37
+ error(`设置失败: ${err}`);
38
+ process.exit(1);
39
+ }
40
+ });
41
+ // comfy config list
42
+ configCmd
43
+ .command('list')
44
+ .description('查看当前配置')
45
+ .option('--json', '输出 JSON 格式')
46
+ .action((options) => {
47
+ try {
48
+ const cfg = getConfig();
49
+ if (options.json) {
50
+ console.log(JSON.stringify(cfg, null, 2));
51
+ }
52
+ else {
53
+ const table = new Table({
54
+ head: ['配置项', '值'],
55
+ colWidths: [20, 60],
56
+ });
57
+ table.push(['Server', cfg.server], ['Timeout', `${cfg.timeout}ms`], ['Auto Connect', cfg.autoConnect ? '✓' : '✗'], ['Output Dir', cfg.outputDir]);
58
+ console.log(table.toString());
59
+ info(`配置文件位置: ${config.path}`);
60
+ }
61
+ }
62
+ catch (err) {
63
+ error(`获取配置失败: ${err}`);
64
+ process.exit(1);
65
+ }
66
+ });
67
+ // comfy config reset
68
+ configCmd
69
+ .command('reset')
70
+ .description('重置为默认配置')
71
+ .option('--confirm', '确认重置')
72
+ .action((options) => {
73
+ try {
74
+ if (!options.confirm) {
75
+ error('请使用 --confirm 确认重置操作');
76
+ process.exit(1);
77
+ }
78
+ resetConfig();
79
+ success('配置已重置为默认值');
80
+ }
81
+ catch (err) {
82
+ error(`重置失败: ${err}`);
83
+ process.exit(1);
84
+ }
85
+ });
86
+ }
87
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerEditCommand(program: Command): void;
3
+ //# sourceMappingURL=edit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edit.d.ts","sourceRoot":"","sources":["../../src/commands/edit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,QA6DnD"}
@@ -0,0 +1,63 @@
1
+ import * as path from 'path';
2
+ import * as fs from 'fs';
3
+ import { ComfyAPIClient } from '../services/api';
4
+ import { loadWorkflow, getBuiltinWorkflowPath, replaceVariables } from '../utils/workflow';
5
+ import { success, error, info } from '../utils/logger';
6
+ export function registerEditCommand(program) {
7
+ program
8
+ .command('edit')
9
+ .description('编辑图像(图生图)')
10
+ .argument('<image>', '输入图像路径')
11
+ .argument('<prompt>', '编辑描述提示词')
12
+ .option('-o, --output <dir>', '输出目录', './output')
13
+ .option('--strength <number>', '变换强度 (0-1)', '0.75')
14
+ .option('--steps <number>', '采样步数', '20')
15
+ .option('--cfg <number>', 'CFG scale', '7.0')
16
+ .option('--seed <number>', '随机种子', String(Math.floor(Math.random() * 1000000)))
17
+ .option('--json', '输出 JSON 格式')
18
+ .action(async (image, prompt, options) => {
19
+ try {
20
+ // 检查图像文件是否存在
21
+ const imagePath = path.resolve(image);
22
+ if (!fs.existsSync(imagePath)) {
23
+ error(`图像文件不存在: ${imagePath}`);
24
+ process.exit(1);
25
+ }
26
+ info(`正在编辑图像: "${path.basename(image)}"`);
27
+ info(`提示词: "${prompt}"`);
28
+ // 先上传图像
29
+ const client = new ComfyAPIClient();
30
+ const imageBuffer = fs.readFileSync(imagePath);
31
+ const uploadResult = await client.uploadImage(imageBuffer, path.basename(imagePath));
32
+ // 加载内置 workflow
33
+ const workflowPath = getBuiltinWorkflowPath('flux_image_to_image.json');
34
+ const workflow = loadWorkflow(workflowPath);
35
+ // 准备参数
36
+ const params = {
37
+ prompt,
38
+ input_image: uploadResult.name,
39
+ strength: parseFloat(options.strength),
40
+ steps: parseInt(options.steps),
41
+ cfg_scale: parseFloat(options.cfg),
42
+ seed: parseInt(options.seed),
43
+ };
44
+ // 替换变量
45
+ const processedWorkflow = replaceVariables(workflow, params);
46
+ // 提交到 ComfyUI
47
+ const result = await client.submitWorkflow(processedWorkflow);
48
+ if (options.json) {
49
+ console.log(JSON.stringify(result, null, 2));
50
+ }
51
+ else {
52
+ success(`图像编辑任务已提交`);
53
+ info(`Prompt ID: ${result.prompt_id}`);
54
+ info(`使用 'comfy workflow get ${result.prompt_id}' 查看结果`);
55
+ }
56
+ }
57
+ catch (err) {
58
+ error(`编辑失败: ${err}`);
59
+ process.exit(1);
60
+ }
61
+ });
62
+ }
63
+ //# sourceMappingURL=edit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edit.js","sourceRoot":"","sources":["../../src/commands/edit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerGenerateCommand(program: Command): void;
3
+ //# sourceMappingURL=generate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/commands/generate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,QAiDvD"}
@@ -0,0 +1,51 @@
1
+ import { ComfyAPIClient } from '../services/api';
2
+ import { loadWorkflow, getBuiltinWorkflowPath, replaceVariables } from '../utils/workflow';
3
+ import { success, error, info } from '../utils/logger';
4
+ export function registerGenerateCommand(program) {
5
+ program
6
+ .command('generate')
7
+ .description('生成图像(文生图)')
8
+ .argument('<prompt>', '图像描述提示词')
9
+ .option('-o, --output <dir>', '输出目录', './output')
10
+ .option('--width <number>', '图像宽度', '1024')
11
+ .option('--height <number>', '图像高度', '1024')
12
+ .option('--steps <number>', '采样步数', '20')
13
+ .option('--cfg <number>', 'CFG scale', '7.0')
14
+ .option('--seed <number>', '随机种子', String(Math.floor(Math.random() * 1000000)))
15
+ .option('--json', '输出 JSON 格式')
16
+ .action(async (prompt, options) => {
17
+ try {
18
+ info(`正在生成图像: "${prompt}"`);
19
+ // 加载内置 workflow
20
+ const workflowPath = getBuiltinWorkflowPath('flux_text_to_image.json');
21
+ const workflow = loadWorkflow(workflowPath);
22
+ // 准备参数
23
+ const params = {
24
+ prompt,
25
+ width: parseInt(options.width),
26
+ height: parseInt(options.height),
27
+ steps: parseInt(options.steps),
28
+ cfg_scale: parseFloat(options.cfg),
29
+ seed: parseInt(options.seed),
30
+ };
31
+ // 替换变量
32
+ const processedWorkflow = replaceVariables(workflow, params);
33
+ // 提交到 ComfyUI
34
+ const client = new ComfyAPIClient();
35
+ const result = await client.submitWorkflow(processedWorkflow);
36
+ if (options.json) {
37
+ console.log(JSON.stringify(result, null, 2));
38
+ }
39
+ else {
40
+ success(`图像生成任务已提交`);
41
+ info(`Prompt ID: ${result.prompt_id}`);
42
+ info(`使用 'comfy workflow get ${result.prompt_id}' 查看结果`);
43
+ }
44
+ }
45
+ catch (err) {
46
+ error(`生成失败: ${err}`);
47
+ process.exit(1);
48
+ }
49
+ });
50
+ }
51
+ //# sourceMappingURL=generate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.js","sourceRoot":"","sources":["../../src/commands/generate.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC3F,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAEvD,MAAM,UAAU,uBAAuB,CAAC,OAAgB;IACtD,OAAO;SACJ,OAAO,CAAC,UAAU,CAAC;SACnB,WAAW,CAAC,WAAW,CAAC;SACxB,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC;SAC/B,MAAM,CAAC,oBAAoB,EAAE,MAAM,EAAE,UAAU,CAAC;SAChD,MAAM,CAAC,kBAAkB,EAAE,MAAM,EAAE,MAAM,CAAC;SAC1C,MAAM,CAAC,mBAAmB,EAAE,MAAM,EAAE,MAAM,CAAC;SAC3C,MAAM,CAAC,kBAAkB,EAAE,MAAM,EAAE,IAAI,CAAC;SACxC,MAAM,CAAC,gBAAgB,EAAE,WAAW,EAAE,KAAK,CAAC;SAC5C,MAAM,CAAC,iBAAiB,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC;SAC9E,MAAM,CAAC,QAAQ,EAAE,YAAY,CAAC;SAC9B,MAAM,CAAC,KAAK,EAAE,MAAc,EAAE,OAAO,EAAE,EAAE;QACxC,IAAI,CAAC;YACH,IAAI,CAAC,YAAY,MAAM,GAAG,CAAC,CAAC;YAE5B,gBAAgB;YAChB,MAAM,YAAY,GAAG,sBAAsB,CAAC,yBAAyB,CAAC,CAAC;YACvE,MAAM,QAAQ,GAAG,YAAY,CAAC,YAAY,CAAC,CAAC;YAE5C,OAAO;YACP,MAAM,MAAM,GAAG;gBACb,MAAM;gBACN,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC;gBAC9B,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC;gBAChC,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC;gBAC9B,SAAS,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC;gBAClC,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC;aAC7B,CAAC;YAEF,OAAO;YACP,MAAM,iBAAiB,GAAG,gBAAgB,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;YAE7D,cAAc;YACd,MAAM,MAAM,GAAG,IAAI,cAAc,EAAE,CAAC;YACpC,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,iBAAiB,CAAC,CAAC;YAE9D,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;gBACjB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YAC/C,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,WAAW,CAAC,CAAC;gBACrB,IAAI,CAAC,cAAc,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC;gBACvC,IAAI,CAAC,0BAA0B,MAAM,CAAC,SAAS,QAAQ,CAAC,CAAC;YAC3D,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,KAAK,CAAC,SAAS,GAAG,EAAE,CAAC,CAAC;YACtB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerInterruptCommand(program: Command): void;
3
+ //# sourceMappingURL=interrupt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interrupt.d.ts","sourceRoot":"","sources":["../../src/commands/interrupt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,OAAO,QAexD"}
@@ -0,0 +1,19 @@
1
+ import { ComfyAPIClient } from '../services/api';
2
+ import { error, success } from '../utils/logger';
3
+ export function registerInterruptCommand(program) {
4
+ program
5
+ .command('interrupt')
6
+ .description('中断当前执行')
7
+ .action(async () => {
8
+ try {
9
+ const client = new ComfyAPIClient();
10
+ await client.interrupt();
11
+ success('已发送中断信号');
12
+ }
13
+ catch (err) {
14
+ error(`中断失败: ${err}`);
15
+ process.exit(1);
16
+ }
17
+ });
18
+ }
19
+ //# sourceMappingURL=interrupt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interrupt.js","sourceRoot":"","sources":["../../src/commands/interrupt.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAEjD,MAAM,UAAU,wBAAwB,CAAC,OAAgB;IACvD,OAAO;SACJ,OAAO,CAAC,WAAW,CAAC;SACpB,WAAW,CAAC,QAAQ,CAAC;SACrB,MAAM,CAAC,KAAK,IAAI,EAAE;QACjB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,cAAc,EAAE,CAAC;YACpC,MAAM,MAAM,CAAC,SAAS,EAAE,CAAC;YAEzB,OAAO,CAAC,SAAS,CAAC,CAAC;QACrB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,KAAK,CAAC,SAAS,GAAG,EAAE,CAAC,CAAC;YACtB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerModelCommand(program: Command): void;
3
+ //# sourceMappingURL=model.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../src/commands/model.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,QA2CpD"}
@@ -0,0 +1,44 @@
1
+ import { ComfyAPIClient } from '../services/api';
2
+ import { error, info } from '../utils/logger';
3
+ import Table from 'cli-table3';
4
+ export function registerModelCommand(program) {
5
+ const modelCmd = program
6
+ .command('model')
7
+ .description('模型管理');
8
+ // comfy model list
9
+ modelCmd
10
+ .command('list')
11
+ .description('列出可用模型')
12
+ .option('--type <type>', '模型类型 (checkpoints, loras, vae, etc.)')
13
+ .option('--json', '输出 JSON 格式')
14
+ .action(async (options) => {
15
+ try {
16
+ const client = new ComfyAPIClient();
17
+ const models = await client.listModels(options.type);
18
+ if (options.json) {
19
+ console.log(JSON.stringify(models, null, 2));
20
+ }
21
+ else {
22
+ const modelList = Array.isArray(models) ? models : Object.values(models).flat();
23
+ if (modelList.length === 0) {
24
+ info('未找到模型');
25
+ return;
26
+ }
27
+ const table = new Table({
28
+ head: ['序号', '模型名称'],
29
+ colWidths: [8, 70],
30
+ });
31
+ modelList.forEach((model, index) => {
32
+ table.push([index + 1, model]);
33
+ });
34
+ console.log(table.toString());
35
+ info(`共 ${modelList.length} 个模型${options.type ? ` (${options.type})` : ''}`);
36
+ }
37
+ }
38
+ catch (err) {
39
+ error(`获取模型列表失败: ${err}`);
40
+ process.exit(1);
41
+ }
42
+ });
43
+ }
44
+ //# sourceMappingURL=model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model.js","sourceRoot":"","sources":["../../src/commands/model.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,KAAK,MAAM,YAAY,CAAC;AAE/B,MAAM,UAAU,oBAAoB,CAAC,OAAgB;IACnD,MAAM,QAAQ,GAAG,OAAO;SACrB,OAAO,CAAC,OAAO,CAAC;SAChB,WAAW,CAAC,MAAM,CAAC,CAAC;IAEvB,mBAAmB;IACnB,QAAQ;SACL,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,QAAQ,CAAC;SACrB,MAAM,CAAC,eAAe,EAAE,sCAAsC,CAAC;SAC/D,MAAM,CAAC,QAAQ,EAAE,YAAY,CAAC;SAC9B,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;QACxB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,cAAc,EAAE,CAAC;YACpC,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YAErD,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;gBACjB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YAC/C,CAAC;iBAAM,CAAC;gBACN,MAAM,SAAS,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;gBAEhF,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBAC3B,IAAI,CAAC,OAAO,CAAC,CAAC;oBACd,OAAO;gBACT,CAAC;gBAED,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC;oBACtB,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC;oBACpB,SAAS,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC;iBACnB,CAAC,CAAC;gBAEH,SAAS,CAAC,OAAO,CAAC,CAAC,KAAa,EAAE,KAAa,EAAE,EAAE;oBACjD,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;gBACjC,CAAC,CAAC,CAAC;gBAEH,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;gBAC9B,IAAI,CAAC,KAAK,SAAS,CAAC,MAAM,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC/E,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,KAAK,CAAC,aAAa,GAAG,EAAE,CAAC,CAAC;YAC1B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerNodeCommand(program: Command): void;
3
+ //# sourceMappingURL=node.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../src/commands/node.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,QAwEnD"}