@optima-chat/comfy-cli 0.7.2 → 0.9.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 (50) hide show
  1. package/.claude/settings.local.json +5 -1
  2. package/dist/backends/bfl.d.ts +18 -0
  3. package/dist/backends/bfl.d.ts.map +1 -0
  4. package/dist/backends/bfl.js +115 -0
  5. package/dist/backends/bfl.js.map +1 -0
  6. package/dist/backends/comfyui.d.ts +10 -0
  7. package/dist/backends/comfyui.d.ts.map +1 -0
  8. package/dist/backends/comfyui.js +108 -0
  9. package/dist/backends/comfyui.js.map +1 -0
  10. package/dist/backends/index.d.ts +24 -0
  11. package/dist/backends/index.d.ts.map +1 -0
  12. package/dist/backends/index.js +59 -0
  13. package/dist/backends/index.js.map +1 -0
  14. package/dist/backends/types.d.ts +97 -0
  15. package/dist/backends/types.d.ts.map +1 -0
  16. package/dist/backends/types.js +2 -0
  17. package/dist/backends/types.js.map +1 -0
  18. package/dist/backends/video-dashscope.d.ts +10 -0
  19. package/dist/backends/video-dashscope.d.ts.map +1 -0
  20. package/dist/backends/video-dashscope.js +94 -0
  21. package/dist/backends/video-dashscope.js.map +1 -0
  22. package/dist/backends/video-index.d.ts +23 -0
  23. package/dist/backends/video-index.d.ts.map +1 -0
  24. package/dist/backends/video-index.js +57 -0
  25. package/dist/backends/video-index.js.map +1 -0
  26. package/dist/commands/config.d.ts.map +1 -1
  27. package/dist/commands/config.js +22 -17
  28. package/dist/commands/config.js.map +1 -1
  29. package/dist/commands/image.d.ts +3 -0
  30. package/dist/commands/image.d.ts.map +1 -0
  31. package/dist/commands/image.js +148 -0
  32. package/dist/commands/image.js.map +1 -0
  33. package/dist/commands/video.d.ts.map +1 -1
  34. package/dist/commands/video.js +205 -119
  35. package/dist/commands/video.js.map +1 -1
  36. package/dist/index.js +2 -0
  37. package/dist/index.js.map +1 -1
  38. package/dist/services/bfl-api.d.ts +67 -0
  39. package/dist/services/bfl-api.d.ts.map +1 -0
  40. package/dist/services/bfl-api.js +99 -0
  41. package/dist/services/bfl-api.js.map +1 -0
  42. package/dist/services/config.d.ts +21 -1
  43. package/dist/services/config.d.ts.map +1 -1
  44. package/dist/services/config.js +41 -0
  45. package/dist/services/config.js.map +1 -1
  46. package/dist/services/dashscope-api.d.ts +69 -0
  47. package/dist/services/dashscope-api.d.ts.map +1 -0
  48. package/dist/services/dashscope-api.js +134 -0
  49. package/dist/services/dashscope-api.js.map +1 -0
  50. package/package.json +1 -1
@@ -34,7 +34,11 @@
34
34
  "Bash(git checkout:*)",
35
35
  "Bash(git branch:*)",
36
36
  "Bash(gh run view:*)",
37
- "WebFetch(domain:code.claude.com)"
37
+ "WebFetch(domain:code.claude.com)",
38
+ "Bash(ssh:*)",
39
+ "Bash(wc:*)",
40
+ "WebFetch(domain:modelstudio.console.alibabacloud.com)",
41
+ "WebFetch(domain:help.aliyun.com)"
38
42
  ],
39
43
  "deny": [],
40
44
  "ask": []
