reelforge 0.4.1 → 0.5.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.
package/README.md CHANGED
@@ -44,7 +44,7 @@ That's the whole story — no server to run.
44
44
 
45
45
  ### Self-hosting
46
46
 
47
- If you want to run your own ReelForge Studio (own LLM / RunningHub keys, your own pricing) clone the upstream repo, `pnpm dev`, then point the CLI at it:
47
+ If you want to run your own ReelForge Studio (own RelayX key, your own pricing) clone the upstream repo, `pnpm dev`, then point the CLI at it:
48
48
 
49
49
  ```bash
50
50
  rf --server http://localhost:8501 health
@@ -72,15 +72,12 @@ Run `rf <command> --help` for full details on any of these.
72
72
 
73
73
  | command | what it does |
74
74
  |---|---|
75
- | `llm chat -p <text>` | Send one prompt to the configured LLM |
76
- | `llm presets` | List built-in provider presets |
77
- | `tts edge -t <text> -o out.mp3` | Local Edge TTS synthesis |
78
- | `tts workflow -t <text> -w <workflow>` | ComfyUI TTS workflow (clone-style, IndexTTS etc.) |
75
+ | `llm chat -p <text>` | Send one prompt to the configured LLM (RelayX gateway by default) |
76
+ | `llm presets` | List built-in RelayX model presets |
77
+ | `tts edge -t <text> -o out.mp3` | Local Edge TTS synthesis (free) |
78
+ | `tts relayx -t <text> -o out.mp3` | RelayX TTS (vox/index-tts-2, 149 built-in voices) |
79
79
  | `tts voices [--locale zh]` | List supported Edge TTS voices |
80
- | `images generate -p <prompt> -w <workflow>` | Image generation via ComfyUI / RunningHub |
81
- | `images analyze -i <image>` | Reverse-describe an image |
82
- | `videos generate -p <prompt> -w <workflow>` | Atomic video generation |
83
- | `videos analyze -i <video>` | Reverse-describe a video |
80
+ | `images generate -p <prompt> -m rx-image-flux` | Image generation via RelayX (rx-image-z / rx-image-flux / rx-image-qwen) |
84
81
 
85
82
  ### Content generation
86
83
 
@@ -116,7 +113,6 @@ All `pipelines *` commands submit an **async task** and (by default) poll until
116
113
 
117
114
  | command | what it does |
118
115
  |---|---|
119
- | `workflows list [--source runninghub] [--kind image]` | Browse ComfyUI workflows |
120
116
  | `bgm list / upload <file> / delete <name>` | Manage background music |
121
117
  | `files list / upload <file> / download <path> / delete <path>` | Manage user assets |
122
118
 
@@ -150,12 +146,12 @@ rf tasks list --limit 5
150
146
  rf history get <task-id> --download recovered.mp4
151
147
 
152
148
  # 4. JSON pipe for automation
153
- rf workflows list --kind image --json | jq '.workflows[].key'
149
+ rf llm presets --json | jq '.[].defaultModel'
154
150
 
155
151
  # 5. Configure & test LLM (self-hosted)
156
- rf config set llm.api_key sk-xxxxx
157
- rf config set llm.base_url https://dashscope.aliyuncs.com/compatible-mode/v1
158
- rf config set llm.model qwen-plus
152
+ rf config set llm.api_key rx-xxxxx # RelayX key (or your own provider key)
153
+ rf config set llm.base_url https://relayx.timor419.com/v1
154
+ rf config set llm.model anthropic/claude-4-7-sonnet
159
155
  rf llm chat -p 'one-sentence summary of antifragile'
160
156
  ```
161
157
 
@@ -4,7 +4,7 @@ import { print } from "../utils/output.js";
4
4
  export function registerConfig(program) {
5
5
  const cfg = program
6
6
  .command("config")
7
- .description("Read or update the server config.yaml (LLM / ComfyUI / RunningHub keys)")
7
+ .description("Read or update the server config.yaml (LLM / RelayX keys)")
8
8
  .helpOption("-h, --help", "show help");
9
9
  cfg
10
10
  .command("get")
@@ -22,10 +22,10 @@ export function registerConfig(program) {
22
22
  "",
23
23
  "Examples:",
24
24
  " reelforge config set llm.api_key sk-xxxxxx",
25
- " reelforge config set llm.base_url https://dashscope.aliyuncs.com/compatible-mode/v1",
26
- " reelforge config set llm.model qwen-plus",
27
- " reelforge config set comfyui.runninghub_api_key rh-xxxxxx",
28
- " reelforge config set comfyui.runninghub_instance_type plus",
25
+ " reelforge config set llm.base_url https://relayx.timor419.com/v1",
26
+ " reelforge config set llm.model openai/gpt-5-mini",
27
+ " reelforge config set relayx.api_key rx-xxxxxx",
28
+ " reelforge config set relayx.default_image_model rx-image-flux",
29
29
  ].join("\n"))
