zerocut-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 (63) hide show
  1. package/.eslintrc.js +11 -0
  2. package/.prettierignore +3 -0
  3. package/.prettierrc.json +6 -0
  4. package/README.md +205 -0
  5. package/dist/bin/zerocut.d.ts +2 -0
  6. package/dist/bin/zerocut.js +5 -0
  7. package/dist/cli.d.ts +3 -0
  8. package/dist/cli.js +24 -0
  9. package/dist/commands/config.d.ts +4 -0
  10. package/dist/commands/config.js +129 -0
  11. package/dist/commands/ffmpeg.d.ts +4 -0
  12. package/dist/commands/ffmpeg.js +32 -0
  13. package/dist/commands/foo.d.ts +4 -0
  14. package/dist/commands/foo.js +14 -0
  15. package/dist/commands/help.d.ts +4 -0
  16. package/dist/commands/help.js +14 -0
  17. package/dist/commands/image.d.ts +4 -0
  18. package/dist/commands/image.js +149 -0
  19. package/dist/commands/music.d.ts +4 -0
  20. package/dist/commands/music.js +74 -0
  21. package/dist/commands/pandoc.d.ts +4 -0
  22. package/dist/commands/pandoc.js +32 -0
  23. package/dist/commands/skill.d.ts +4 -0
  24. package/dist/commands/skill.js +24 -0
  25. package/dist/commands/tts.d.ts +4 -0
  26. package/dist/commands/tts.js +74 -0
  27. package/dist/commands/video.d.ts +4 -0
  28. package/dist/commands/video.js +166 -0
  29. package/dist/index.d.ts +1 -0
  30. package/dist/index.js +6 -0
  31. package/dist/services/cerevox.d.ts +34 -0
  32. package/dist/services/cerevox.js +256 -0
  33. package/dist/services/commandLoader.d.ts +3 -0
  34. package/dist/services/commandLoader.js +80 -0
  35. package/dist/services/config.d.ts +15 -0
  36. package/dist/services/config.js +235 -0
  37. package/dist/skill/SKILL.md +133 -0
  38. package/dist/types/command.d.ts +6 -0
  39. package/dist/types/command.js +2 -0
  40. package/dist/utils/progress.d.ts +1 -0
  41. package/dist/utils/progress.js +13 -0
  42. package/eslint.config.js +30 -0
  43. package/package.json +52 -0
  44. package/scripts/copy-skill-md.cjs +8 -0
  45. package/src/bin/zerocut.ts +3 -0
  46. package/src/cli.ts +25 -0
  47. package/src/commands/config.ts +130 -0
  48. package/src/commands/ffmpeg.ts +37 -0
  49. package/src/commands/help.ts +13 -0
  50. package/src/commands/image.ts +194 -0
  51. package/src/commands/music.ts +78 -0
  52. package/src/commands/pandoc.ts +37 -0
  53. package/src/commands/skill.ts +20 -0
  54. package/src/commands/tts.ts +80 -0
  55. package/src/commands/video.ts +202 -0
  56. package/src/index.ts +1 -0
  57. package/src/services/cerevox.ts +296 -0
  58. package/src/services/commandLoader.ts +42 -0
  59. package/src/services/config.ts +230 -0
  60. package/src/skill/SKILL.md +209 -0
  61. package/src/types/command.ts +7 -0
  62. package/src/utils/progress.ts +10 -0
  63. package/tsconfig.json +16 -0
