@renoise/video-maker 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 (28) hide show
  1. package/.claude-plugin/plugin.json +5 -0
  2. package/README.md +50 -0
  3. package/hooks/hooks.json +16 -0
  4. package/hooks/session-start.sh +17 -0
  5. package/lib/gemini.ts +49 -0
  6. package/package.json +22 -0
  7. package/skills/director/SKILL.md +272 -0
  8. package/skills/director/references/narrative-pacing.md +257 -0
  9. package/skills/director/references/style-library.md +179 -0
  10. package/skills/product-sheet-generate/SKILL.md +75 -0
  11. package/skills/renoise-gen/SKILL.md +362 -0
  12. package/skills/renoise-gen/references/api-endpoints.md +138 -0
  13. package/skills/renoise-gen/references/video-capabilities.md +524 -0
  14. package/skills/renoise-gen/renoise-cli.mjs +723 -0
  15. package/skills/scene-generate/SKILL.md +52 -0
  16. package/skills/short-film-editor/SKILL.md +479 -0
  17. package/skills/short-film-editor/examples/mystery-package-4shot.md +260 -0
  18. package/skills/short-film-editor/references/continuity-guide.md +170 -0
  19. package/skills/short-film-editor/scripts/analyze-beats.py +271 -0
  20. package/skills/short-film-editor/scripts/batch-generate.sh +150 -0
  21. package/skills/short-film-editor/scripts/generate-storyboard-html.ts +714 -0
  22. package/skills/short-film-editor/scripts/split-grid.sh +70 -0
  23. package/skills/tiktok-content-maker/SKILL.md +143 -0
  24. package/skills/tiktok-content-maker/examples/dress-demo.md +86 -0
  25. package/skills/tiktok-content-maker/references/ecom-prompt-guide.md +261 -0
  26. package/skills/tiktok-content-maker/scripts/analyze-images.ts +122 -0
  27. package/skills/video-download/SKILL.md +161 -0
  28. package/skills/video-download/scripts/download-video.sh +91 -0
