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.
- package/.eslintrc.js +11 -0
- package/.prettierignore +3 -0
- package/.prettierrc.json +6 -0
- package/README.md +205 -0
- package/dist/bin/zerocut.d.ts +2 -0
- package/dist/bin/zerocut.js +5 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +24 -0
- package/dist/commands/config.d.ts +4 -0
- package/dist/commands/config.js +129 -0
- package/dist/commands/ffmpeg.d.ts +4 -0
- package/dist/commands/ffmpeg.js +32 -0
- package/dist/commands/foo.d.ts +4 -0
- package/dist/commands/foo.js +14 -0
- package/dist/commands/help.d.ts +4 -0
- package/dist/commands/help.js +14 -0
- package/dist/commands/image.d.ts +4 -0
- package/dist/commands/image.js +149 -0
- package/dist/commands/music.d.ts +4 -0
- package/dist/commands/music.js +74 -0
- package/dist/commands/pandoc.d.ts +4 -0
- package/dist/commands/pandoc.js +32 -0
- package/dist/commands/skill.d.ts +4 -0
- package/dist/commands/skill.js +24 -0
- package/dist/commands/tts.d.ts +4 -0
- package/dist/commands/tts.js +74 -0
- package/dist/commands/video.d.ts +4 -0
- package/dist/commands/video.js +166 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +6 -0
- package/dist/services/cerevox.d.ts +34 -0
- package/dist/services/cerevox.js +256 -0
- package/dist/services/commandLoader.d.ts +3 -0
- package/dist/services/commandLoader.js +80 -0
- package/dist/services/config.d.ts +15 -0
- package/dist/services/config.js +235 -0
- package/dist/skill/SKILL.md +133 -0
- package/dist/types/command.d.ts +6 -0
- package/dist/types/command.js +2 -0
- package/dist/utils/progress.d.ts +1 -0
- package/dist/utils/progress.js +13 -0
- package/eslint.config.js +30 -0
- package/package.json +52 -0
- package/scripts/copy-skill-md.cjs +8 -0
- package/src/bin/zerocut.ts +3 -0
- package/src/cli.ts +25 -0
- package/src/commands/config.ts +130 -0
- package/src/commands/ffmpeg.ts +37 -0
- package/src/commands/help.ts +13 -0
- package/src/commands/image.ts +194 -0
- package/src/commands/music.ts +78 -0
- package/src/commands/pandoc.ts +37 -0
- package/src/commands/skill.ts +20 -0
- package/src/commands/tts.ts +80 -0
- package/src/commands/video.ts +202 -0
- package/src/index.ts +1 -0
- package/src/services/cerevox.ts +296 -0
- package/src/services/commandLoader.ts +42 -0
- package/src/services/config.ts +230 -0
- package/src/skill/SKILL.md +209 -0
- package/src/types/command.ts +7 -0
- package/src/utils/progress.ts +10 -0
- 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
|
+
};
|
package/.prettierignore
ADDED
package/.prettierrc.json
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+

|
|
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
|
package/dist/cli.d.ts
ADDED
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,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,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,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,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,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
|
+
}
|