@@ -0,0 +1,18 @@
1
+ import type { ImageBackend, ImageParams, ImageResult } from './types.js';
2
+ export declare class BFLBackend implements ImageBackend {
3
+ readonly name: "bfl";
4
+ private client;
5
+ constructor(apiKey: string);
6
+ isAvailable(): boolean;
7
+ generate(params: ImageParams): Promise<ImageResult>;
8
+ getStatus(id: string): Promise<ImageResult>;
9
+ /**
10
+ * 处理输入图像,转换为 base64
11
+ */
12
+ private processInputImages;
13
+ /**
14
+ * 将输入图像映射到 BFL 参数
15
+ */
16
+ private mapInputImages;
17
+ }
18
+ //# sourceMappingURL=bfl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bfl.d.ts","sourceRoot":"","sources":["../../src/backends/bfl.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAIzE,qBAAa,UAAW,YAAW,YAAY;IAC7C,QAAQ,CAAC,IAAI,EAAG,KAAK,CAAU;IAC/B,OAAO,CAAC,MAAM,CAAY;gBAEd,MAAM,EAAE,MAAM;IAI1B,WAAW,IAAI,OAAO;IAIhB,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;IA4DnD,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAsBjD;;OAEG;YACW,kBAAkB;IAQhC;;OAEG;IACH,OAAO,CAAC,cAAc;CAcvB"}
@@ -0,0 +1,115 @@
1
+ import { BFLClient } from '../services/bfl-api.js';
2
+ import { getConfig } from '../services/config.js';
3
+ export class BFLBackend {
4
+ name = 'bfl';
5
+ client;
6
+ constructor(apiKey) {
7
+ this.client = new BFLClient(apiKey);
8
+ }
9
+ isAvailable() {
10
+ return true;
11
+ }
12
+ async generate(params) {
13
+ const startTime = Date.now();
14
+ const cfg = getConfig();
15
+ // 1. 处理输入图像(转为 base64)
16
+ const inputImageBase64s = await this.processInputImages(params.inputImages);
17
+ // 2. 构建 BFL 请求参数
18
+ const bflParams = {
19
+ prompt: params.prompt,
20
+ width: params.width,
21
+ height: params.height,
22
+ seed: params.seed,
23
+ output_format: params.outputFormat || cfg.bfl?.outputFormat || 'png',
24
+ safety_tolerance: params.safetyTolerance ?? cfg.bfl?.safetyTolerance ?? 2,
25
+ ...this.mapInputImages(inputImageBase64s),
26
+ };
27
+ // 3. 提交任务
28
+ const submitResult = await this.client.submit(bflParams);
29
+ const taskId = submitResult.id;
30
+ // 4. 如果不等待,直接返回
31
+ if (params.wait === false) {
32
+ return {
33
+ id: taskId,
34
+ backend: 'bfl',
35
+ status: 'pending',
36
+ };
37
+ }
38
+ // 5. 轮询等待结果
39
+ const pollResult = await this.client.poll(taskId, {
40
+ interval: 1000,
41
+ timeout: 300000,
42
+ });
43
+ // 6. 下载图像到本地
44
+ const outputDir = cfg.outputDir || './comfy-output';
45
+ const outputFormat = params.outputFormat || cfg.bfl?.outputFormat || 'png';
46
+ const filename = `bfl_${taskId}.${outputFormat}`;
47
+ let localPath;
48
+ if (pollResult.result?.sample) {
49
+ localPath = await this.client.downloadImage(pollResult.result.sample, outputDir, filename);
50
+ }
51
+ const durationMs = Date.now() - startTime;
52
+ return {
53
+ id: taskId,
54
+ backend: 'bfl',
55
+ status: 'completed',
56
+ output: localPath,
57
+ outputUrl: pollResult.result?.sample,
58
+ seed: pollResult.result?.seed,
59
+ durationMs,
60
+ };
61
+ }
62
+ async getStatus(id) {
63
+ const result = await this.client.getResult(id);
64
+ let status = 'pending';
65
+ if (result.status === 'Ready') {
66
+ status = 'completed';
67
+ }
68
+ else if (result.status === 'Error' || result.status === 'Request Moderated' || result.status === 'Content Moderated') {
69
+ status = 'failed';
70
+ }
71
+ else if (result.status === 'Pending') {
72
+ status = result.progress && result.progress > 0 ? 'processing' : 'pending';
73
+ }
74
+ return {
75
+ id,
76
+ backend: 'bfl',
77
+ status,
78
+ outputUrl: result.result?.sample,
79
+ seed: result.result?.seed,
80
+ error: status === 'failed' ? result.status : undefined,
81
+ };
82
+ }
83
+ /**
84
+ * 处理输入图像,转换为 base64
85
+ */
86
+ async processInputImages(paths) {
87
+ if (!paths || paths.length === 0)
88
+ return [];
89
+ return Promise.all(paths.map(p => this.client.imageToBase64(p)));
90
+ }
91
+ /**
92
+ * 将输入图像映射到 BFL 参数
93
+ */
94
+ mapInputImages(base64Images) {
95
+ const mapping = {};
96
+ if (base64Images[0])
97
+ mapping.input_image = base64Images[0];
98
+ if (base64Images[1])
99
+ mapping.input_image_2 = base64Images[1];
100
+ if (base64Images[2])
101
+ mapping.input_image_3 = base64Images[2];
102
+ if (base64Images[3])
103
+ mapping.input_image_4 = base64Images[3];
104
+ if (base64Images[4])
105
+ mapping.input_image_5 = base64Images[4];
106
+ if (base64Images[5])
107
+ mapping.input_image_6 = base64Images[5];
108
+ if (base64Images[6])
109
+ mapping.input_image_7 = base64Images[6];
110
+ if (base64Images[7])
111
+ mapping.input_image_8 = base64Images[7];
112
+ return mapping;
113
+ }
114
+ }
115
+ //# sourceMappingURL=bfl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bfl.js","sourceRoot":"","sources":["../../src/backends/bfl.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { ImageBackend, ImageParams, ImageResult } from './types.js';
2
+ export declare class ComfyUIBackend implements ImageBackend {
3
+ readonly name: "comfyui";
4
+ private client;
5
+ constructor();
6
+ isAvailable(): boolean;
7
+ generate(params: ImageParams): Promise<ImageResult>;
8
+ getStatus(id: string): Promise<ImageResult>;
9
+ }
10
+ //# sourceMappingURL=comfyui.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comfyui.d.ts","sourceRoot":"","sources":["../../src/backends/comfyui.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAMzE,qBAAa,cAAe,YAAW,YAAY;IACjD,QAAQ,CAAC,IAAI,EAAG,SAAS,CAAU;IACnC,OAAO,CAAC,MAAM,CAAiB;;IAM/B,WAAW,IAAI,OAAO;IAIhB,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;IAiGnD,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;CAelD"}
@@ -0,0 +1,108 @@
1
+ import * as fs from 'fs/promises';
2
+ import * as path from 'path';
3
+ import { ComfyAPIClient } from '../services/api.js';
4
+ import { loadWorkflow, getBuiltinWorkflowPath, replaceVariables } from '../utils/workflow.js';
5
+ import { monitorProgress } from '../services/websocket.js';
6
+ import { getConfig } from '../services/config.js';
7
+ export class ComfyUIBackend {
8
+ name = 'comfyui';
9
+ client;
10
+ constructor() {
11
+ this.client = new ComfyAPIClient();
12
+ }
13
+ isAvailable() {
14
+ return true;
15
+ }
16
+ async generate(params) {
17
+ const startTime = Date.now();
18
+ const cfg = getConfig();
19
+ // 1. 选择工作流模板
20
+ const hasInputImage = params.inputImages && params.inputImages.length > 0;
21
+ const workflowName = hasInputImage ? 'flux_image_to_image.json' : 'flux_text_to_image.json';
22
+ const workflowPath = getBuiltinWorkflowPath(workflowName);
23
+ const workflow = loadWorkflow(workflowPath);
24
+ // 2. 处理输入图像(如果有)
25
+ let uploadedImageName;
26
+ if (hasInputImage && params.inputImages[0]) {
27
+ const imagePath = params.inputImages[0];
28
+ const imageBuffer = await fs.readFile(imagePath);
29
+ const filename = path.basename(imagePath);
30
+ const uploadResult = await this.client.uploadImage(imageBuffer, filename);
31
+ uploadedImageName = uploadResult.name;
32
+ }
33
+ // 3. 构建工作流参数
34
+ const workflowParams = {
35
+ prompt: params.prompt,
36
+ width: params.width || 1024,
37
+ height: params.height || 1024,
38
+ steps: 20,
39
+ cfg_scale: 1.0,
40
+ seed: params.seed ?? Math.floor(Math.random() * 1000000),
41
+ filename_prefix: 'comfy-cli',
42
+ model: 'FLUX1/flux1-dev-fp8.safetensors',
43
+ sampler: 'euler',
44
+ scheduler: 'simple',
45
+ negative_prompt: 'Blurry details, distorted shapes, unrealistic reflections, harsh lighting, excessive shadows, overly saturated colors, unnatural composition, poor rendering, low resolution, pixelation, artificial-looking textures, overly exaggerated contrast, cluttered background, unnecessary elements, random objects, plastic-like appearance.',
46
+ guidance: 3.5,
47
+ };
48
+ if (uploadedImageName) {
49
+ workflowParams.input_image = uploadedImageName;
50
+ workflowParams.denoise = 0.75; // 图生图默认 denoise
51
+ }
52
+ // 4. 替换变量并提交
53
+ const processedWorkflow = replaceVariables(workflow, workflowParams);
54
+ const result = await this.client.submitWorkflow(processedWorkflow);
55
+ const promptId = result.prompt_id;
56
+ // 5. 如果不等待,直接返回
57
+ if (params.wait === false) {
58
+ return {
59
+ id: promptId,
60
+ backend: 'comfyui',
61
+ status: 'pending',
62
+ seed: workflowParams.seed,
63
+ };
64
+ }
65
+ // 6. WebSocket 监控进度
66
+ await monitorProgress(promptId);
67
+ // 7. 获取输出文件并下载到本地
68
+ const outputs = await this.client.getOutputFiles(promptId);
69
+ const outputFile = outputs[0];
70
+ let localPath;
71
+ let outputUrl;
72
+ if (outputFile) {
73
+ // 构建 URL
74
+ const serverUrl = cfg.server;
75
+ outputUrl = `${serverUrl}/view?filename=${encodeURIComponent(outputFile.filename)}&subfolder=${encodeURIComponent(outputFile.subfolder)}&type=${encodeURIComponent(outputFile.type)}`;
76
+ // 下载到本地
77
+ const outputDir = cfg.outputDir || './comfy-output';
78
+ await fs.mkdir(outputDir, { recursive: true });
79
+ localPath = path.join(outputDir, outputFile.filename);
80
+ const imageBuffer = await this.client.downloadImage(outputFile.filename, outputFile.subfolder, outputFile.type);
81
+ await fs.writeFile(localPath, imageBuffer);
82
+ }
83
+ const durationMs = Date.now() - startTime;
84
+ return {
85
+ id: promptId,
86
+ backend: 'comfyui',
87
+ status: 'completed',
88
+ output: localPath,
89
+ outputUrl,
90
+ seed: workflowParams.seed,
91
+ durationMs,
92
+ };
93
+ }
94
+ async getStatus(id) {
95
+ const history = await this.client.getHistory(id);
96
+ let status = 'pending';
97
+ if (history && history[id]) {
98
+ const outputs = history[id].outputs;
99
+ status = outputs && Object.keys(outputs).length > 0 ? 'completed' : 'processing';
100
+ }
101
+ return {
102
+ id,
103
+ backend: 'comfyui',
104
+ status,
105
+ };
106
+ }
107
+ }
108
+ //# sourceMappingURL=comfyui.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comfyui.js","sourceRoot":"","sources":["../../src/backends/comfyui.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import type { ImageBackend } from './types.js';
2
+ export type BackendType = 'auto' | 'bfl' | 'comfyui';
3
+ export { ImageBackend, ImageParams, ImageResult } from './types.js';
4
+ export { BFLBackend } from './bfl.js';
5
+ export { ComfyUIBackend } from './comfyui.js';
6
+ /**
7
+ * 获取 BFL API Key(优先环境变量,其次配置文件)
8
+ */
9
+ export declare function getBFLApiKey(): string | undefined;
10
+ /**
11
+ * 获取图像生成后端
12
+ *
13
+ * - auto: 有 BFL API Key 则用 BFL,否则用 ComfyUI
14
+ * - bfl: 强制使用 BFL(需要 API Key)
15
+ * - comfyui: 强制使用 ComfyUI
16
+ *
17
+ * API Key 优先级: 环境变量 BFL_API_KEY > 配置文件 bfl.apiKey
18
+ */
19
+ export declare function getBackend(type?: BackendType): ImageBackend;
20
+ /**
21
+ * 获取当前会使用的后端名称
22
+ */
23
+ export declare function getCurrentBackendName(type?: BackendType): 'bfl' | 'comfyui';
24
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/backends/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,KAAK,GAAG,SAAS,CAAC;AAErD,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9C;;GAEG;AACH,wBAAgB,YAAY,IAAI,MAAM,GAAG,SAAS,CAEjD;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,IAAI,CAAC,EAAE,WAAW,GAAG,YAAY,CAyB3D;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,CAAC,EAAE,WAAW,GAAG,KAAK,GAAG,SAAS,CAe3E"}
@@ -0,0 +1,59 @@
1
+ import { getConfig } from '../services/config.js';
2
+ import { BFLBackend } from './bfl.js';
3
+ import { ComfyUIBackend } from './comfyui.js';
4
+ export { BFLBackend } from './bfl.js';
5
+ export { ComfyUIBackend } from './comfyui.js';
6
+ /**
7
+ * 获取 BFL API Key(优先环境变量,其次配置文件)
8
+ */
9
+ export function getBFLApiKey() {
10
+ return process.env.BFL_API_KEY || getConfig().bfl?.apiKey;
11
+ }
12
+ /**
13
+ * 获取图像生成后端
14
+ *
15
+ * - auto: 有 BFL API Key 则用 BFL,否则用 ComfyUI
16
+ * - bfl: 强制使用 BFL(需要 API Key)
17
+ * - comfyui: 强制使用 ComfyUI
18
+ *
19
+ * API Key 优先级: 环境变量 BFL_API_KEY > 配置文件 bfl.apiKey
20
+ */
21
+ export function getBackend(type) {
22
+ const cfg = getConfig();
23
+ const backendType = type || cfg.preferBackend || 'auto';
24
+ // 强制使用 BFL
25
+ if (backendType === 'bfl') {
26
+ const apiKey = getBFLApiKey();
27
+ if (!apiKey) {
28
+ throw new Error('BFL API key not configured. Set BFL_API_KEY env or run: comfy config set bfl.apiKey <key>');
29
+ }
30
+ return new BFLBackend(apiKey);
31
+ }
32
+ // 强制使用 ComfyUI
33
+ if (backendType === 'comfyui') {
34
+ return new ComfyUIBackend();
35
+ }
36
+ // 自动选择:优先 BFL
37
+ const apiKey = getBFLApiKey();
38
+ if (apiKey) {
39
+ return new BFLBackend(apiKey);
40
+ }
41
+ return new ComfyUIBackend();
42
+ }
43
+ /**
44
+ * 获取当前会使用的后端名称
45
+ */
46
+ export function getCurrentBackendName(type) {
47
+ const cfg = getConfig();
48
+ const backendType = type || cfg.preferBackend || 'auto';
49
+ if (backendType === 'bfl') {
50
+ return 'bfl';
51
+ }
52
+ if (backendType === 'comfyui') {
53
+ return 'comfyui';
54
+ }
55
+ // auto
56
+ const apiKey = getBFLApiKey();
57
+ return apiKey ? 'bfl' : 'comfyui';
58
+ }
59
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/backends/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAM9C,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE9C;;GAEG;AACH,MAAM,UAAU,YAAY;IAC1B,OAAO,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC;AAC5D,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,UAAU,CAAC,IAAkB;IAC3C,MAAM,GAAG,GAAG,SAAS,EAAE,CAAC;IACxB,MAAM,WAAW,GAAG,IAAI,IAAI,GAAG,CAAC,aAAa,IAAI,MAAM,CAAC;IAExD,WAAW;IACX,IAAI,WAAW,KAAK,KAAK,EAAE,CAAC;QAC1B,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;QAC9B,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CAAC,2FAA2F,CAAC,CAAC;QAC/G,CAAC;QACD,OAAO,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;IAChC,CAAC;IAED,eAAe;IACf,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;QAC9B,OAAO,IAAI,cAAc,EAAE,CAAC;IAC9B,CAAC;IAED,cAAc;IACd,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;IAC9B,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;IAChC,CAAC;IAED,OAAO,IAAI,cAAc,EAAE,CAAC;AAC9B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAkB;IACtD,MAAM,GAAG,GAAG,SAAS,EAAE,CAAC;IACxB,MAAM,WAAW,GAAG,IAAI,IAAI,GAAG,CAAC,aAAa,IAAI,MAAM,CAAC;IAExD,IAAI,WAAW,KAAK,KAAK,EAAE,CAAC;QAC1B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;QAC9B,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO;IACP,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;IAC9B,OAAO,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AACpC,CAAC"}
@@ -0,0 +1,97 @@
1
+ /**
2
+ * 图像生成/编辑参数
3
+ */
4
+ export interface ImageParams {
5
+ prompt: string;
6
+ inputImages?: string[];
7
+ width?: number;
8
+ height?: number;
9
+ seed?: number;
10
+ outputFormat?: 'jpeg' | 'png';
11
+ safetyTolerance?: number;
12
+ wait?: boolean;
13
+ }
14
+ /**
15
+ * 图像生成/编辑结果
16
+ */
17
+ export interface ImageResult {
18
+ id: string;
19
+ backend: 'bfl' | 'comfyui';
20
+ status: 'pending' | 'processing' | 'completed' | 'failed';
21
+ output?: string;
22
+ outputUrl?: string;
23
+ seed?: number;
24
+ cost?: number;
25
+ megapixels?: number;
26
+ durationMs?: number;
27
+ error?: string;
28
+ }
29
+ /**
30
+ * 后端接口
31
+ */
32
+ export interface ImageBackend {
33
+ readonly name: 'bfl' | 'comfyui';
34
+ /**
35
+ * 检查后端是否可用
36
+ */
37
+ isAvailable(): boolean;
38
+ /**
39
+ * 生成/编辑图像
40
+ */
41
+ generate(params: ImageParams): Promise<ImageResult>;
42
+ /**
43
+ * 获取任务状态
44
+ */
45
+ getStatus(id: string): Promise<ImageResult>;
46
+ }
47
+ /**
48
+ * 视频生成参数
49
+ */
50
+ export interface VideoParams {
51
+ inputImage: string;
52
+ prompt?: string;
53
+ negativePrompt?: string;
54
+ resolution?: '720P' | '1080P';
55
+ duration?: 5 | 10 | 15;
56
+ seed?: number;
57
+ wait?: boolean;
58
+ promptExtend?: boolean;
59
+ audio?: boolean;
60
+ audioUrl?: string;
61
+ shotType?: 'single' | 'multi';
62
+ watermark?: boolean;
63
+ }
64
+ /**
65
+ * 视频生成结果
66
+ */
67
+ export interface VideoResult {
68
+ id: string;
69
+ backend: 'dashscope' | 'comfyui';
70
+ status: 'pending' | 'processing' | 'completed' | 'failed';
71
+ output?: string;
72
+ outputUrl?: string;
73
+ duration?: number;
74
+ resolution?: string;
75
+ seed?: number;
76
+ durationMs?: number;
77
+ error?: string;
78
+ }
79
+ /**
80
+ * 视频后端接口
81
+ */
82
+ export interface VideoBackend {
83
+ readonly name: 'dashscope' | 'comfyui';
84
+ /**
85
+ * 检查后端是否可用
86
+ */
87
+ isAvailable(): boolean;
88
+ /**
89
+ * 生成视频
90
+ */
91
+ generate(params: VideoParams): Promise<VideoResult>;
92
+ /**
93
+ * 获取任务状态
94
+ */
95
+ getStatus(id: string): Promise<VideoResult>;
96
+ }
97
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/backends/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/backends/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,10 @@
1
+ import type { VideoBackend, VideoParams, VideoResult } from './types.js';
2
+ export declare class DashScopeVideoBackend implements VideoBackend {
3
+ readonly name: "dashscope";
4
+ private client;
5
+ constructor(apiKey: string);
6
+ isAvailable(): boolean;
7
+ generate(params: VideoParams): Promise<VideoResult>;
8
+ getStatus(id: string): Promise<VideoResult>;
9
+ }
10
+ //# sourceMappingURL=video-dashscope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"video-dashscope.d.ts","sourceRoot":"","sources":["../../src/backends/video-dashscope.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAIzE,qBAAa,qBAAsB,YAAW,YAAY;IACxD,QAAQ,CAAC,IAAI,EAAG,WAAW,CAAU;IACrC,OAAO,CAAC,MAAM,CAAkB;gBAEpB,MAAM,EAAE,MAAM;IAI1B,WAAW,IAAI,OAAO;IAIhB,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;IAqEnD,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;CAsBlD"}
@@ -0,0 +1,94 @@
1
+ import { DashScopeClient } from '../services/dashscope-api.js';
2
+ import { getConfig } from '../services/config.js';
3
+ export class DashScopeVideoBackend {
4
+ name = 'dashscope';
5
+ client;
6
+ constructor(apiKey) {
7
+ this.client = new DashScopeClient(apiKey);
8
+ }
9
+ isAvailable() {
10
+ return true;
11
+ }
12
+ async generate(params) {
13
+ const startTime = Date.now();
14
+ const cfg = getConfig();
15
+ // 1. 处理输入图像(转为 base64 或直接使用 URL)
16
+ let imgUrl = params.inputImage;
17
+ if (!imgUrl.startsWith('http') && !imgUrl.startsWith('data:')) {
18
+ imgUrl = await this.client.imageToBase64(params.inputImage);
19
+ }
20
+ // 2. 构建请求参数,合并配置默认值
21
+ const submitParams = {
22
+ prompt: params.prompt,
23
+ negativePrompt: params.negativePrompt,
24
+ imgUrl,
25
+ audioUrl: params.audioUrl,
26
+ resolution: params.resolution || cfg.dashscope?.resolution || '1080P',
27
+ duration: params.duration || cfg.dashscope?.duration || 5,
28
+ promptExtend: params.promptExtend ?? cfg.dashscope?.promptExtend ?? true,
29
+ audio: params.audio ?? cfg.dashscope?.audio ?? true,
30
+ shotType: params.shotType || cfg.dashscope?.shotType || 'single',
31
+ watermark: params.watermark ?? false,
32
+ seed: params.seed,
33
+ };
34
+ // 3. 提交任务
35
+ const submitResult = await this.client.submit(submitParams);
36
+ const taskId = submitResult.output.task_id;
37
+ // 4. 如果不等待,直接返回
38
+ if (params.wait === false) {
39
+ return {
40
+ id: taskId,
41
+ backend: 'dashscope',
42
+ status: 'pending',
43
+ resolution: submitParams.resolution,
44
+ duration: submitParams.duration,
45
+ };
46
+ }
47
+ // 5. 轮询等待结果
48
+ const pollResult = await this.client.poll(taskId, {
49
+ interval: 15000,
50
+ timeout: 600000,
51
+ });
52
+ // 6. 下载视频到本地
53
+ const outputDir = cfg.outputDir || './comfy-output';
54
+ const filename = `dashscope_${taskId}.mp4`;
55
+ let localPath;
56
+ if (pollResult.output.video_url) {
57
+ localPath = await this.client.downloadVideo(pollResult.output.video_url, outputDir, filename);
58
+ }
59
+ const durationMs = Date.now() - startTime;
60
+ return {
61
+ id: taskId,
62
+ backend: 'dashscope',
63
+ status: 'completed',
64
+ output: localPath,
65
+ outputUrl: pollResult.output.video_url,
66
+ duration: pollResult.usage?.duration,
67
+ resolution: submitParams.resolution,
68
+ durationMs,
69
+ };
70
+ }
71
+ async getStatus(id) {
72
+ const result = await this.client.getResult(id);
73
+ const status = result.output.task_status;
74
+ let videoStatus = 'pending';
75
+ if (status === 'SUCCEEDED') {
76
+ videoStatus = 'completed';
77
+ }
78
+ else if (status === 'FAILED' || status === 'CANCELED' || status === 'UNKNOWN') {
79
+ videoStatus = 'failed';
80
+ }
81
+ else if (status === 'RUNNING') {
82
+ videoStatus = 'processing';
83
+ }
84
+ return {
85
+ id,
86
+ backend: 'dashscope',
87
+ status: videoStatus,
88
+ outputUrl: result.output.video_url,
89
+ duration: result.usage?.duration,
90
+ error: videoStatus === 'failed' ? status : undefined,
91
+ };
92
+ }
93
+ }
94
+ //# sourceMappingURL=video-dashscope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"video-dashscope.js","sourceRoot":"","sources":["../../src/backends/video-dashscope.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import type { VideoBackend } from './types.js';
2
+ export type VideoBackendType = 'auto' | 'dashscope' | 'comfyui';
3
+ export { VideoBackend, VideoParams, VideoResult } from './types.js';
4
+ export { DashScopeVideoBackend } from './video-dashscope.js';
5
+ /**
6
+ * 获取 DashScope API Key(优先环境变量,其次配置文件)
7
+ */
8
+ export declare function getDashScopeApiKey(): string | undefined;
9
+ /**
10
+ * 获取视频生成后端
11
+ *
12
+ * - auto: 有 DashScope API Key 则用 DashScope,否则用 ComfyUI
13
+ * - dashscope: 强制使用 DashScope(需要 API Key)
14
+ * - comfyui: 强制使用 ComfyUI
15
+ *
16
+ * API Key 优先级: 环境变量 DASHSCOPE_API_KEY > 配置文件 dashscope.apiKey
17
+ */
18
+ export declare function getVideoBackend(type?: VideoBackendType): VideoBackend;
19
+ /**
20
+ * 获取当前会使用的视频后端名称
21
+ */
22
+ export declare function getCurrentVideoBackendName(type?: VideoBackendType): 'dashscope' | 'comfyui';
23
+ //# sourceMappingURL=video-index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"video-index.d.ts","sourceRoot":"","sources":["../../src/backends/video-index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,WAAW,GAAG,SAAS,CAAC;AAEhE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAE7D;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,GAAG,SAAS,CAEvD;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,IAAI,CAAC,EAAE,gBAAgB,GAAG,YAAY,CAyBrE;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,CAAC,EAAE,gBAAgB,GAAG,WAAW,GAAG,SAAS,CAe3F"}