30
30
  .action(async (key, value) => {
31
31
  const parts = key.split(".");
@@ -7,20 +7,8 @@ import { downloadTo } from "../utils/download.js";
7
7
  import { info, print, success, warn } from "../utils/output.js";
8
8
  const LAST_CREATE_PATH = path.join(os.homedir(), ".reelforge", "last-create.json");
9
9
  // ── Cost estimation (mirrors server src/lib/billing.ts) ──────────
10
- function calcWorkflowUnits(wfKey) {
11
- if (!wfKey)
12
- return 3;
13
- const base = (wfKey.split("/").pop() || wfKey).toLowerCase();
14
- if (base.startsWith("tts_"))
15
- return 1;
16
- if (base.startsWith("image_"))
17
- return 3;
18
- if (base.startsWith("video_"))
19
- return 15;
20
- if (base.startsWith("analyse_") || base.startsWith("analyze_"))
21
- return 2;
22
- return 3;
23
- }
10
+ const IMAGE_UNITS = 3; // matches ATOMIC_UNITS["images.generate"] in src/lib/billing.ts
11
+ const TTS_RELAYX_UNITS = 1; // matches ATOMIC_UNITS["tts.relayx"]
24
12
  function estimateUnits(body) {
25
13
  const mode = body.mode || "generate";
26
14
  const titleExplicit = !!body.title;
@@ -30,22 +18,12 @@ function estimateUnits(body) {
30
18
  const tplBase = (tplKey.split("/").pop() || "").toLowerCase();
31
19
  const tplType = tplBase.startsWith("static_")
32
20
  ? "static"
33
- : tplBase.startsWith("image_")
34
- ? "image"
35
- : tplBase.startsWith("video_")
36
- ? "video"
37
- : tplBase.startsWith("asset_")
38
- ? "asset"
39
- : "image";
40
- let mediaPerFrame = 0;
41
- if (tplType === "image") {
42
- mediaPerFrame = body.media_workflow ? calcWorkflowUnits(body.media_workflow) : 3;
43
- }
44
- else if (tplType === "video") {
45
- mediaPerFrame = body.media_workflow ? calcWorkflowUnits(body.media_workflow) : 15;
46
- }
47
- const ttsMode = body.tts_inference_mode || (body.tts_workflow ? "comfyui" : "local");
48
- const ttsPerFrame = ttsMode === "comfyui" ? 1 : 0;
21
+ : tplBase.startsWith("asset_")
22
+ ? "asset"
23
+ : "image";
24
+ const mediaPerFrame = tplType === "image" ? IMAGE_UNITS : 0;
25
+ const ttsMode = body.tts_inference_mode || "edge";
26
+ const ttsPerFrame = ttsMode === "relayx" ? TTS_RELAYX_UNITS : 0;
49
27
  const narrations = mode === "generate" ? 1 : 0;
50
28
  const title = titleExplicit ? 0 : 1;
51
29
  const imagePrompts = tplType === "static" ? 0 : 1;
@@ -156,14 +134,10 @@ function optsToBody(opts) {
156
134
  out.tts_voice = opts.ttsVoice;
157
135
  if (opts.voiceId !== undefined)
158
136
  out.voice_id = opts.voiceId;
159
- if (opts.ttsWorkflow !== undefined)
160
- out.tts_workflow = opts.ttsWorkflow;
161
137
  if (opts.ttsSpeed !== undefined)
162
138
  out.tts_speed = opts.ttsSpeed;
163
- if (opts.refAudio !== undefined)
164
- out.ref_audio = opts.refAudio;
165
- if (opts.mediaWorkflow !== undefined)
166
- out.media_workflow = opts.mediaWorkflow;
139
+ if (opts.imageModel !== undefined)
140
+ out.image_model = opts.imageModel;
167
141
  if (opts.frameTemplate !== undefined)
168
142
  out.frame_template = opts.frameTemplate;
169
143
  if (opts.promptPrefix !== undefined)
@@ -322,16 +296,14 @@ export function registerCreate(program) {
322
296
  .option("--max-image-prompt-words <N>", "image prompt max words", (v) => parseInt(v, 10))
323
297
  // --- Visual ---
324
298
  .option("--frame-template <key>", "HTML frame template, e.g. 1080x1920/image_default.html")
325
- .option("--media-workflow <key>", "AI image/video workflow, e.g. runninghub/image_flux.json")
299
+ .option("--image-model <id>", "RelayX image model (rx-image-z | rx-image-flux | rx-image-qwen)")
326
300
  .option("--prompt-prefix <text>", "raw style prefix prepended to every image prompt (overrides --style)")
327
301
  .option("--style <preset>", "image style preset — shortcut for --prompt-prefix; see 'Style presets' below for the full list")
328
302
  // --- Audio (TTS) ---
329
- .option("--tts-voice <id>", "Edge TTS voice id, e.g. zh-CN-YunjianNeural / en-US-AriaNeural")
303
+ .option("--tts-voice <id>", "TTS voice id; for edge use e.g. zh-CN-YunjianNeural / en-US-AriaNeural; for relayx use vox voice ids (default: 专业解说)")
330
304
  .option("--tts-speed <n>", "speech speed 0.5..2", parseFloat)
331
- .option("--tts-inference-mode <mode>", "local | comfyui")
332
- .option("--tts-workflow <key>", "ComfyUI TTS workflow (forces inference-mode=comfyui)")
305
+ .option("--tts-inference-mode <mode>", "edge (default, local Microsoft Edge TTS) | relayx (vox/index-tts-2 via RelayX)")
333
306
  .option("--voice-id <id>", "alias of --tts-voice (legacy compat)")
334
- .option("--ref-audio <path>", "reference audio for voice-cloning TTS workflows")
335
307
  // --- Audio (BGM) ---
336
308
  .option("--bgm <path>", "background music file path (server-side relative to bgm/)")
337
309
  .option("--bgm-volume <n>", "BGM volume 0..1", parseFloat)
@@ -363,8 +335,8 @@ export function registerCreate(program) {
363
335
  "",
364
336
  "Param groups:",
365
337
  " Content : --mode --title -n --split-mode --min/max-narration-words --min/max-image-prompt-words",
366
- " Visual : --frame-template --media-workflow --style --prompt-prefix",
367
- " TTS : --tts-voice --tts-speed --tts-inference-mode --tts-workflow --voice-id --ref-audio",
338
+ " Visual : --frame-template --image-model --style --prompt-prefix",
339
+ " TTS : --tts-voice --tts-speed --tts-inference-mode --voice-id",
368
340
  " BGM : --bgm --bgm-volume --bgm-mode",
369
341
  " Output : --video-fps --template-params -o --no-download --no-wait --poll-ms --timeout-ms",
370
342
  " Workflow: --recipe --redo --dry-run",
@@ -382,8 +354,6 @@ export function registerCreate(program) {
382
354
  "",
383
355
  "Explore available resources (separate commands):",
384
356
  " reelforge templates list # all HTML templates",
385
- " reelforge workflows list --kind image # all AI image workflows",
386
- " reelforge workflows list --kind video # all AI video workflows",
387
357
  " reelforge tts voices --locale zh # Edge TTS voice ids",
388
358
  " reelforge bgm list # built-in BGM files",
389
359
  "",
@@ -400,11 +370,6 @@ export function registerCreate(program) {
400
370
  " # Landscape (1920x1080)",
401
371
  ' rf create "..." --frame-template 1920x1080/image_default.html',
402
372
  "",
403
- " # AI-generated video background instead of still image",
404
- ' rf create "..." \\',
405
- " --frame-template 1080x1920/video_default.html \\",
406
- " --media-workflow runninghub/video_wan2.2.json",
407
- "",
408
373
  " # Add BGM",
409
374
  ' rf create "..." --bgm bgm/Echoes.mp3 --bgm-volume 0.3 --bgm-mode loop',
410
375
  "",
@@ -441,7 +406,7 @@ export function registerCreate(program) {
441
406
  ' "text": "为什么我们还没找到外星文明?",',
442
407
  ' "n_scenes": 7,',
443
408
  ' "frame_template": "1080x1920/image_default.html",',
444
- ' "media_workflow": "runninghub/image_flux.json",',
409
+ ' "image_model": "rx-image-flux",',
445
410
  ' "prompt_prefix": "Minimalist matchstick figure style",',
446
411
  ' "tts_voice": "zh-CN-YunjianNeural",',
447
412
  ' "tts_speed": 1.2,',
@@ -4,42 +4,32 @@ import { print, success } from "../utils/output.js";
4
4
  export function registerImages(program) {
5
5
  const images = program
6
6
  .command("images")
7
- .description("Image generation and analysis via ComfyUI / RunningHub workflows")
7
+ .description("Image generation via RelayX (rx-image-z / rx-image-flux / rx-image-qwen)")
8
8
  .helpOption("-h, --help", "show help");
9
9
  images
10
10
  .command("generate")
11
- .description("Generate an image via a ComfyUI image_* workflow")
11
+ .description("Generate an image via RelayX")
12
12
  .helpOption("-h, --help", "show help")
13
13
  .requiredOption("-p, --prompt <text>", "text prompt")
14
- .requiredOption("-w, --workflow <key>", "workflow key, e.g. selfhost/image_flux.json")
14
+ .option("-m, --model <id>", "RelayX image model id (rx-image-z | rx-image-flux | rx-image-qwen)")
15
15
  .option("--width <n>", "image width", parseInt)
16
16
  .option("--height <n>", "image height", parseInt)
17
- .option("--steps <n>", "sampling steps", parseInt)
18
- .option("--seed <n>", "random seed", parseInt)
19
- .option("--cfg <n>", "CFG scale", parseFloat)
20
- .option("--negative <text>", "negative prompt")
21
17
  .option("-o, --output <file>", "download first image to this local path")
22
18
  .option("--all-output <dir>", "download ALL generated images into this directory")
23
19
  .addHelpText("after", [
24
20
  "",
25
21
  "Examples:",
26
- " reelforge images generate -p 'a cat' -w selfhost/image_flux.json --width 1024 --height 1024 -o cat.png",
27
- " reelforge images generate -p 'cyberpunk city' -w runninghub/image_qwen.json --seed 42 -o city.png",
22
+ " reelforge images generate -p 'a cat' -m rx-image-flux --width 1024 --height 1024 -o cat.png",
23
+ " reelforge images generate -p 'cyberpunk city, neon, 9:16' --width 1080 --height 1920 -o city.png",
28
24
  ].join("\n"))
29
25
  .action(async (opts) => {
30
- const body = { prompt: opts.prompt, workflow: opts.workflow };
26
+ const body = { prompt: opts.prompt };
27
+ if (opts.model)
28
+ body.model = opts.model;
31
29
  if (opts.width !== undefined)
32
30
  body.width = opts.width;
33
31
  if (opts.height !== undefined)
34
32
  body.height = opts.height;
35
- if (opts.steps !== undefined)
36
- body.steps = opts.steps;
37
- if (opts.seed !== undefined)
38
- body.seed = opts.seed;
39
- if (opts.cfg !== undefined)
40
- body.cfg = opts.cfg;
41
- if (opts.negative)
42
- body.negative_prompt = opts.negative;
43
33
  const r = await post("/api/v1/images/generate", body);
44
34
  if (opts.output && r.images?.[0]) {
45
35
  await downloadTo(r.images[0], opts.output);
@@ -54,14 +44,4 @@ export function registerImages(program) {
54
44
  }
55
45
  print(r);
56
46
  });
57
- images
58
- .command("analyze")
59
- .description("Reverse-describe an image using an analyse_image workflow")
60
- .helpOption("-h, --help", "show help")
61
- .requiredOption("-i, --image <pathOrUrl>", "image to analyze (local path or URL)")
62
- .option("-w, --workflow <key>", "workflow key", "selfhost/analyse_image.json")
63
- .action(async (opts) => {
64
- const r = await post("/api/v1/images/analyze", { image: opts.image, workflow: opts.workflow });
65
- print(r);
66
- });
67
47
  }
@@ -4,7 +4,7 @@ import { print, table } from "../utils/output.js";
4
4
  export function registerLlm(program) {
5
5
  const llm = program
6
6
  .command("llm")
7
- .description("Large-language-model utilities (chat, list providers)")
7
+ .description("Large-language-model utilities (chat, list RelayX model presets)")
8
8
  .helpOption("-h, --help", "show help");
9
9
  llm
10
10
  .command("chat")
@@ -21,7 +21,7 @@ export function registerLlm(program) {
21
21
  "",
22
22
  "Examples:",
23
23
  " reelforge llm chat -p 'Hello'",
24
- " reelforge llm chat -p @prompt.txt -m qwen-plus -t 0.4",
24
+ " reelforge llm chat -p @prompt.txt -m anthropic/claude-4-7-sonnet -t 0.4",
25
25
  " reelforge llm chat -p 'movie review of Inception' --schema review.json --json",
26
26
  ].join("\n"))
27
27
  .action(async (opts) => {
@@ -47,7 +47,7 @@ export function registerLlm(program) {
47
47
  });
48
48
  llm
49
49
  .command("presets")
50
- .description("List built-in LLM provider presets (Qwen / OpenAI / Claude / DeepSeek / Ollama / Moonshot)")
50
+ .description("List built-in RelayX model presets (GPT-5 / Claude 4.7 / Gemini 3 / DeepSeek / Qwen / Kimi)")
51
51
  .helpOption("-h, --help", "show help")
52
52
  .action(async () => {
53
53
  const r = await get("/api/v1/llm/presets");
@@ -47,7 +47,7 @@ export function registerPipelines(program) {
47
47
  .option("-n, --n-scenes <n>", "number of scenes (mode=generate)", parseInt, 5)
48
48
  .option("--split-mode <mode>", "paragraph | line | sentence (mode=fixed)", "paragraph")
49
49
  .option("--frame-template <key>", "template, e.g. 1080x1920/static_default.html", "1080x1920/static_default.html")
50
- .option("--media-workflow <key>", "ComfyUI workflow for AI image/video (when template requires it)")
50
+ .option("--image-model <id>", "RelayX image model (rx-image-z | rx-image-flux | rx-image-qwen) — only when template requires AI images")
51
51
  .option("--prompt-prefix <text>", "style prefix prepended to image prompts")
52
52
  .option("--tts-voice <id>", "Edge TTS voice", "zh-CN-YunjianNeural")
53
53
  .option("--tts-speed <n>", "speech speed (0.5..2)", parseFloat, 1.2)
@@ -58,7 +58,7 @@ export function registerPipelines(program) {
58
58
  "Examples:",
59
59
  " reelforge pipelines standard -t 'why we explore space' -n 5 -o space.mp4",
60
60
  " reelforge pipelines standard -t @script.txt --mode fixed --split-mode paragraph --title 'My Show' -o out.mp4",
61
- " reelforge pipelines standard -t '宠物' --frame-template 1080x1920/image_default.html --media-workflow runninghub/image_flux.json --prompt-prefix 'cinematic'",
61
+ " reelforge pipelines standard -t '宠物' --frame-template 1080x1920/image_default.html --image-model rx-image-flux --prompt-prefix 'cinematic'",
62
62
  ].join("\n"))).action(async (opts) => {
63
63
  let text = opts.text;
64
64
  if (text.startsWith("@"))
@@ -70,7 +70,7 @@ export function registerPipelines(program) {
70
70
  n_scenes: opts.nScenes,
71
71
  split_mode: opts.splitMode,
72
72
  frame_template: opts.frameTemplate,
73
- media_workflow: opts.mediaWorkflow,
73
+ image_model: opts.imageModel,
74
74
  prompt_prefix: opts.promptPrefix,
75
75
  tts_voice: opts.ttsVoice,
76
76
  tts_speed: opts.ttsSpeed,
@@ -4,7 +4,7 @@ import { print, table, success } from "../utils/output.js";
4
4
  export function registerTts(program) {
5
5
  const tts = program
6
6
  .command("tts")
7
- .description("Text-to-speech: local Edge TTS or ComfyUI workflow")
7
+ .description("Text-to-speech: local Edge TTS (free) or RelayX vox/index-tts-2")
8
8
  .helpOption("-h, --help", "show help");
9
9
  tts
10
10
  .command("edge")
@@ -37,32 +37,31 @@ export function registerTts(program) {
37
37
  print({ ok: r.ok, voice: r.voice, rate: r.rate, size_bytes: r.size_bytes, file_path: r.file_path, url: r.url, downloaded_to: opts.output || null });
38
38
  });
39
39
  tts
40
- .command("workflow")
41
- .description("Synthesize speech via a ComfyUI TTS workflow (e.g. IndexTTS, SparkTTS)")
40
+ .command("relayx")
41
+ .description("Synthesize speech via RelayX (vox/index-tts-2 by default; 149 built-in voices)")
42
42
  .helpOption("-h, --help", "show help")
43
43
  .requiredOption("-t, --text <text>", "text to synthesize")
44
- .requiredOption("-w, --workflow <key>", "workflow key, e.g. runninghub/tts_index2.json")
45
- .option("--voice <id>", "voice ID (workflow-specific)")
46
- .option("--speed <n>", "speech speed multiplier", parseFloat)
47
- .option("--ref-audio <path>", "reference audio (URL or local path) for clone-style workflows")
44
+ .option("-m, --model <id>", "RelayX TTS model id (default: vox/index-tts-2)")
45
+ .option("--voice <id>", "voice id within the model (vox default: 专业解说)")
46
+ .option("--speed <n>", "speech speed multiplier (0.5..2)", parseFloat)
48
47
  .option("-o, --output <file>", "download audio to this local path")
49
48
  .addHelpText("after", [
50
49
  "",
51
50
  "Example:",
52
- " reelforge tts workflow -t 'hello' -w runninghub/tts_index2.json --ref-audio data/uploads/me.wav -o me.mp3",
51
+ " reelforge tts relayx -t '你好世界' --voice '专业解说' -o hello.mp3",
53
52
  ].join("\n"))
54
53
  .action(async (opts) => {
55
- const body = { text: opts.text, workflow: opts.workflow };
54
+ const body = { text: opts.text };
55
+ if (opts.model)
56
+ body.model = opts.model;
56
57
  if (opts.voice)
57
58
  body.voice = opts.voice;
58
59
  if (opts.speed !== undefined)
59
60
  body.speed = opts.speed;
60
- if (opts.refAudio)
61
- body.ref_audio = opts.refAudio;
62
- const r = await post("/api/v1/tts/workflow", body);
63
- if (opts.output && r.audio_url) {
64
- await downloadTo(r.audio_url, opts.output);
65
- success(`Saved → ${opts.output}`);
61
+ const r = await post("/api/v1/tts/relayx", body);
62
+ if (opts.output) {
63
+ await downloadTo(r.url, opts.output);
64
+ success(`Saved → ${opts.output} (${r.size_bytes} bytes, model=${r.model}, voice=${r.voice})`);
66
65
  }
67
66
  print(r);
68
67
  });
package/dist/index.js CHANGED
@@ -17,13 +17,11 @@ import { registerCreate } from "./commands/create.js";
17
17
  import { registerLlm } from "./commands/llm.js";
18
18
  import { registerTts } from "./commands/tts.js";
19
19
  import { registerImages } from "./commands/images.js";
20
- import { registerVideos } from "./commands/videos.js";
21
20
  import { registerContent } from "./commands/content.js";
22
21
  import { registerTemplates } from "./commands/templates.js";
23
22
  import { registerFrames } from "./commands/frames.js";
24
23
  import { registerCompositions } from "./commands/compositions.js";
25
24
  import { registerPipelines } from "./commands/pipelines.js";
26
- import { registerWorkflows } from "./commands/workflows.js";
27
25
  import { registerBgm } from "./commands/bgm.js";
28
26
  import { registerFiles } from "./commands/files.js";
29
27
  import { registerTasks } from "./commands/tasks.js";
@@ -70,7 +68,7 @@ program.addHelpText("afterAll", [
70
68
  " rf create '...' -o ./videos/space.mp4 # pick the exact path",
71
69
  " rf llm chat --prompt 'explain antifragile in 3 sentences'",
72
70
  " rf tts edge --text 'hello world' --voice en-US-AriaNeural -o out.mp3",
73
- " rf images generate --prompt 'a cat' --workflow selfhost/image_flux.json -o cat.png",
71
+ " rf images generate --prompt 'a cat' --model rx-image-flux -o cat.png",
74
72
  " rf pipelines standard --text 'why we explore space' --tts-voice zh-CN-YunjianNeural",
75
73
  " rf tasks list --status running",
76
74
  " rf config get",
@@ -80,13 +78,11 @@ registerCreate(program);
80
78
  registerLlm(program);
81
79
  registerTts(program);
82
80
  registerImages(program);
83
- registerVideos(program);
84
81
  registerContent(program);
85
82
  registerTemplates(program);
86
83
  registerFrames(program);
87
84
  registerCompositions(program);
88
85
  registerPipelines(program);
89
- registerWorkflows(program);
90
86
  registerBgm(program);
91
87
  registerFiles(program);
92
88
  registerTasks(program);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reelforge",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "description": "CLI for ReelForge Studio — AI video engine. Installs as both `reelforge` and the short alias `rf`. Every REST API exposed as a command, with --help on every level.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -35,8 +35,7 @@
35
35
  "keywords": [
36
36
  "reelforge",
37
37
  "ai-video",
38
- "comfyui",
39
- "runninghub",
38
+ "relayx",
40
39
  "tts",
41
40
  "edge-tts",
42
41
  "ffmpeg",
@@ -1,52 +0,0 @@
1
- import { post } from "../client.js";
2
- import { downloadTo } from "../utils/download.js";
3
- import { print, success } from "../utils/output.js";
4
- export function registerVideos(program) {
5
- const videos = program
6
- .command("videos")
7
- .description("Atomic video generation and analysis via ComfyUI / RunningHub workflows")
8
- .helpOption("-h, --help", "show help");
9
- videos
10
- .command("generate")
11
- .description("Generate a video via a video_* workflow")
12
- .helpOption("-h, --help", "show help")
13
- .requiredOption("-p, --prompt <text>", "text prompt")
14
- .requiredOption("-w, --workflow <key>", "workflow key, e.g. runninghub/video_wan2.2.json")
15
- .option("--width <n>", "video width", parseInt)
16
- .option("--height <n>", "video height", parseInt)
17
- .option("--duration <s>", "target duration in seconds", parseFloat)
18
- .option("--image <pathOrUrl>", "reference image (when the workflow accepts one)")
19
- .option("-o, --output <file>", "download first video to this local path")
20
- .addHelpText("after", [
21
- "",
22
- "Example:",
23
- " reelforge videos generate -p 'astronaut riding a horse' -w runninghub/video_wan2.2.json --duration 4 -o out.mp4",
24
- ].join("\n"))
25
- .action(async (opts) => {
26
- const body = { prompt: opts.prompt, workflow: opts.workflow };
27
- if (opts.width !== undefined)
28
- body.width = opts.width;
29
- if (opts.height !== undefined)
30
- body.height = opts.height;
31
- if (opts.duration !== undefined)
32
- body.duration = opts.duration;
33
- if (opts.image)
34
- body.image = opts.image;
35
- const r = await post("/api/v1/videos/generate", body);
36
- if (opts.output && r.videos?.[0]) {
37
- await downloadTo(r.videos[0], opts.output);
38
- success(`Saved → ${opts.output}`);
39
- }
40
- print(r);
41
- });
42
- videos
43
- .command("analyze")
44
- .description("Reverse-describe a video using an analyse_video / video_understanding workflow")
45
- .helpOption("-h, --help", "show help")
46
- .requiredOption("-i, --video <pathOrUrl>", "video to analyze (local path or URL)")
47
- .option("-w, --workflow <key>", "workflow key", "selfhost/analyse_video.json")
48
- .action(async (opts) => {
49
- const r = await post("/api/v1/videos/analyze", { video: opts.video, workflow: opts.workflow });
50
- print(r);
51
- });
52
- }
@@ -1,29 +0,0 @@
1
- import { get } from "../client.js";
2
- import { table } from "../utils/output.js";
3
- export function registerWorkflows(program) {
4
- const wf = program
5
- .command("workflows")
6
- .alias("workflow")
7
- .description("Browse ComfyUI / RunningHub workflows under workflows/")
8
- .helpOption("-h, --help", "show help");
9
- wf
10
- .command("list")
11
- .description("List all available workflows")
12
- .helpOption("-h, --help", "show help")
13
- .option("--source <src>", "filter by source: selfhost | runninghub")
14
- .option("--kind <kind>", "filter by kind: image | video | tts | analyse")
15
- .action(async (opts) => {
16
- const qs = new URLSearchParams();
17
- if (opts.source)
18
- qs.set("source", opts.source);
19
- if (opts.kind)
20
- qs.set("kind", opts.kind);
21
- const r = await get(`/api/v1/workflows${qs.toString() ? `?${qs}` : ""}`);
22
- table(r.workflows.map((w) => ({
23
- key: w.key,
24
- source: w.source,
25
- kind: w.kind,
26
- workflow_id: w.workflow_id || "-",
27
- })));
28
- });
29
- }