package/.eslintrc.js ADDED
@@ -0,0 +1,11 @@
1
+ module.exports = {
2
+ root: true,
3
+ parser: "@typescript-eslint/parser",
4
+ plugins: ["@typescript-eslint", "prettier"],
5
+ extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended", "prettier"],
6
+ rules: {
7
+ "prettier/prettier": "error",
8
+ "@typescript-eslint/no-unused-vars": "warn",
9
+ "@typescript-eslint/no-explicit-any": "warn",
10
+ },
11
+ };
@@ -0,0 +1,3 @@
1
+ dist
2
+ node_modules
3
+
@@ -0,0 +1,6 @@
1
+ {
2
+ "semi": true,
3
+ "singleQuote": false,
4
+ "printWidth": 100,
5
+ "trailingComma": "es5"
6
+ }
package/README.md ADDED
@@ -0,0 +1,205 @@
1
+ ![status: WIP](https://img.shields.io/badge/status-WIP-orange)
2
+
3
+ # ZeroCut CLI
4
+
5
+ ZeroCut CLI is a modular, extensible command-line toolkit for media generation and sandbox tool execution. It is built with Node.js (TypeScript), uses CommonJS output, and follows a one-command-per-file architecture.
6
+
7
+ ## Features
8
+
9
+ - Media generation for image/video/music/tts
10
+ - Sandbox tool execution for ffmpeg and pandoc
11
+ - Modular commands (one file per command under `src/commands/*`)
12
+ - Dynamic external command loading via `ZEROCUT_COMMANDS_DIR` (.js/.cjs files)
13
+ - Configuration interceptor that validates required keys (`apiKey`)
14
+ - Session lifecycle management using Cerevox (open on preAction, close on postAction)
15
+ - Built-in `skill` command to print tool usage spec
16
+ - Strict TypeScript, ESLint + Prettier, pnpm-based project
17
+
18
+ ## Requirements
19
+
20
+ - Node.js >= 18
21
+ - pnpm (package manager)
22
+
23
+ ## Installation
24
+
25
+ ### From Registry (recommended)
26
+
27
+ ```bash
28
+ # pnpm global install (preferred)
29
+ pnpm add -g zerocut-cli
30
+
31
+ # Run directly without installing
32
+ pnpm dlx zerocut-cli help
33
+
34
+ # Run directly with npx
35
+ npx zerocut-cli help
36
+ ```
37
+
38
+ ### Local Development
39
+
40
+ ```bash
41
+ # install deps and build
42
+ pnpm install
43
+ pnpm run build
44
+
45
+ # link globally for local testing
46
+ pnpm link --global
47
+
48
+ # verify
49
+ zerocut help
50
+ ```
51
+
52
+ ## Quick Start
53
+
54
+ - Show help:
55
+
56
+ ```bash
57
+ npx zerocut-cli help
58
+ ```
59
+
60
+ - Configure required settings:
61
+
62
+ ```bash
63
+ npx zerocut-cli config key <key> # Or use OTT exchange below
64
+ # quick OTT exchange:
65
+ npx zerocut-cli config --ott <token> --region <cn|us>
66
+ ```
67
+
68
+ If configuration is missing, Zerocut prints:
69
+
70
+ ```
71
+ Missing required configuration: apiKey
72
+ Configure using:
73
+ zerocut config key <key>
74
+ or:
75
+ zerocut config --ott <token> --region <cn|us>
76
+ ```
77
+
78
+ ## Configuration
79
+
80
+ - Config file primary path: `~/.zerocut/config.json`
81
+ - Keys:
82
+ - `apiKey`: string
83
+ - `region`: environment region, one of `us` or `cn` (default: `us`)
84
+ - Key-path API (internal): `getConfigValueSync('a.b')`, `setConfigValueSync('a.b.c','value')`
85
+
86
+ ### Interactive configuration
87
+
88
+ Key setup supports direct input or OTT exchange:
89
+
90
+ ```bash
91
+ zerocut config key # prompts: choose region (cn/us), then enter OTT
92
+ zerocut config --ott <token> --region <cn|us> # non-interactive
93
+ ```
94
+
95
+ ## Commands
96
+
97
+ - `help` — show available commands
98
+ - `skill` — print built-in `SKILL.md` content
99
+ - `config` — configuration management
100
+ - `key [key]` — set API key (prompts if omitted; supports OTT exchange)
101
+ - `image` — create a new image (default action; requires `--prompt`)
102
+ - Options:
103
+ - `--prompt <prompt>` (required)
104
+ - `--model <model>` (seedream|seedream-pro|seedream-5l|banana|banana2|banana-pro|wan)
105
+ - `--aspectRatio <ratio>` (1:1|3:4|4:3|16:9|9:16|2:3|3:2|21:9|1:4|4:1|1:8|8:1)
106
+ - `--resolution <resolution>` (1K|2K|4K)
107
+ - `--refs <img1,img2,...>` (comma-separated paths/URLs)
108
+ - `--output <file>` (output file path)
109
+ - `video` — create a new video (default action; requires `--prompt`)
110
+ - Options:
111
+ - `--prompt <prompt>` (required)
112
+ - `--duration <seconds>` (integer 1–16; when `--sourceVideo` is set, must be 3–10)
113
+ - `--model <model>` (enum: `zerocut3.0|seedance-1.5-pro|vidu|vidu-pro|viduq3|viduq3-turbo|kling|kling-v3|wan|wan-flash|sora2|sora2-pro|veo3.1|veo3.1-pro`; default `vidu`)
114
+ - `--sourceVideo <video>` (base video path/url for edit mode)
115
+ - `--seed <seed>`
116
+ - `--firstFrame <image>`
117
+ - `--lastFrame <image>`
118
+ - `--refs <assets>`
119
+ - `--resolution <resolution>`
120
+ - `--aspectRatio <ratio>` (9:16|16:9|1:1)
121
+ - `--withAudio`
122
+ - `--optimizeCameraMotion`
123
+ - `--output <file>`
124
+ - Notes:
125
+ - long videos over 16 seconds should be split into multiple clips (each 1–16s)
126
+ - merge split clips using `ffmpeg` command
127
+ - `music` — create music (default action; requires `--prompt`)
128
+ - Options:
129
+ - `--prompt <prompt>` (required)
130
+ - `--output <file>`
131
+ - `tts` — text to speech (default action; requires `--text`)
132
+ - Options:
133
+ - `--prompt <prompt>`
134
+ - `--text <text>` (required)
135
+ - `--voiceId <voiceId>`
136
+ - `--output <file>`
137
+ - `ffmpeg` — run ffmpeg in sandbox
138
+ - Options:
139
+ - `--args <args...>` (required)
140
+ - `--resources <resources...>` (optional)
141
+ - Notes:
142
+ - only `ffmpeg`/`ffprobe` commands are allowed
143
+ - `ffmpeg` auto-injects `-y` when missing
144
+ - output file is auto-downloaded to current directory
145
+ - `pandoc` — run pandoc in sandbox
146
+ - Options:
147
+ - `--args <args...>` (required)
148
+ - `--resources <resources...>` (optional)
149
+ - Notes:
150
+ - only `pandoc` command is allowed
151
+ - output file must be specified with `-o` / `--output` / `--output=...`
152
+ - output file is auto-downloaded to current directory
153
+
154
+ ### Examples
155
+
156
+ ```bash
157
+ # Create an image (default action)
158
+ npx zerocut-cli image --prompt "a cat" --model seedream --aspectRatio 1:1 --resolution 1K --refs ref1.png,ref2.jpg --output out.png
159
+
160
+ # Create video (default action)
161
+ npx zerocut-cli video --prompt "city night drive" --duration 12 --model vidu --refs frame1.png,frame2.png --resolution 720p --output movie.mp4
162
+
163
+ # Edit from source video (duration must be 3-10 when sourceVideo is set)
164
+ npx zerocut-cli video --prompt "remix this clip" --model vidu --sourceVideo input.mp4 --duration 6 --output edited.mp4
165
+
166
+ # Split long video需求并拼接
167
+ printf "file 'part1.mp4'\nfile 'part2.mp4'\nfile 'part3.mp4'\n" > concat.txt
168
+ npx zerocut-cli ffmpeg --args -f concat -safe 0 -i concat.txt -c copy final.mp4 --resources concat.txt part1.mp4 part2.mp4 part3.mp4
169
+
170
+ # Create speech audio (default action)
171
+ npx zerocut-cli tts --text "你好,欢迎使用 ZeroCut" --voiceId voice_xxx --output speech.mp3
172
+
173
+ # Create music (default action)
174
+ npx zerocut-cli music --prompt "lofi beat" --output music.mp3
175
+
176
+ # Run ffmpeg in sandbox
177
+ npx zerocut-cli ffmpeg --args -i input.mp4 -vn output.mp3 --resources input.mp4
178
+
179
+ # Run pandoc in sandbox
180
+ npx zerocut-cli pandoc --args input.md -o output.pdf --resources input.md
181
+
182
+ # Print built-in skill spec
183
+ npx zerocut-cli skill
184
+ ```
185
+
186
+ ## Dynamic External Commands
187
+
188
+ Set `ZEROCUT_COMMANDS_DIR` to a directory containing `.js` or `.cjs` files that export a `register(program: Command)` function. Zerocut auto-loads them at runtime.
189
+
190
+ ```bash
191
+ export ZEROCUT_COMMANDS_DIR=/path/to/commands
192
+ zerocut help
193
+ ```
194
+
195
+ ## Development
196
+
197
+ - Build: `pnpm run build`
198
+ - Typecheck: `pnpm run typecheck`
199
+ - Lint: `pnpm run lint`
200
+ - Format: `pnpm run format`
201
+ - Dev (CLI entry): `pnpm run dev`
202
+
203
+ ## License
204
+
205
+ MIT
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const cli_1 = require("../cli");
5
+ (0, cli_1.run)(process.argv);
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ export declare function createProgram(): Command;
3
+ export declare function run(argv: string[]): Promise<void>;
package/dist/cli.js ADDED
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createProgram = createProgram;
4
+ exports.run = run;
5
+ const commander_1 = require("commander");
6
+ const commandLoader_1 = require("./services/commandLoader");
7
+ const config_1 = require("./services/config");
8
+ function createProgram() {
9
+ const program = new commander_1.Command();
10
+ program.name("zerocut").description("Zerocut CLI");
11
+ (0, commandLoader_1.loadBuiltInCommands)(program);
12
+ (0, config_1.applyConfigInterceptor)(program);
13
+ return program;
14
+ }
15
+ async function run(argv) {
16
+ const args = argv.slice(2);
17
+ const program = createProgram();
18
+ await (0, commandLoader_1.loadExternalCommandsAsync)(program);
19
+ if (args.length === 0) {
20
+ await program.parseAsync([argv[0] ?? "node", argv[1] ?? "zerocut", "help"]);
21
+ return;
22
+ }
23
+ await program.parseAsync(argv);
24
+ }
@@ -0,0 +1,4 @@
1
+ import type { Command } from "commander";
2
+ export declare const name = "config";
3
+ export declare const description = "Configuration management";
4
+ export declare function register(program: Command): void;
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.description = exports.name = void 0;
7
+ exports.register = register;
8
+ const node_readline_1 = __importDefault(require("node:readline"));
9
+ const config_1 = require("../services/config");
10
+ exports.name = "config";
11
+ exports.description = "Configuration management";
12
+ async function ask(question, defaults) {
13
+ const rl = node_readline_1.default.createInterface({ input: process.stdin, output: process.stdout });
14
+ const q = defaults ? `${question} [${defaults}]: ` : `${question}: `;
15
+ const answer = await new Promise((resolve) => rl.question(q, (ans) => resolve(ans)));
16
+ rl.close();
17
+ const trimmed = answer.trim();
18
+ return trimmed.length > 0 ? trimmed : (defaults ?? "");
19
+ }
20
+ function register(program) {
21
+ const parent = program
22
+ .command("config")
23
+ .description("Configuration management: set key, projectDir, and region")
24
+ .option("--ott <token>", "One-Time Token (OTT) for fetching API key")
25
+ .option("--region <region>", "Region for OTT exchange: cn|us")
26
+ .action(async function (opts) {
27
+ const ott = typeof opts.ott === "string" ? opts.ott.trim() : "";
28
+ const region = typeof opts.region === "string" ? opts.region.trim().toLowerCase() : "";
29
+ if (!ott)
30
+ return; // no quick params; fall through to subcommands normally
31
+ if (region !== "cn" && region !== "us") {
32
+ process.stderr.write("Invalid or missing --region. Allowed: cn|us\n");
33
+ process.exitCode = 1;
34
+ return;
35
+ }
36
+ try {
37
+ const base = region === "cn" ? "https://api2.zerocut.cn" : "https://api2.zerocut.art";
38
+ const resp = await fetch(`${base}/api/open/ott/exchange`, {
39
+ method: "POST",
40
+ headers: { "content-type": "application/json" },
41
+ body: JSON.stringify({ ott }),
42
+ });
43
+ if (!resp.ok) {
44
+ process.stderr.write(`OTT exchange failed: HTTP ${resp.status}\n`);
45
+ process.exitCode = 1;
46
+ return;
47
+ }
48
+ const json = (await resp.json());
49
+ const apiKey = json?.data?.apiKey;
50
+ if (typeof apiKey !== "string" || apiKey.length === 0) {
51
+ process.stderr.write("OTT exchange failed: missing data.apiKey in response\n");
52
+ process.exitCode = 1;
53
+ return;
54
+ }
55
+ (0, config_1.setConfigValueSync)("apiKey", apiKey);
56
+ (0, config_1.setConfigValueSync)("region", region);
57
+ process.stdout.write("apiKey set via OTT\n");
58
+ }
59
+ catch (err) {
60
+ process.stderr.write(`OTT exchange failed: ${err.message}\n`);
61
+ process.exitCode = 1;
62
+ }
63
+ });
64
+ parent
65
+ .command("key [key]")
66
+ .description("Set API key. If omitted, exchange via One-Time Token (OTT) after choosing region.")
67
+ .action(async (key) => {
68
+ const direct = typeof key === "string" ? key.trim() : "";
69
+ if (direct.length > 0) {
70
+ (0, config_1.setConfigValueSync)("apiKey", direct);
71
+ process.stdout.write("apiKey set\n");
72
+ return;
73
+ }
74
+ const region = (await ask("Choose region (cn/us)", "us")).trim().toLowerCase();
75
+ if (region !== "cn" && region !== "us") {
76
+ process.stderr.write("Invalid region. Allowed: cn|us\n");
77
+ process.exitCode = 1;
78
+ return;
79
+ }
80
+ const ott = (await ask("Enter One-Time Token (OTT)")).trim();
81
+ if (!ott) {
82
+ process.stderr.write("OTT is required when no apiKey is provided\n");
83
+ process.exitCode = 1;
84
+ return;
85
+ }
86
+ try {
87
+ const base = region === "cn" ? "https://api2.zerocut.cn" : "https://api2.zerocut.art";
88
+ const resp = await fetch(`${base}/api/open/ott/exchange`, {
89
+ method: "POST",
90
+ headers: {
91
+ "content-type": "application/json",
92
+ },
93
+ body: JSON.stringify({ ott }),
94
+ });
95
+ if (!resp.ok) {
96
+ process.stderr.write(`OTT exchange failed: HTTP ${resp.status}\n`);
97
+ process.exitCode = 1;
98
+ return;
99
+ }
100
+ const json = (await resp.json());
101
+ const apiKey = json?.data?.apiKey;
102
+ if (typeof apiKey !== "string" || apiKey.length === 0) {
103
+ process.stderr.write("OTT exchange failed: missing data.apiKey in response\n");
104
+ process.exitCode = 1;
105
+ return;
106
+ }
107
+ (0, config_1.setConfigValueSync)("apiKey", apiKey);
108
+ (0, config_1.setConfigValueSync)("region", region);
109
+ process.stdout.write("apiKey set via OTT\n");
110
+ }
111
+ catch (err) {
112
+ process.stderr.write(`OTT exchange failed: ${err.message}\n`);
113
+ process.exitCode = 1;
114
+ }
115
+ });
116
+ parent
117
+ .command("list")
118
+ .description("List current configuration values")
119
+ .action(() => {
120
+ const cfg = (0, config_1.readConfigSync)();
121
+ const masked = { ...cfg };
122
+ const k = masked.apiKey;
123
+ if (typeof k === "string" && k.length > 0) {
124
+ const visible = k.slice(-4);
125
+ masked.apiKey = `${"*".repeat(Math.max(0, k.length - 4))}${visible}`;
126
+ }
127
+ process.stdout.write(`${JSON.stringify(masked, null, 2)}\n`);
128
+ });
129
+ }
@@ -0,0 +1,4 @@
1
+ import type { Command } from "commander";
2
+ export declare const name = "ffmpeg";
3
+ export declare const description = "Run ffmpeg in sandbox";
4
+ export declare function register(program: Command): void;
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.description = exports.name = void 0;
4
+ exports.register = register;
5
+ const cerevox_1 = require("../services/cerevox");
6
+ exports.name = "ffmpeg";
7
+ exports.description = "Run ffmpeg in sandbox";
8
+ function register(program) {
9
+ program
10
+ .command("ffmpeg")
11
+ .description("Run ffmpeg command in sandbox")
12
+ .allowUnknownOption(true)
13
+ .option("--args <args...>", "Arguments passed to ffmpeg")
14
+ .option("--resources <resources...>", "Resource files/urls to sync into sandbox")
15
+ .action(async function (opts) {
16
+ const session = (0, cerevox_1.getSessionFromCommand)(this);
17
+ if (!session) {
18
+ process.stderr.write("No active session\n");
19
+ return;
20
+ }
21
+ const args = Array.isArray(opts.args) ? opts.args : [];
22
+ if (args.length === 0) {
23
+ process.stderr.write("Missing required option: --args\n");
24
+ process.exitCode = 1;
25
+ return;
26
+ }
27
+ const command = `ffmpeg ${args.join(" ")}`;
28
+ const resources = Array.isArray(opts.resources) ? opts.resources : [];
29
+ const res = await (0, cerevox_1.runFFMpegCommand)(session, command, resources);
30
+ console.log(res);
31
+ });
32
+ }
@@ -0,0 +1,4 @@
1
+ import type { Command } from "commander";
2
+ export declare const name = "foo";
3
+ export declare const description = "Print bar";
4
+ export declare function register(program: Command): void;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.description = exports.name = void 0;
4
+ exports.register = register;
5
+ exports.name = "foo";
6
+ exports.description = "Print bar";
7
+ function register(program) {
8
+ program
9
+ .command("foo")
10
+ .description("Print bar")
11
+ .action(() => {
12
+ process.stdout.write("bar\n");
13
+ });
14
+ }
@@ -0,0 +1,4 @@
1
+ import type { Command } from "commander";
2
+ export declare const name = "help";
3
+ export declare const description = "Show help";
4
+ export declare function register(program: Command): void;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.description = exports.name = void 0;
4
+ exports.register = register;
5
+ exports.name = "help";
6
+ exports.description = "Show help";
7
+ function register(program) {
8
+ program
9
+ .command("help")
10
+ .description("Show help")
11
+ .action(() => {
12
+ program.outputHelp();
13
+ });
14
+ }
@@ -0,0 +1,4 @@
1
+ import type { Command } from "commander";
2
+ export declare const name = "image";
3
+ export declare const description = "Image command: create image";
4
+ export declare function register(program: Command): void;
@@ -0,0 +1,149 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.description = exports.name = void 0;
7
+ exports.register = register;
8
+ const node_fs_1 = __importDefault(require("node:fs"));
9
+ const node_path_1 = __importDefault(require("node:path"));
10
+ const cerevox_1 = require("../services/cerevox");
11
+ const progress_1 = require("../utils/progress");
12
+ exports.name = "image";
13
+ exports.description = "Image command: create image";
14
+ function register(program) {
15
+ const parent = program.command("image").description("Create a new image; requires --prompt");
16
+ const allowedModels = [
17
+ "seedream",
18
+ "seedream-pro",
19
+ "seedream-5l",
20
+ "banana",
21
+ "banana2",
22
+ "banana-pro",
23
+ "wan",
24
+ ];
25
+ const allowedAspectRatios = [
26
+ "1:1",
27
+ "3:4",
28
+ "4:3",
29
+ "16:9",
30
+ "9:16",
31
+ "2:3",
32
+ "3:2",
33
+ "21:9",
34
+ "1:4",
35
+ "4:1",
36
+ "1:8",
37
+ "8:1",
38
+ ];
39
+ const allowedResolutions = ["1K", "2K", "4K"];
40
+ async function performImageGeneration(session, { prompt, model, aspectRatio, resolution, refsList, output, }) {
41
+ const referenceImages = await Promise.all(refsList.map(async (ref) => ({ url: await (0, cerevox_1.getMaterialUri)(session, ref) })));
42
+ const onProgress = (0, progress_1.createProgressSpinner)("inferencing");
43
+ const payload = {
44
+ model: model || "seedream-5l",
45
+ prompt,
46
+ aspect_ratio: aspectRatio,
47
+ resolution,
48
+ reference_images: referenceImages,
49
+ onProgress,
50
+ };
51
+ const res = await session.ai.generateImage(payload);
52
+ if (res?.urls && Array.isArray(res.urls) && res.urls.length > 0) {
53
+ try {
54
+ const tosUrl = await (0, cerevox_1.syncToTOS)(res.urls[0]);
55
+ if (tosUrl) {
56
+ res.url = tosUrl;
57
+ res.urls[0] = tosUrl;
58
+ }
59
+ }
60
+ catch { }
61
+ }
62
+ process.stdout.write("\n");
63
+ if (output) {
64
+ const dir = process.cwd();
65
+ const url = res.url ?? res.urls[0];
66
+ const response = await fetch(url);
67
+ const buffer = Buffer.from(await response.arrayBuffer());
68
+ const filePath = node_path_1.default.resolve(dir, output);
69
+ if (!node_fs_1.default.existsSync(node_path_1.default.dirname(filePath))) {
70
+ node_fs_1.default.mkdirSync(node_path_1.default.dirname(filePath), { recursive: true });
71
+ }
72
+ node_fs_1.default.writeFileSync(filePath, buffer);
73
+ res.output = filePath;
74
+ }
75
+ console.log(res);
76
+ }
77
+ async function imageCreateAction(opts) {
78
+ const session = (0, cerevox_1.getSessionFromCommand)(this);
79
+ if (!session) {
80
+ process.stderr.write("No active session\n");
81
+ return;
82
+ }
83
+ const prompt = typeof opts.prompt === "string" ? opts.prompt : undefined;
84
+ if (!prompt || prompt.trim().length === 0) {
85
+ process.stderr.write("Missing required option: --prompt\n");
86
+ process.exitCode = 1;
87
+ return;
88
+ }
89
+ const model = typeof opts.model === "string" ? opts.model.trim() : undefined;
90
+ if (model && !allowedModels.includes(model)) {
91
+ process.stderr.write(`Invalid value for --model: ${model}. Allowed: ${allowedModels.join("|")}\n`);
92
+ process.exitCode = 1;
93
+ return;
94
+ }
95
+ const modelArg = (model ?? undefined);
96
+ const aspectRatio = typeof opts.aspectRatio === "string"
97
+ ? opts.aspectRatio.trim()
98
+ : undefined;
99
+ if (aspectRatio && !allowedAspectRatios.includes(aspectRatio)) {
100
+ process.stderr.write(`Invalid value for --aspectRatio: ${aspectRatio}. Allowed: ${allowedAspectRatios.join("|")}\n`);
101
+ process.exitCode = 1;
102
+ return;
103
+ }
104
+ const resolution = typeof opts.resolution === "string"
105
+ ? opts.resolution.trim()
106
+ : undefined;
107
+ if (resolution && !allowedResolutions.includes(resolution)) {
108
+ process.stderr.write(`Invalid value for --resolution: ${resolution}. Allowed: ${allowedResolutions.join("|")}\n`);
109
+ process.exitCode = 1;
110
+ return;
111
+ }
112
+ const refsList = typeof opts.refs === "string" && opts.refs.length > 0
113
+ ? opts.refs
114
+ .split(",")
115
+ .map((s) => s.trim())
116
+ .filter((s) => s.length > 0)
117
+ : [];
118
+ const output = typeof opts.output === "string" ? opts.output : undefined;
119
+ await performImageGeneration(session, {
120
+ prompt,
121
+ model: modelArg,
122
+ aspectRatio,
123
+ resolution,
124
+ refsList,
125
+ output,
126
+ });
127
+ }
128
+ // default action on `zerocut image`
129
+ parent
130
+ .option("--prompt <prompt>", "Text prompt for image generation (required)")
131
+ .option("--model <model>", `Generator model: ${allowedModels.join("|")}`)
132
+ .option("--aspectRatio <ratio>", `Aspect ratio: ${allowedAspectRatios.join("|")}`)
133
+ .option("--resolution <resolution>", `Resolution: ${allowedResolutions.join("|")}`)
134
+ .option("--refs <refs>", "Comma-separated reference image paths/urls")
135
+ .option("--output <file>", "Output file path")
136
+ .action(imageCreateAction);
137
+ // keep `image create` for compatibility
138
+ parent
139
+ .command("create")
140
+ .description("Create a new image; requires --prompt")
141
+ .option("--prompt <prompt>", "Text prompt for image generation (required)")
142
+ .option("--model <model>", `Generator model: ${allowedModels.join("|")}`)
143
+ .option("--aspectRatio <ratio>", `Aspect ratio: ${allowedAspectRatios.join("|")}`)
144
+ .option("--resolution <resolution>", `Resolution: ${allowedResolutions.join("|")}`)
145
+ .option("--refs <refs>", "Comma-separated reference image paths/urls")
146
+ .option("--output <file>", "Output file path")
147
+ .action(imageCreateAction);
148
+ // removed `image edit`
149
+ }
@@ -0,0 +1,4 @@
1
+ import type { Command } from "commander";
2
+ export declare const name = "music";
3
+ export declare const description = "Music command: create music";
4
+ export declare function register(program: Command): void;