zerocut-cli 0.3.3 → 0.4.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 +2 -1
- package/dist/commands/config.js +50 -48
- package/dist/commands/image.js +13 -1
- package/dist/commands/skill.js +10 -6
- package/dist/services/cerevox.d.ts +1 -0
- package/dist/services/cerevox.js +5 -2
- package/dist/services/commandLoader.js +3 -1
- package/dist/skill/SKILL.md +4 -1
- package/dist/skill/one-click-video/SKILL.md +339 -66
- package/dist/skill/one-click-video/SKILL_CN.md +851 -0
- package/package.json +1 -1
- package/src/commands/image.ts +18 -0
- package/src/services/cerevox.ts +5 -4
- package/src/skill/SKILL.md +4 -1
- package/src/skill/one-click-video/SKILL.md +339 -66
- package/src/skill/one-click-video/SKILL_CN.md +851 -0
package/README.md
CHANGED
|
@@ -102,6 +102,7 @@ zerocut config --ott <token> --region <cn|us> # non-interactive
|
|
|
102
102
|
- Options:
|
|
103
103
|
- `--prompt <prompt>` (required)
|
|
104
104
|
- `--model <model>` (seedream|seedream-pro|seedream-5l|banana|banana2|banana-pro|wan|wan-pro)
|
|
105
|
+
- `--type <type>` (default|storyboard|subject-turnaround, default: default)
|
|
105
106
|
- `--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
107
|
- `--resolution <resolution>` (1K|2K|4K)
|
|
107
108
|
- `--refs <img1,img2,...>` (comma-separated paths/URLs)
|
|
@@ -158,7 +159,7 @@ zerocut config --ott <token> --region <cn|us> # non-interactive
|
|
|
158
159
|
|
|
159
160
|
```bash
|
|
160
161
|
# Create an image (default action)
|
|
161
|
-
npx zerocut-cli image --prompt "a cat" --model seedream --aspectRatio 1:1 --resolution 1K --refs ref1.png,ref2.jpg --output out.png
|
|
162
|
+
npx zerocut-cli image --prompt "a cat" --model seedream --type default --aspectRatio 1:1 --resolution 1K --refs ref1.png,ref2.jpg --output out.png
|
|
162
163
|
|
|
163
164
|
# Create video (default action)
|
|
164
165
|
npx zerocut-cli video --prompt "city night drive" --duration 12 --model vidu --refs frame1.png,frame2.png --resolution 720p --output movie.mp4
|
package/dist/commands/config.js
CHANGED
|
@@ -17,6 +17,29 @@ async function ask(question, defaults) {
|
|
|
17
17
|
const trimmed = answer.trim();
|
|
18
18
|
return trimmed.length > 0 ? trimmed : (defaults ?? "");
|
|
19
19
|
}
|
|
20
|
+
async function exchangeOttAndSave(ott, region) {
|
|
21
|
+
const base = region === "cn" ? "https://api2.zerocut.cn" : "https://api2.zerocut.art";
|
|
22
|
+
const resp = await fetch(`${base}/api/open/ott/exchange`, {
|
|
23
|
+
method: "POST",
|
|
24
|
+
headers: { "content-type": "application/json" },
|
|
25
|
+
body: JSON.stringify({ ott }),
|
|
26
|
+
});
|
|
27
|
+
if (!resp.ok) {
|
|
28
|
+
process.stderr.write(`OTT exchange failed: HTTP ${resp.status}\n`);
|
|
29
|
+
process.exitCode = 1;
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const json = (await resp.json());
|
|
33
|
+
const apiKey = json?.data?.apiKey;
|
|
34
|
+
if (typeof apiKey !== "string" || apiKey.length === 0) {
|
|
35
|
+
process.stderr.write("OTT exchange failed: missing data.apiKey in response\n");
|
|
36
|
+
process.exitCode = 1;
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
(0, config_1.setConfigValueSync)("apiKey", apiKey);
|
|
40
|
+
(0, config_1.setConfigValueSync)("region", region);
|
|
41
|
+
process.stdout.write("apiKey set via OTT\n");
|
|
42
|
+
}
|
|
20
43
|
function register(program) {
|
|
21
44
|
const parent = program
|
|
22
45
|
.command("config")
|
|
@@ -24,37 +47,38 @@ function register(program) {
|
|
|
24
47
|
.option("--ott <token>", "One-Time Token (OTT) for fetching API key")
|
|
25
48
|
.option("--region <region>", "Region for OTT exchange: cn|us")
|
|
26
49
|
.action(async function (opts) {
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
50
|
+
const cfg = (0, config_1.readConfigSync)();
|
|
51
|
+
const regionInput = typeof opts.region === "string" ? opts.region.trim().toLowerCase() : "";
|
|
52
|
+
let region;
|
|
53
|
+
if (regionInput === "cn" || regionInput === "us") {
|
|
54
|
+
region = regionInput;
|
|
55
|
+
}
|
|
56
|
+
else if (regionInput.length > 0) {
|
|
57
|
+
process.stderr.write("Invalid --region. Allowed: cn|us\n");
|
|
33
58
|
process.exitCode = 1;
|
|
34
59
|
return;
|
|
35
60
|
}
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if (!resp.ok) {
|
|
44
|
-
process.stderr.write(`OTT exchange failed: HTTP ${resp.status}\n`);
|
|
61
|
+
else {
|
|
62
|
+
const defaultRegion = cfg.region === "cn" || cfg.region === "us" ? cfg.region : "us";
|
|
63
|
+
const regionAnswer = (await ask("Choose region (cn/us)", defaultRegion))
|
|
64
|
+
.trim()
|
|
65
|
+
.toLowerCase();
|
|
66
|
+
if (regionAnswer !== "cn" && regionAnswer !== "us") {
|
|
67
|
+
process.stderr.write("Invalid region. Allowed: cn|us\n");
|
|
45
68
|
process.exitCode = 1;
|
|
46
69
|
return;
|
|
47
70
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
71
|
+
region = regionAnswer;
|
|
72
|
+
}
|
|
73
|
+
const ott = typeof opts.ott === "string" ? opts.ott.trim() : "";
|
|
74
|
+
const ottValue = ott.length > 0 ? ott : (await ask("Enter One-Time Token (OTT)")).trim();
|
|
75
|
+
if (!ottValue) {
|
|
76
|
+
process.stderr.write("OTT is required\n");
|
|
77
|
+
process.exitCode = 1;
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
await exchangeOttAndSave(ottValue, region);
|
|
58
82
|
}
|
|
59
83
|
catch (err) {
|
|
60
84
|
process.stderr.write(`OTT exchange failed: ${err.message}\n`);
|
|
@@ -84,29 +108,7 @@ function register(program) {
|
|
|
84
108
|
return;
|
|
85
109
|
}
|
|
86
110
|
try {
|
|
87
|
-
|
|
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");
|
|
111
|
+
await exchangeOttAndSave(ott, region);
|
|
110
112
|
}
|
|
111
113
|
catch (err) {
|
|
112
114
|
process.stderr.write(`OTT exchange failed: ${err.message}\n`);
|
package/dist/commands/image.js
CHANGED
|
@@ -38,11 +38,13 @@ function register(program) {
|
|
|
38
38
|
"8:1",
|
|
39
39
|
];
|
|
40
40
|
const allowedResolutions = ["1K", "2K", "4K"];
|
|
41
|
-
|
|
41
|
+
const allowedTypes = ["default", "storyboard", "subject-turnaround"];
|
|
42
|
+
async function performImageGeneration(session, { prompt, model, type, aspectRatio, resolution, refsList, output, }) {
|
|
42
43
|
const referenceImages = await Promise.all(refsList.map(async (ref) => ({ url: await (0, cerevox_1.getMaterialUri)(session, ref) })));
|
|
43
44
|
const onProgress = (0, progress_1.createProgressSpinner)("inferencing");
|
|
44
45
|
const payload = {
|
|
45
46
|
model: model || "seedream-5l",
|
|
47
|
+
type,
|
|
46
48
|
prompt,
|
|
47
49
|
aspect_ratio: aspectRatio,
|
|
48
50
|
resolution,
|
|
@@ -95,6 +97,13 @@ function register(program) {
|
|
|
95
97
|
return;
|
|
96
98
|
}
|
|
97
99
|
const modelArg = (model ?? undefined);
|
|
100
|
+
const type = typeof opts.type === "string" ? opts.type.trim() : "default";
|
|
101
|
+
if (!allowedTypes.includes(type)) {
|
|
102
|
+
process.stderr.write(`Invalid value for --type: ${type}. Allowed: ${allowedTypes.join("|")}\n`);
|
|
103
|
+
process.exitCode = 1;
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const typeArg = type;
|
|
98
107
|
const aspectRatio = typeof opts.aspectRatio === "string"
|
|
99
108
|
? opts.aspectRatio.trim()
|
|
100
109
|
: undefined;
|
|
@@ -121,6 +130,7 @@ function register(program) {
|
|
|
121
130
|
await performImageGeneration(session, {
|
|
122
131
|
prompt,
|
|
123
132
|
model: modelArg,
|
|
133
|
+
type: typeArg,
|
|
124
134
|
aspectRatio,
|
|
125
135
|
resolution,
|
|
126
136
|
refsList,
|
|
@@ -131,6 +141,7 @@ function register(program) {
|
|
|
131
141
|
parent
|
|
132
142
|
.option("--prompt <prompt>", "Text prompt for image generation (required)")
|
|
133
143
|
.option("--model <model>", `Generator model: ${allowedModels.join("|")}`)
|
|
144
|
+
.option("--type <type>", `Image type: ${allowedTypes.join("|")}`)
|
|
134
145
|
.option("--aspectRatio <ratio>", `Aspect ratio: ${allowedAspectRatios.join("|")}`)
|
|
135
146
|
.option("--resolution <resolution>", `Resolution: ${allowedResolutions.join("|")}`)
|
|
136
147
|
.option("--refs <refs>", "Comma-separated reference image paths/urls")
|
|
@@ -142,6 +153,7 @@ function register(program) {
|
|
|
142
153
|
.description("Create a new image; requires --prompt")
|
|
143
154
|
.option("--prompt <prompt>", "Text prompt for image generation (required)")
|
|
144
155
|
.option("--model <model>", `Generator model: ${allowedModels.join("|")}`)
|
|
156
|
+
.option("--type <type>", `Image type: ${allowedTypes.join("|")}`)
|
|
145
157
|
.option("--aspectRatio <ratio>", `Aspect ratio: ${allowedAspectRatios.join("|")}`)
|
|
146
158
|
.option("--resolution <resolution>", `Resolution: ${allowedResolutions.join("|")}`)
|
|
147
159
|
.option("--refs <refs>", "Comma-separated reference image paths/urls")
|
package/dist/commands/skill.js
CHANGED
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.description = exports.name = void 0;
|
|
7
7
|
exports.register = register;
|
|
8
8
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
9
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
9
10
|
const node_path_1 = __importDefault(require("node:path"));
|
|
10
11
|
exports.name = "skill";
|
|
11
12
|
exports.description = "Print built-in SKILL.md content";
|
|
@@ -16,13 +17,12 @@ function printSkill(relativePath) {
|
|
|
16
17
|
if (!content.endsWith("\n")) {
|
|
17
18
|
process.stdout.write("\n");
|
|
18
19
|
}
|
|
19
|
-
const outputPath = node_path_1.default.resolve(process.cwd(), "zerocut-cli/SKILL.md");
|
|
20
|
-
node_fs_1.default.mkdirSync(node_path_1.default.dirname(outputPath), { recursive: true });
|
|
21
|
-
node_fs_1.default.writeFileSync(outputPath, content, "utf8");
|
|
22
|
-
process.stderr.write(`Saved skill markdown to ${outputPath}\n`);
|
|
23
20
|
}
|
|
24
21
|
function register(program) {
|
|
25
|
-
const parent = program
|
|
22
|
+
const parent = program
|
|
23
|
+
.command("skill")
|
|
24
|
+
.description("Print built-in skill markdown")
|
|
25
|
+
.option("--claude", "Append Claude skill save hint");
|
|
26
26
|
parent
|
|
27
27
|
.command("one-click-video")
|
|
28
28
|
.description("Print one-click-video skill markdown")
|
|
@@ -35,7 +35,11 @@ function register(program) {
|
|
|
35
35
|
.action(() => {
|
|
36
36
|
printSkill("../skill/edit-video/SKILL.md");
|
|
37
37
|
});
|
|
38
|
-
parent.action(() => {
|
|
38
|
+
parent.action((opts) => {
|
|
39
39
|
printSkill("../skill/SKILL.md");
|
|
40
|
+
if (opts.claude) {
|
|
41
|
+
const savePath = node_path_1.default.join(node_os_1.default.homedir(), ".claude", "skills", "zerocut", "SKILL.md");
|
|
42
|
+
process.stdout.write(`\n⚠️ Please save the markdown above to ${savePath} to create the skill.\n`);
|
|
43
|
+
}
|
|
40
44
|
});
|
|
41
45
|
}
|
|
@@ -18,6 +18,7 @@ export declare function syncToTOS(url: string): Promise<string>;
|
|
|
18
18
|
export declare function runFFMpegCommand(session: Session, command: string, resources?: string[]): Promise<{
|
|
19
19
|
exitCode: number;
|
|
20
20
|
outputFilePath: string;
|
|
21
|
+
tosUrl: string | undefined;
|
|
21
22
|
data: {
|
|
22
23
|
stdout: string;
|
|
23
24
|
stderr: string | undefined;
|
package/dist/services/cerevox.js
CHANGED
|
@@ -193,20 +193,23 @@ async function runFFMpegCommand(session, command, resources = []) {
|
|
|
193
193
|
cwd: workDir,
|
|
194
194
|
});
|
|
195
195
|
const outputFilePath = trimmedCommand.startsWith("ffmpeg")
|
|
196
|
-
? finalCommand.split(" ").pop() || ""
|
|
196
|
+
? (finalCommand.split(" ").pop() || "").replace(/^["']|["']$/g, "")
|
|
197
197
|
: "";
|
|
198
198
|
const sandboxFilePath = (0, node_path_2.join)(workDir, outputFilePath);
|
|
199
|
+
let tosUrl;
|
|
199
200
|
// 等待命令完成
|
|
200
201
|
const result = await response.json();
|
|
201
202
|
if (result.exitCode === 0 && outputFilePath) {
|
|
202
203
|
const savePath = (0, node_path_2.join)(process.cwd(), (0, node_path_1.basename)(outputFilePath));
|
|
203
|
-
console.log(sandboxFilePath, savePath);
|
|
204
204
|
const files = session.files;
|
|
205
205
|
await files.download(sandboxFilePath, savePath);
|
|
206
|
+
const sandboxUrl = await getMaterialUri(session, savePath);
|
|
207
|
+
tosUrl = await syncToTOS(sandboxUrl);
|
|
206
208
|
}
|
|
207
209
|
return {
|
|
208
210
|
exitCode: result.exitCode,
|
|
209
211
|
outputFilePath,
|
|
212
|
+
tosUrl,
|
|
210
213
|
data: {
|
|
211
214
|
stdout: result.stdout || (!result.exitCode && result.stderr) || "",
|
|
212
215
|
stderr: result.exitCode ? result.stderr : undefined,
|
|
@@ -49,6 +49,7 @@ const pandoc_1 = require("../commands/pandoc");
|
|
|
49
49
|
const skill_1 = require("../commands/skill");
|
|
50
50
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
51
51
|
const node_path_1 = __importDefault(require("node:path"));
|
|
52
|
+
const node_url_1 = require("node:url");
|
|
52
53
|
function loadBuiltInCommands(program) {
|
|
53
54
|
(0, help_1.register)(program);
|
|
54
55
|
(0, config_1.register)(program);
|
|
@@ -70,7 +71,8 @@ async function loadExternalCommandsAsync(program, dir) {
|
|
|
70
71
|
for (const f of files) {
|
|
71
72
|
const full = node_path_1.default.join(d, f);
|
|
72
73
|
try {
|
|
73
|
-
const
|
|
74
|
+
const moduleSpecifier = (0, node_url_1.pathToFileURL)(full).href;
|
|
75
|
+
const mod = (await Promise.resolve(`${moduleSpecifier}`).then(s => __importStar(require(s))));
|
|
74
76
|
const fn = mod.register ?? mod.default;
|
|
75
77
|
if (typeof fn === "function")
|
|
76
78
|
fn(program);
|
package/dist/skill/SKILL.md
CHANGED
|
@@ -102,13 +102,14 @@ Default action: `create`
|
|
|
102
102
|
|
|
103
103
|
```bash
|
|
104
104
|
npx zerocut-cli image --prompt "a cat on a bike" --output out.png
|
|
105
|
-
npx zerocut-cli image create --prompt "a cat on a bike" --model seedream-5l --aspectRatio 1:1 --resolution 1K --refs ref1.png,ref2.jpg --output out.png
|
|
105
|
+
npx zerocut-cli image create --prompt "a cat on a bike" --model seedream-5l --type default --aspectRatio 1:1 --resolution 1K --refs ref1.png,ref2.jpg --output out.png
|
|
106
106
|
```
|
|
107
107
|
|
|
108
108
|
Options:
|
|
109
109
|
|
|
110
110
|
- `--prompt <prompt>` required
|
|
111
111
|
- `--model <model>`
|
|
112
|
+
- `--type <type>`
|
|
112
113
|
- `--aspectRatio <ratio>`
|
|
113
114
|
- `--resolution <resolution>`
|
|
114
115
|
- `--refs <refs>` comma-separated local paths or URLs
|
|
@@ -118,6 +119,8 @@ Validation rules:
|
|
|
118
119
|
|
|
119
120
|
- `--prompt` must be non-empty
|
|
120
121
|
- `--model` allowed: `seedream|seedream-pro|seedream-5l|banana|banana2|banana-pro|wan|wan-pro`
|
|
122
|
+
- `--type` allowed: `default|storyboard|subject-turnaround`
|
|
123
|
+
- unless user specifies type, default to `default`
|
|
121
124
|
- `--aspectRatio` allowed: `1:1|3:4|4:3|16:9|9:16|2:3|3:2|21:9|1:4|4:1|1:8|8:1`
|
|
122
125
|
- unless user specifies aspect ratio, default to `16:9`
|
|
123
126
|
- `--resolution` allowed: `1K|2K|4K`
|