@@ -0,0 +1,723 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/errors.ts
4
+ var ApiError = class extends Error {
5
+ constructor(status, body, message) {
6
+ super(message || `API Error ${status}: ${JSON.stringify(body)}`);
7
+ this.status = status;
8
+ this.body = body;
9
+ this.name = "ApiError";
10
+ }
11
+ };
12
+ var AuthError = class extends ApiError {
13
+ constructor(body) {
14
+ super(401, body, "Authentication failed \u2014 check your API key");
15
+ this.name = "AuthError";
16
+ }
17
+ };
18
+ var InsufficientCreditError = class extends ApiError {
19
+ available;
20
+ required;
21
+ constructor(body) {
22
+ super(402, body, `Insufficient credits: need ${body.required}, have ${body.available}`);
23
+ this.name = "InsufficientCreditError";
24
+ this.available = body.available ?? 0;
25
+ this.required = body.required ?? 0;
26
+ }
27
+ };
28
+
29
+ // src/client.ts
30
+ var RenoiseClient = class {
31
+ baseUrl;
32
+ apiKey;
33
+ authToken;
34
+ constructor(config) {
35
+ this.baseUrl = config.baseUrl.replace(/\/$/, "");
36
+ this.apiKey = config.apiKey;
37
+ this.authToken = config.authToken;
38
+ }
39
+ buildAuthHeaders() {
40
+ const headers = {};
41
+ if (this.apiKey) headers["X-API-Key"] = this.apiKey;
42
+ if (this.authToken) headers["Authorization"] = `Bearer ${this.authToken}`;
43
+ return headers;
44
+ }
45
+ // ---- HTTP ----
46
+ async request(method, path, body) {
47
+ const url = `${this.baseUrl}${path}`;
48
+ const headers = this.buildAuthHeaders();
49
+ if (body) headers["Content-Type"] = "application/json";
50
+ const resp = await fetch(url, {
51
+ method,
52
+ headers,
53
+ body: body ? JSON.stringify(body) : void 0
54
+ });
55
+ if (resp.status === 401) throw new AuthError(await resp.json().catch(() => ({})));
56
+ if (resp.status === 402) throw new InsufficientCreditError(await resp.json().catch(() => ({})));
57
+ const data = await resp.json().catch(() => ({}));
58
+ if (!resp.ok) throw new ApiError(resp.status, data, data.error);
59
+ return data;
60
+ }
61
+ // ---- Credit ----
62
+ async getMe() {
63
+ return this.request("GET", "/me");
64
+ }
65
+ async estimateCost(params) {
66
+ const qs = new URLSearchParams();
67
+ if (params.model) qs.set("model", params.model);
68
+ if (params.duration) qs.set("duration", String(params.duration));
69
+ if (params.hasVideoRef) qs.set("hasVideoRef", "1");
70
+ return this.request("GET", `/credit/estimate?${qs}`);
71
+ }
72
+ async getCreditHistory(limit = 50, offset = 0) {
73
+ return this.request("GET", `/credit/history?limit=${limit}&offset=${offset}`);
74
+ }
75
+ // ---- Task ----
76
+ async createTask(params) {
77
+ return this.request("POST", "/tasks", params);
78
+ }
79
+ async listTasks(params = {}) {
80
+ const qs = new URLSearchParams();
81
+ if (params.status) qs.set("status", params.status);
82
+ if (params.tag) qs.set("tag", params.tag);
83
+ qs.set("limit", String(params.limit ?? 50));
84
+ qs.set("offset", String(params.offset ?? 0));
85
+ return this.request("GET", `/tasks?${qs}`);
86
+ }
87
+ async getTask(id) {
88
+ return this.request("GET", `/tasks/${id}`);
89
+ }
90
+ async getTaskResult(id) {
91
+ return this.request("GET", `/tasks/${id}/result`);
92
+ }
93
+ async cancelTask(id) {
94
+ return this.request("POST", `/tasks/${id}/cancel`);
95
+ }
96
+ async updateTags(id, tags) {
97
+ return this.request("PATCH", `/tasks/${id}/tags`, { tags });
98
+ }
99
+ async listTags() {
100
+ return this.request("GET", "/tags");
101
+ }
102
+ async waitForTask(id, options = {}) {
103
+ const interval = options.pollInterval ?? 1e4;
104
+ const timeout = options.timeout ?? 6e5;
105
+ const start = Date.now();
106
+ while (true) {
107
+ const { task } = await this.getTask(id);
108
+ options.onPoll?.(task);
109
+ if (task.status === "completed") {
110
+ return this.getTaskResult(id);
111
+ }
112
+ if (task.status === "failed") {
113
+ throw new ApiError(400, { error: task.error, status: "failed" }, `Task ${id} failed: ${task.error}`);
114
+ }
115
+ if (task.status === "cancelled") {
116
+ throw new ApiError(400, { status: "cancelled" }, `Task ${id} was cancelled`);
117
+ }
118
+ if (Date.now() - start > timeout) {
119
+ throw new Error(`Task ${id} timed out after ${timeout / 1e3}s (status: ${task.status})`);
120
+ }
121
+ await new Promise((r) => setTimeout(r, interval));
122
+ }
123
+ }
124
+ async generate(params, options) {
125
+ const { task } = await this.createTask(params);
126
+ return this.waitForTask(task.id, options);
127
+ }
128
+ // ---- Material ----
129
+ async uploadMaterial(file, filename, type = "image") {
130
+ const url = `${this.baseUrl}/materials/upload`;
131
+ const form = new FormData();
132
+ const blob = file instanceof Blob ? file : new Blob([file]);
133
+ form.append("file", blob, filename);
134
+ form.append("type", type);
135
+ const resp = await fetch(url, {
136
+ method: "POST",
137
+ headers: this.buildAuthHeaders(),
138
+ body: form
139
+ });
140
+ if (resp.status === 401) throw new AuthError(await resp.json().catch(() => ({})));
141
+ const data = await resp.json().catch(() => ({}));
142
+ if (!resp.ok) throw new ApiError(resp.status, data, data.error);
143
+ return data;
144
+ }
145
+ async listMaterials(params = {}) {
146
+ const qs = new URLSearchParams();
147
+ if (params.type) qs.set("type", params.type);
148
+ if (params.search) qs.set("search", params.search);
149
+ if (params.limit) qs.set("limit", String(params.limit));
150
+ if (params.offset) qs.set("offset", String(params.offset));
151
+ return this.request("GET", `/materials?${qs}`);
152
+ }
153
+ // ---- Character ----
154
+ async listCharacters(params = {}) {
155
+ const qs = new URLSearchParams();
156
+ if (params.category) qs.set("category", params.category);
157
+ if (params.usage_group) qs.set("usage_group", params.usage_group);
158
+ if (params.search) qs.set("search", params.search);
159
+ if (params.page) qs.set("page", String(params.page));
160
+ if (params.page_size) qs.set("page_size", String(params.page_size));
161
+ return this.request("GET", `/characters?${qs}`);
162
+ }
163
+ async getCharacter(id) {
164
+ return this.request("GET", `/characters/${id}`);
165
+ }
166
+ };
167
+
168
+ // src/cli.ts
169
+ import { readFileSync } from "fs";
170
+ import { join, extname, basename } from "path";
171
+ import { fileURLToPath } from "url";
172
+ var __dir = fileURLToPath(new URL(".", import.meta.url));
173
+ function loadEnv() {
174
+ const candidates = [
175
+ join(process.cwd(), ".env"),
176
+ join(__dir, ".env")
177
+ ];
178
+ for (const p of candidates) {
179
+ try {
180
+ const content = readFileSync(p, "utf-8");
181
+ for (const line of content.split("\n")) {
182
+ const trimmed = line.trim();
183
+ if (!trimmed || trimmed.startsWith("#")) continue;
184
+ const eqIdx = trimmed.indexOf("=");
185
+ if (eqIdx === -1) continue;
186
+ const key = trimmed.slice(0, eqIdx).trim();
187
+ let val = trimmed.slice(eqIdx + 1).trim();
188
+ if (val.startsWith('"') && val.endsWith('"') || val.startsWith("'") && val.endsWith("'")) {
189
+ val = val.slice(1, -1);
190
+ }
191
+ if (!process.env[key]) process.env[key] = val;
192
+ }
193
+ break;
194
+ } catch {
195
+ }
196
+ }
197
+ }
198
+ function env(key, fallback) {
199
+ const v = process.env[key] ?? fallback;
200
+ if (!v) {
201
+ console.error(`Error: ${key} is not set.
202
+ Set it via environment variable or .env file.`);
203
+ process.exit(1);
204
+ }
205
+ return v;
206
+ }
207
+ function createClient() {
208
+ loadEnv();
209
+ const apiKey = process.env["RENOISE_API_KEY"];
210
+ const authToken = process.env["RENOISE_AUTH_TOKEN"];
211
+ if (!apiKey && !authToken) {
212
+ console.error("Error: RENOISE_API_KEY or RENOISE_AUTH_TOKEN is required.\nSet one via environment variable or .env file.");
213
+ process.exit(1);
214
+ }
215
+ return new RenoiseClient({
216
+ baseUrl: env("RENOISE_BASE_URL", "https://www.renoise.ai/api/public/v1"),
217
+ apiKey,
218
+ authToken
219
+ });
220
+ }
221
+ function json(data) {
222
+ console.log(JSON.stringify(data, null, 2));
223
+ }
224
+ function parseArgs(args) {
225
+ const flags = {};
226
+ const positional = [];
227
+ for (let i = 0; i < args.length; i++) {
228
+ const arg = args[i];
229
+ if (arg.startsWith("--")) {
230
+ const key = arg.slice(2);
231
+ const next = args[i + 1];
232
+ if (next && !next.startsWith("--")) {
233
+ flags[key] = next;
234
+ i++;
235
+ } else {
236
+ flags[key] = "true";
237
+ }
238
+ } else {
239
+ positional.push(arg);
240
+ }
241
+ }
242
+ return { flags, positional };
243
+ }
244
+ var HELP = `
245
+ RENOISE CLI \u2014 AI generation task management
246
+
247
+ Usage:
248
+ renoise <domain> <action> [options]
249
+
250
+ Domains:
251
+ task Create, list, and manage generation tasks
252
+ material Upload and manage materials
253
+ character Browse available characters
254
+ credit Check balance and transaction history
255
+
256
+ Environment:
257
+ RENOISE_API_KEY API key (starts with fk_), sent as X-API-Key
258
+ RENOISE_AUTH_TOKEN Auth token, sent as Authorization: Bearer
259
+ (at least one of API_KEY or AUTH_TOKEN required)
260
+ RENOISE_BASE_URL (optional) Full API base URL (including path)
261
+ Default: https://www.renoise.ai/api/public/v1
262
+
263
+ Run "renoise <domain> help" for domain-specific commands.
264
+ `.trim();
265
+ var HELP_TASK = `
266
+ renoise task \u2014 Manage generation tasks
267
+
268
+ Commands:
269
+ generate Create task + wait for result (one step)
270
+ create Create a task (returns immediately)
271
+ list List tasks
272
+ get <id> Get task detail
273
+ result <id> Get task result
274
+ wait <id> Wait for task to complete
275
+ cancel <id> Cancel a pending task
276
+ tags List all your tags
277
+ tag <id> --tags a,b,c Update tags on a task
278
+
279
+ Options for generate/create:
280
+ --prompt <text> (required) Generation prompt
281
+ --model <name> Model name (default: renoise-2.0)
282
+ --duration <seconds> Video duration (default: 5)
283
+ --ratio <w:h> Aspect ratio (default: 1:1)
284
+ --resolution <1k|2k> Image resolution (for image models)
285
+ --tags <a,b,c> Comma-separated tags
286
+ --materials <spec> Material refs: "id:role" or "id1:role1,id2:role2"
287
+ --characters <spec> Character refs: "id1,id2" or "id1:role,id2:role"
288
+
289
+ Options for list:
290
+ --status <status> Filter by status
291
+ --tag <tag> Filter by tag
292
+ --limit <n> Max results (default: 20)
293
+ --offset <n> Pagination offset
294
+
295
+ Options for wait:
296
+ --interval <seconds> Poll interval (default: 10)
297
+ --timeout <seconds> Timeout (default: 600)
298
+
299
+ Examples:
300
+ renoise task generate --prompt "a cat dancing" --duration 5
301
+ renoise task generate --prompt "cute cat" --model nano-banana-2
302
+ renoise task create --prompt "epic scene" --duration 10 --ratio 16:9
303
+ renoise task list --status completed --limit 5
304
+ renoise task result 123
305
+ renoise task wait 123 --interval 15
306
+ `.trim();
307
+ var HELP_MATERIAL = `
308
+ renoise material \u2014 Manage materials
309
+
310
+ Commands:
311
+ list List your uploaded materials
312
+ upload <file> Upload a material (image or video)
313
+
314
+ Options for list:
315
+ --type <image|video> Filter by type
316
+ --search <keyword> Search by name
317
+ --limit <n> Max results (default: 20)
318
+
319
+ Options for upload:
320
+ --type <image|video> Override auto-detected type
321
+
322
+ Examples:
323
+ renoise material list
324
+ renoise material upload /path/to/image.jpg
325
+ renoise material upload /path/to/video.mp4 --type video
326
+ `.trim();
327
+ var HELP_CHARACTER = `
328
+ renoise character \u2014 Browse characters
329
+
330
+ Commands:
331
+ list List available characters
332
+ get <id> Get character detail
333
+
334
+ Options for list:
335
+ --category <category> Filter by category
336
+ --usage_group <group> Filter by usage group
337
+ --search <keyword> Search by name
338
+ --page <n> Page number
339
+ --page_size <n> Page size
340
+
341
+ Examples:
342
+ renoise character list
343
+ renoise character list --category female --search Jasmine
344
+ renoise character get 3
345
+ `.trim();
346
+ var HELP_CREDIT = `
347
+ renoise credit \u2014 Balance and transactions
348
+
349
+ Commands:
350
+ me Show current user info and balance
351
+ estimate Estimate task cost
352
+ history Show credit transaction history
353
+
354
+ Options for estimate:
355
+ --model <name> Model name
356
+ --duration <seconds> Duration
357
+ --hasVideoRef Has video reference material
358
+
359
+ Options for history:
360
+ --limit <n> Max results (default: 20)
361
+ --offset <n> Pagination offset
362
+
363
+ Examples:
364
+ renoise credit me
365
+ renoise credit estimate --model renoise-2.0 --duration 5
366
+ renoise credit history --limit 10
367
+ `.trim();
368
+ async function taskGenerate(client, flags) {
369
+ if (!flags.prompt) {
370
+ console.error("Error: --prompt is required.\n");
371
+ console.log(HELP_TASK);
372
+ process.exit(1);
373
+ }
374
+ const params = buildCreateParams(flags);
375
+ console.log("Creating task...");
376
+ const { task } = await client.createTask(params);
377
+ console.log(`Task #${task.id} created (${task.status}). Waiting for completion...`);
378
+ if (task.estimatedCredit) console.log(`Cost: ${task.estimatedCredit} credits`);
379
+ const interval = (flags.interval ? parseInt(flags.interval) : 10) * 1e3;
380
+ const timeout = (flags.timeout ? parseInt(flags.timeout) : 600) * 1e3;
381
+ const result = await client.waitForTask(task.id, {
382
+ pollInterval: interval,
383
+ timeout,
384
+ onPoll: (t) => {
385
+ console.log(` [${(/* @__PURE__ */ new Date()).toLocaleTimeString()}] ${t.status}`);
386
+ }
387
+ });
388
+ console.log("\nDone!");
389
+ printResult(result);
390
+ }
391
+ async function taskCreate(client, flags) {
392
+ if (!flags.prompt) {
393
+ console.error("Error: --prompt is required.\n");
394
+ console.log(HELP_TASK);
395
+ process.exit(1);
396
+ }
397
+ const params = buildCreateParams(flags);
398
+ const data = await client.createTask(params);
399
+ console.log(`Task created: id=${data.task.id}, status=${data.task.status}`);
400
+ if (data.task.estimatedCredit) console.log(`Cost: ${data.task.estimatedCredit} credits`);
401
+ json(data);
402
+ }
403
+ async function taskList(client, flags) {
404
+ const data = await client.listTasks({
405
+ status: flags.status,
406
+ tag: flags.tag,
407
+ limit: flags.limit ? parseInt(flags.limit) : 20,
408
+ offset: flags.offset ? parseInt(flags.offset) : 0
409
+ });
410
+ console.log(`Found ${data.tasks.length} task(s):
411
+ `);
412
+ for (const t of data.tasks) {
413
+ const tags = (() => {
414
+ try {
415
+ return JSON.parse(t.tags || "[]");
416
+ } catch {
417
+ return [];
418
+ }
419
+ })();
420
+ const tagStr = tags.length ? ` [${tags.join(", ")}]` : "";
421
+ console.log(` #${t.id} ${t.status.padEnd(10)} ${t.model} ${t.prompt.slice(0, 60)}${tagStr}`);
422
+ }
423
+ }
424
+ async function taskGet(client, positional) {
425
+ const id = parseInt(positional[0]);
426
+ if (!id) {
427
+ console.error("Error: task ID required.\nUsage: renoise task get <id>");
428
+ process.exit(1);
429
+ }
430
+ json(await client.getTask(id));
431
+ }
432
+ async function taskResult(client, positional) {
433
+ const id = parseInt(positional[0]);
434
+ if (!id) {
435
+ console.error("Error: task ID required.\nUsage: renoise task result <id>");
436
+ process.exit(1);
437
+ }
438
+ const result = await client.getTaskResult(id);
439
+ printResult(result);
440
+ }
441
+ async function taskWait(client, positional, flags) {
442
+ const id = parseInt(positional[0]);
443
+ if (!id) {
444
+ console.error("Error: task ID required.\nUsage: renoise task wait <id>");
445
+ process.exit(1);
446
+ }
447
+ const interval = (flags.interval ? parseInt(flags.interval) : 10) * 1e3;
448
+ const timeout = (flags.timeout ? parseInt(flags.timeout) : 600) * 1e3;
449
+ console.log(`Waiting for task #${id} (poll every ${interval / 1e3}s, timeout ${timeout / 1e3}s)...`);
450
+ const result = await client.waitForTask(id, {
451
+ pollInterval: interval,
452
+ timeout,
453
+ onPoll: (task) => {
454
+ console.log(` [${(/* @__PURE__ */ new Date()).toLocaleTimeString()}] ${task.status}`);
455
+ }
456
+ });
457
+ console.log("\nDone!");
458
+ printResult(result);
459
+ }
460
+ async function taskCancel(client, positional) {
461
+ const id = parseInt(positional[0]);
462
+ if (!id) {
463
+ console.error("Error: task ID required.\nUsage: renoise task cancel <id>");
464
+ process.exit(1);
465
+ }
466
+ await client.cancelTask(id);
467
+ console.log(`Task #${id} cancelled.`);
468
+ }
469
+ async function taskTags(client) {
470
+ json(await client.listTags());
471
+ }
472
+ async function taskTag(client, positional, flags) {
473
+ const id = parseInt(positional[0]);
474
+ if (!id || !flags.tags) {
475
+ console.error("Usage: renoise task tag <id> --tags a,b,c");
476
+ process.exit(1);
477
+ }
478
+ const tags = flags.tags.split(",").map((t) => t.trim());
479
+ json(await client.updateTags(id, tags));
480
+ }
481
+ async function materialList(client, flags) {
482
+ const data = await client.listMaterials({
483
+ type: flags.type,
484
+ search: flags.search,
485
+ limit: flags.limit ? parseInt(flags.limit) : 20,
486
+ offset: flags.offset ? parseInt(flags.offset) : 0
487
+ });
488
+ console.log(`Found ${data.materials.length} material(s):
489
+ `);
490
+ for (const m of data.materials) {
491
+ console.log(` #${m.id} ${m.type.padEnd(6)} ${m.name}`);
492
+ }
493
+ }
494
+ async function materialUpload(client, positional, flags) {
495
+ const filePath = positional[0];
496
+ if (!filePath) {
497
+ console.error("Error: file path required.\nUsage: renoise material upload <file> [--type image|video]");
498
+ process.exit(1);
499
+ }
500
+ const ext = extname(filePath).toLowerCase();
501
+ const videoExts = [".mp4", ".mov", ".avi", ".webm", ".mkv"];
502
+ const type = flags.type || (videoExts.includes(ext) ? "video" : "image");
503
+ const buffer = readFileSync(filePath);
504
+ const filename = basename(filePath);
505
+ console.log(`Uploading ${filename} (${type}, ${(buffer.byteLength / 1024).toFixed(1)}KB)...`);
506
+ const data = await client.uploadMaterial(buffer, filename, type);
507
+ if (data.action === "exists") {
508
+ console.log(`Material already exists: #${data.material.id}`);
509
+ } else {
510
+ console.log(`Material uploaded: #${data.material.id}`);
511
+ }
512
+ json(data);
513
+ }
514
+ async function characterList(client, flags) {
515
+ const data = await client.listCharacters({
516
+ category: flags.category,
517
+ usage_group: flags.usage_group,
518
+ search: flags.search,
519
+ page: flags.page ? parseInt(flags.page) : void 0,
520
+ page_size: flags.page_size ? parseInt(flags.page_size) : void 0
521
+ });
522
+ console.log(`Found ${data.characters.length} character(s) (total: ${data.total}):
523
+ `);
524
+ for (const ch of data.characters) {
525
+ console.log(` #${String(ch.id).padEnd(4)} ${ch.code.padEnd(5)} ${ch.name.padEnd(16)} ${ch.category.padEnd(8)} ${ch.usage_group}`);
526
+ }
527
+ }
528
+ async function characterGet(client, positional) {
529
+ const id = parseInt(positional[0]);
530
+ if (!id) {
531
+ console.error("Error: character ID required.\nUsage: renoise character get <id>");
532
+ process.exit(1);
533
+ }
534
+ json(await client.getCharacter(id));
535
+ }
536
+ async function creditMe(client) {
537
+ json(await client.getMe());
538
+ }
539
+ async function creditEstimate(client, flags) {
540
+ json(await client.estimateCost({
541
+ model: flags.model,
542
+ duration: flags.duration ? parseInt(flags.duration) : void 0,
543
+ hasVideoRef: flags.hasVideoRef === "true" || flags.hasVideoRef === "1"
544
+ }));
545
+ }
546
+ async function creditHistory(client, flags) {
547
+ const limit = flags.limit ? parseInt(flags.limit) : 20;
548
+ const offset = flags.offset ? parseInt(flags.offset) : 0;
549
+ json(await client.getCreditHistory(limit, offset));
550
+ }
551
+ function buildCreateParams(flags) {
552
+ const params = { prompt: flags.prompt };
553
+ if (flags.model) params.model = flags.model;
554
+ if (flags.duration) params.duration = parseInt(flags.duration);
555
+ if (flags.ratio) params.ratio = flags.ratio;
556
+ if (flags.resolution) params.resolution = flags.resolution;
557
+ if (flags.tags) params.tags = flags.tags.split(",").map((t) => t.trim());
558
+ const allMaterials = [];
559
+ if (flags.materials) {
560
+ for (const m of flags.materials.split(",")) {
561
+ const [id, role] = m.trim().split(":");
562
+ allMaterials.push({ id: parseInt(id), role: role || "ref_video" });
563
+ }
564
+ }
565
+ if (flags.characters) {
566
+ for (const m of flags.characters.split(",")) {
567
+ const trimmed = m.trim();
568
+ const parts = trimmed.split(":");
569
+ const charId = parseInt(parts[0]);
570
+ const role = parts[1] || "reference_image";
571
+ allMaterials.push({ character_id: charId, role });
572
+ }
573
+ }
574
+ if (allMaterials.length) params.materials = allMaterials;
575
+ return params;
576
+ }
577
+ function printResult(result) {
578
+ console.log(`Task #${result.taskId} ${result.status}`);
579
+ if (result.videoUrl) console.log(` Video: ${result.videoUrl}`);
580
+ if (result.coverUrl) console.log(` Cover: ${result.coverUrl}`);
581
+ if (result.imageUrl) console.log(` Image: ${result.imageUrl}`);
582
+ if (result.resolutions && Object.keys(result.resolutions).length) {
583
+ console.log(` Resolutions: ${Object.keys(result.resolutions).join(", ")}`);
584
+ }
585
+ if (result.warning) console.log(` Warning: ${result.warning}`);
586
+ json(result);
587
+ }
588
+ var DOMAIN_HELP = {
589
+ task: HELP_TASK,
590
+ material: HELP_MATERIAL,
591
+ character: HELP_CHARACTER,
592
+ credit: HELP_CREDIT
593
+ };
594
+ async function main() {
595
+ const args = process.argv.slice(2);
596
+ const { flags, positional } = parseArgs(args);
597
+ const domain = positional[0];
598
+ const action = positional[1];
599
+ const subPositional = positional.slice(2);
600
+ if (!domain || domain === "help" || flags.help === "true") {
601
+ console.log(HELP);
602
+ return;
603
+ }
604
+ if (action === "help" || !action && flags.help !== "true") {
605
+ console.log(DOMAIN_HELP[domain] || HELP);
606
+ return;
607
+ }
608
+ if (flags.help === "true") {
609
+ console.log(DOMAIN_HELP[domain] || HELP);
610
+ return;
611
+ }
612
+ const client = createClient();
613
+ try {
614
+ switch (domain) {
615
+ case "task":
616
+ switch (action) {
617
+ case "generate":
618
+ await taskGenerate(client, flags);
619
+ break;
620
+ case "create":
621
+ await taskCreate(client, flags);
622
+ break;
623
+ case "list":
624
+ await taskList(client, flags);
625
+ break;
626
+ case "get":
627
+ await taskGet(client, subPositional);
628
+ break;
629
+ case "result":
630
+ await taskResult(client, subPositional);
631
+ break;
632
+ case "wait":
633
+ await taskWait(client, subPositional, flags);
634
+ break;
635
+ case "cancel":
636
+ await taskCancel(client, subPositional);
637
+ break;
638
+ case "tags":
639
+ await taskTags(client);
640
+ break;
641
+ case "tag":
642
+ await taskTag(client, subPositional, flags);
643
+ break;
644
+ default:
645
+ console.error(`Unknown task action: ${action}
646
+ `);
647
+ console.log(HELP_TASK);
648
+ process.exit(1);
649
+ }
650
+ break;
651
+ case "material":
652
+ switch (action) {
653
+ case "list":
654
+ await materialList(client, flags);
655
+ break;
656
+ case "upload":
657
+ await materialUpload(client, subPositional, flags);
658
+ break;
659
+ default:
660
+ console.error(`Unknown material action: ${action}
661
+ `);
662
+ console.log(HELP_MATERIAL);
663
+ process.exit(1);
664
+ }
665
+ break;
666
+ case "character":
667
+ switch (action) {
668
+ case "list":
669
+ await characterList(client, flags);
670
+ break;
671
+ case "get":
672
+ await characterGet(client, subPositional);
673
+ break;
674
+ default:
675
+ console.error(`Unknown character action: ${action}
676
+ `);
677
+ console.log(HELP_CHARACTER);
678
+ process.exit(1);
679
+ }
680
+ break;
681
+ case "credit":
682
+ switch (action) {
683
+ case "me":
684
+ await creditMe(client);
685
+ break;
686
+ case "estimate":
687
+ await creditEstimate(client, flags);
688
+ break;
689
+ case "history":
690
+ await creditHistory(client, flags);
691
+ break;
692
+ default:
693
+ console.error(`Unknown credit action: ${action}
694
+ `);
695
+ console.log(HELP_CREDIT);
696
+ process.exit(1);
697
+ }
698
+ break;
699
+ default:
700
+ console.error(`Unknown domain: ${domain}
701
+ `);
702
+ console.log(HELP);
703
+ process.exit(1);
704
+ }
705
+ } catch (e) {
706
+ if (e instanceof AuthError) {
707
+ console.error(`Auth Error: ${e.message}`);
708
+ console.error("Make sure RENOISE_API_KEY is set correctly.");
709
+ process.exit(1);
710
+ }
711
+ if (e instanceof InsufficientCreditError) {
712
+ console.error(`Credit Error: ${e.message}`);
713
+ console.error(` Available: ${e.available}, Required: ${e.required}`);
714
+ process.exit(1);
715
+ }
716
+ if (e instanceof ApiError) {
717
+ console.error(`API Error (${e.status}): ${e.message}`);
718
+ process.exit(1);
719
+ }
720
+ throw e;
721
+ }
722
+ }
723
+ main();