@mrgoonie/multix 0.7.2 → 0.7.3

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/dist/cli.js CHANGED
@@ -95,151 +95,6 @@ var init_errors = __esm({
95
95
  }
96
96
  });
97
97
 
98
- // src/core/fetch-json.ts
99
- function parseRetryAfter(value, now = Date.now()) {
100
- if (!value) return void 0;
101
- const trimmed = value.trim();
102
- if (/^\d+(\.\d+)?$/.test(trimmed)) return Math.round(Number(trimmed) * 1e3);
103
- const date = Date.parse(trimmed);
104
- if (Number.isNaN(date)) return void 0;
105
- return Math.max(0, date - now);
106
- }
107
- async function httpErrorFromResponse(response, url) {
108
- let snippet = "";
109
- try {
110
- const text = await response.text();
111
- snippet = text.slice(0, SNIPPET_MAX);
112
- } catch {
113
- snippet = "(could not read body)";
114
- }
115
- return new HttpError(
116
- response.status,
117
- snippet,
118
- url,
119
- parseRetryAfter(response.headers.get("retry-after"))
120
- );
121
- }
122
- async function fetchJson(opts) {
123
- const { url, method = "GET", headers = {}, body, timeoutMs = DEFAULT_TIMEOUT_MS } = opts;
124
- const doFetch = opts.fetch ?? ((input, init2) => globalThis.fetch(input, init2));
125
- const init = {
126
- method,
127
- headers: {
128
- "Content-Type": "application/json",
129
- ...headers
130
- },
131
- signal: AbortSignal.timeout(timeoutMs)
132
- };
133
- if (body !== void 0) {
134
- init.body = JSON.stringify(body);
135
- }
136
- let response;
137
- try {
138
- response = await doFetch(url, init);
139
- } catch (cause) {
140
- const msg = cause instanceof Error ? cause.message : String(cause);
141
- throw new HttpError(0, msg, url);
142
- }
143
- if (!response.ok) {
144
- throw await httpErrorFromResponse(response, url);
145
- }
146
- let text;
147
- try {
148
- text = await response.text();
149
- } catch (cause) {
150
- const msg = cause instanceof Error ? cause.message : String(cause);
151
- throw new HttpError(0, `Failed to read response body: ${msg}`, url);
152
- }
153
- if (text.trim() === "") return void 0;
154
- try {
155
- return JSON.parse(text);
156
- } catch {
157
- throw new HttpError(
158
- response.status,
159
- `Invalid JSON response: ${text.slice(0, SNIPPET_MAX)}`,
160
- url
161
- );
162
- }
163
- }
164
- var DEFAULT_TIMEOUT_MS, SNIPPET_MAX;
165
- var init_fetch_json = __esm({
166
- "src/core/fetch-json.ts"() {
167
- "use strict";
168
- init_errors();
169
- DEFAULT_TIMEOUT_MS = 12e4;
170
- SNIPPET_MAX = 500;
171
- }
172
- });
173
-
174
- // src/core/http-client.ts
175
- var http_client_exports = {};
176
- __export(http_client_exports, {
177
- downloadFile: () => downloadFile,
178
- fetchBytes: () => fetchBytes,
179
- httpJson: () => httpJson
180
- });
181
- import { once } from "events";
182
- import fs4 from "fs";
183
- import path4 from "path";
184
- import { pipeline } from "stream/promises";
185
- async function httpJson(opts) {
186
- return fetchJson(opts);
187
- }
188
- async function downloadFile(url, dest, timeoutMs = DEFAULT_TIMEOUT_MS, headers) {
189
- fs4.mkdirSync(path4.dirname(dest), { recursive: true });
190
- let response;
191
- try {
192
- response = await globalThis.fetch(url, {
193
- signal: AbortSignal.timeout(timeoutMs),
194
- ...headers ? { headers } : {}
195
- });
196
- } catch (cause) {
197
- const msg = cause instanceof Error ? cause.message : String(cause);
198
- throw new HttpError(0, msg, url);
199
- }
200
- if (!response.ok) {
201
- throw new HttpError(response.status, "(download failed)", url);
202
- }
203
- if (!response.body) {
204
- throw new HttpError(0, "Response body is null", url);
205
- }
206
- const fileStream = fs4.createWriteStream(dest);
207
- try {
208
- await pipeline(response.body, fileStream);
209
- } catch (err2) {
210
- if (!fileStream.closed) await once(fileStream, "close");
211
- throw err2;
212
- }
213
- }
214
- async function fetchBytes(urlOrDataUri) {
215
- if (urlOrDataUri.startsWith("data:")) {
216
- const commaIdx = urlOrDataUri.indexOf(",");
217
- if (commaIdx === -1) throw new Error("Malformed data URI: missing comma");
218
- const encoded = urlOrDataUri.slice(commaIdx + 1);
219
- return Buffer.from(encoded, "base64");
220
- }
221
- let response;
222
- try {
223
- response = await globalThis.fetch(urlOrDataUri, {
224
- signal: AbortSignal.timeout(DEFAULT_TIMEOUT_MS)
225
- });
226
- } catch (cause) {
227
- const msg = cause instanceof Error ? cause.message : String(cause);
228
- throw new HttpError(0, msg, urlOrDataUri);
229
- }
230
- if (!response.ok) {
231
- throw new HttpError(response.status, "(bytes fetch failed)", urlOrDataUri);
232
- }
233
- return Buffer.from(await response.arrayBuffer());
234
- }
235
- var init_http_client = __esm({
236
- "src/core/http-client.ts"() {
237
- "use strict";
238
- init_errors();
239
- init_fetch_json();
240
- }
241
- });
242
-
243
98
  // src/cli.ts
244
99
  import { createRequire as createRequire2 } from "module";
245
100
  import { Command } from "commander";
@@ -447,7 +302,135 @@ function resolveKey(name) {
447
302
 
448
303
  // src/providers/byteplus/client.ts
449
304
  init_errors();
450
- init_http_client();
305
+
306
+ // src/core/http-client.ts
307
+ init_errors();
308
+ import { once } from "events";
309
+ import fs4 from "fs";
310
+ import path4 from "path";
311
+ import { pipeline } from "stream/promises";
312
+
313
+ // src/core/fetch-json.ts
314
+ init_errors();
315
+ var DEFAULT_TIMEOUT_MS = 12e4;
316
+ var SNIPPET_MAX = 500;
317
+ function parseRetryAfter(value, now = Date.now()) {
318
+ if (!value) return void 0;
319
+ const trimmed = value.trim();
320
+ if (/^\d+(\.\d+)?$/.test(trimmed)) return Math.round(Number(trimmed) * 1e3);
321
+ const date = Date.parse(trimmed);
322
+ if (Number.isNaN(date)) return void 0;
323
+ return Math.max(0, date - now);
324
+ }
325
+ async function httpErrorFromResponse(response, url) {
326
+ let snippet = "";
327
+ try {
328
+ const text = await response.text();
329
+ snippet = text.slice(0, SNIPPET_MAX);
330
+ } catch {
331
+ snippet = "(could not read body)";
332
+ }
333
+ return new HttpError(
334
+ response.status,
335
+ snippet,
336
+ url,
337
+ parseRetryAfter(response.headers.get("retry-after"))
338
+ );
339
+ }
340
+ async function fetchJson(opts) {
341
+ const { url, method = "GET", headers: headers2 = {}, body, timeoutMs = DEFAULT_TIMEOUT_MS } = opts;
342
+ const doFetch = opts.fetch ?? ((input, init2) => globalThis.fetch(input, init2));
343
+ const init = {
344
+ method,
345
+ headers: {
346
+ "Content-Type": "application/json",
347
+ ...headers2
348
+ },
349
+ signal: AbortSignal.timeout(timeoutMs)
350
+ };
351
+ if (body !== void 0) {
352
+ init.body = JSON.stringify(body);
353
+ }
354
+ let response;
355
+ try {
356
+ response = await doFetch(url, init);
357
+ } catch (cause) {
358
+ const msg = cause instanceof Error ? cause.message : String(cause);
359
+ throw new HttpError(0, msg, url);
360
+ }
361
+ if (!response.ok) {
362
+ throw await httpErrorFromResponse(response, url);
363
+ }
364
+ let text;
365
+ try {
366
+ text = await response.text();
367
+ } catch (cause) {
368
+ const msg = cause instanceof Error ? cause.message : String(cause);
369
+ throw new HttpError(0, `Failed to read response body: ${msg}`, url);
370
+ }
371
+ if (text.trim() === "") return void 0;
372
+ try {
373
+ return JSON.parse(text);
374
+ } catch {
375
+ throw new HttpError(
376
+ response.status,
377
+ `Invalid JSON response: ${text.slice(0, SNIPPET_MAX)}`,
378
+ url
379
+ );
380
+ }
381
+ }
382
+
383
+ // src/core/http-client.ts
384
+ async function httpJson(opts) {
385
+ return fetchJson(opts);
386
+ }
387
+ async function downloadFile(url, dest, timeoutMs = DEFAULT_TIMEOUT_MS, headers2) {
388
+ fs4.mkdirSync(path4.dirname(dest), { recursive: true });
389
+ let response;
390
+ try {
391
+ response = await globalThis.fetch(url, {
392
+ signal: AbortSignal.timeout(timeoutMs),
393
+ ...headers2 ? { headers: headers2 } : {}
394
+ });
395
+ } catch (cause) {
396
+ const msg = cause instanceof Error ? cause.message : String(cause);
397
+ throw new HttpError(0, msg, url);
398
+ }
399
+ if (!response.ok) {
400
+ throw new HttpError(response.status, "(download failed)", url);
401
+ }
402
+ if (!response.body) {
403
+ throw new HttpError(0, "Response body is null", url);
404
+ }
405
+ const fileStream = fs4.createWriteStream(dest);
406
+ try {
407
+ await pipeline(response.body, fileStream);
408
+ } catch (err2) {
409
+ if (!fileStream.closed) await once(fileStream, "close");
410
+ throw err2;
411
+ }
412
+ }
413
+ async function fetchBytes(urlOrDataUri) {
414
+ if (urlOrDataUri.startsWith("data:")) {
415
+ const commaIdx = urlOrDataUri.indexOf(",");
416
+ if (commaIdx === -1) throw new Error("Malformed data URI: missing comma");
417
+ const encoded = urlOrDataUri.slice(commaIdx + 1);
418
+ return Buffer.from(encoded, "base64");
419
+ }
420
+ let response;
421
+ try {
422
+ response = await globalThis.fetch(urlOrDataUri, {
423
+ signal: AbortSignal.timeout(DEFAULT_TIMEOUT_MS)
424
+ });
425
+ } catch (cause) {
426
+ const msg = cause instanceof Error ? cause.message : String(cause);
427
+ throw new HttpError(0, msg, urlOrDataUri);
428
+ }
429
+ if (!response.ok) {
430
+ throw new HttpError(response.status, "(bytes fetch failed)", urlOrDataUri);
431
+ }
432
+ return Buffer.from(await response.arrayBuffer());
433
+ }
451
434
 
452
435
  // src/providers/byteplus/models.ts
453
436
  var DEFAULT_BYTEPLUS_BASE_URL = "https://ark.ap-southeast.bytepluses.com/api/v3";
@@ -527,7 +510,6 @@ function createBytePlusClient() {
527
510
  }
528
511
 
529
512
  // src/providers/byteplus/generators/three-d.ts
530
- init_http_client();
531
513
  function buildThreeDTaskBody(opts) {
532
514
  const promptText = (opts.prompt ?? "").trim();
533
515
  const flagPart = opts.flags?.trim() ?? "";
@@ -835,7 +817,6 @@ function finalizeGeneratedImages(files, opts) {
835
817
  }
836
818
 
837
819
  // src/providers/byteplus/generators/image.ts
838
- init_http_client();
839
820
  import fs7 from "fs";
840
821
  import path7 from "path";
841
822
  var ASPECT_TO_SIZE = {
@@ -999,7 +980,6 @@ function collect2(value, prev) {
999
980
  import path9 from "path";
1000
981
 
1001
982
  // src/core/video-thumb.ts
1002
- init_http_client();
1003
983
  import fs8 from "fs";
1004
984
  import path8 from "path";
1005
985
  var THUMB_KEYS = /* @__PURE__ */ new Set([
@@ -1084,7 +1064,6 @@ async function maybeDownloadThumb(source, videoPath, opts = {}) {
1084
1064
  }
1085
1065
 
1086
1066
  // src/providers/byteplus/generators/video.ts
1087
- init_http_client();
1088
1067
  function buildFlagSuffix(opts) {
1089
1068
  const parts = [];
1090
1069
  if (opts.resolution) parts.push(`--rs ${opts.resolution}`);
@@ -1895,7 +1874,6 @@ function registerCloudflareCommands(program2) {
1895
1874
  import fs10 from "fs";
1896
1875
  import path14 from "path";
1897
1876
  init_errors();
1898
- init_http_client();
1899
1877
 
1900
1878
  // src/providers/elevenlabs/models.ts
1901
1879
  var ELEVENLABS_BASE_URL = "https://api.elevenlabs.io/v1";
@@ -2004,19 +1982,19 @@ async function apiPostBinary(endpoint2, body, apiKey, opts = {}) {
2004
1982
  if (opts.query) {
2005
1983
  for (const [k, v] of Object.entries(opts.query)) url.searchParams.set(k, v);
2006
1984
  }
2007
- const headers = authHeaders(apiKey);
1985
+ const headers2 = authHeaders(apiKey);
2008
1986
  let init;
2009
1987
  if (body instanceof FormData) {
2010
1988
  init = {
2011
1989
  method: "POST",
2012
- headers,
1990
+ headers: headers2,
2013
1991
  body,
2014
1992
  signal: AbortSignal.timeout(opts.timeoutMs ?? DEFAULT_TIMEOUT_MS2)
2015
1993
  };
2016
1994
  } else {
2017
1995
  init = {
2018
1996
  method: "POST",
2019
- headers: { ...headers, "Content-Type": "application/json" },
1997
+ headers: { ...headers2, "Content-Type": "application/json" },
2020
1998
  body: JSON.stringify(body),
2021
1999
  signal: AbortSignal.timeout(opts.timeoutMs ?? DEFAULT_TIMEOUT_MS2)
2022
2000
  };
@@ -2185,7 +2163,6 @@ function registerCloneCommand(parent) {
2185
2163
 
2186
2164
  // src/providers/elevenlabs/commands/dub.ts
2187
2165
  init_errors();
2188
- init_http_client();
2189
2166
  import fs12 from "fs";
2190
2167
  import path16 from "path";
2191
2168
  function registerDubCommand(parent) {
@@ -2669,7 +2646,6 @@ var FAL_DEFAULTS = {
2669
2646
  };
2670
2647
 
2671
2648
  // src/providers/fal/run-helpers.ts
2672
- init_http_client();
2673
2649
  import fs16 from "fs";
2674
2650
  import path19 from "path";
2675
2651
 
@@ -2706,7 +2682,6 @@ init_redact();
2706
2682
  // src/providers/fal/client.ts
2707
2683
  init_errors();
2708
2684
  init_errors();
2709
- init_http_client();
2710
2685
  init_redact();
2711
2686
  function requireFalKey() {
2712
2687
  const key = resolveKey("FAL_KEY");
@@ -3118,7 +3093,6 @@ import fs20 from "fs";
3118
3093
  import fs18 from "fs";
3119
3094
  import path22 from "path";
3120
3095
  init_errors();
3121
- init_http_client();
3122
3096
 
3123
3097
  // src/providers/gemini/task-resolver.ts
3124
3098
  import path21 from "path";
@@ -3813,11 +3787,179 @@ function collectSpeakers(value, prev) {
3813
3787
  }
3814
3788
 
3815
3789
  // src/providers/gemini/commands/generate-video.ts
3816
- import fs23 from "fs";
3790
+ import fs24 from "fs";
3817
3791
  import path25 from "path";
3818
3792
  init_errors();
3819
- init_http_client();
3820
- var BASE2 = "https://generativelanguage.googleapis.com";
3793
+
3794
+ // src/providers/gemini/veo.ts
3795
+ import fs23 from "fs";
3796
+
3797
+ // src/core/async-poll.ts
3798
+ init_errors();
3799
+ var PollTimeoutError2 = class extends MultixError {
3800
+ constructor(message, lastStatus) {
3801
+ super(message, "POLL_TIMEOUT");
3802
+ this.lastStatus = lastStatus;
3803
+ this.name = "PollTimeoutError";
3804
+ }
3805
+ lastStatus;
3806
+ };
3807
+ function isTransientHttpError(error) {
3808
+ if (!(error instanceof HttpError)) return false;
3809
+ return error.status === 0 || error.status === 429 || error.status >= 500;
3810
+ }
3811
+ var defaultSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
3812
+ async function pollUntil2(opts) {
3813
+ const now = opts.now ?? Date.now;
3814
+ const sleep5 = opts.sleep ?? defaultSleep;
3815
+ const random = opts.random ?? Math.random;
3816
+ const isTransient = opts.isTransientError ?? isTransientHttpError;
3817
+ const maxInterval = opts.maxIntervalMs ?? 15e3;
3818
+ const minInterval = opts.minIntervalMs ?? 500;
3819
+ const factor = opts.backoffFactor ?? 1.5;
3820
+ const jitter = opts.jitter ?? true;
3821
+ const deadline = now() + opts.timeoutMs;
3822
+ let interval = opts.intervalMs ?? 3e3;
3823
+ let attempt = 0;
3824
+ let last;
3825
+ let lastError;
3826
+ for (; ; ) {
3827
+ attempt++;
3828
+ let retryAfterMs;
3829
+ try {
3830
+ last = await opts.fetchStatus({ remainingMs: Math.max(1, deadline - now()), attempt });
3831
+ lastError = void 0;
3832
+ opts.onPoll?.(last, attempt);
3833
+ if (opts.isTerminal(last)) return last;
3834
+ } catch (error) {
3835
+ if (!isTransient(error)) throw error;
3836
+ lastError = error;
3837
+ if (error instanceof HttpError) retryAfterMs = error.retryAfterMs;
3838
+ }
3839
+ const remaining = deadline - now();
3840
+ if (remaining <= 0) {
3841
+ const hint = opts.resumeHint ? ` ${opts.resumeHint}` : "";
3842
+ const cause = lastError instanceof Error ? ` Last error: ${lastError.message}.` : "";
3843
+ throw new PollTimeoutError2(
3844
+ `Timed out after ${opts.timeoutMs}ms waiting for job.${cause}${hint}`,
3845
+ last
3846
+ );
3847
+ }
3848
+ let delay = retryAfterMs ?? interval;
3849
+ if (jitter && retryAfterMs === void 0) delay *= 0.8 + random() * 0.4;
3850
+ await sleep5(Math.min(Math.max(delay, minInterval), remaining));
3851
+ interval = Math.min(interval * factor, maxInterval);
3852
+ }
3853
+ }
3854
+
3855
+ // src/providers/gemini/veo.ts
3856
+ init_errors();
3857
+ var API_HOST = "generativelanguage.googleapis.com";
3858
+ var BASE2 = `https://${API_HOST}/v1beta`;
3859
+ var VEO_IMAGE_TYPES = /* @__PURE__ */ new Set(["image/jpeg", "image/png", "image/webp"]);
3860
+ var VEO_TIMEOUT_MS = 10 * 60 * 1e3;
3861
+ function veoImageFromFile(imgPath) {
3862
+ if (!fs23.existsSync(imgPath)) throw new ValidationError(`Image not found: ${imgPath}`);
3863
+ const detected = getMimeType(imgPath);
3864
+ const mimeType = detected === "application/octet-stream" ? "image/jpeg" : detected;
3865
+ if (!VEO_IMAGE_TYPES.has(mimeType)) {
3866
+ throw new ValidationError(`Veo accepts JPEG, PNG or WebP images: ${imgPath} (${mimeType})`);
3867
+ }
3868
+ return { bytesBase64Encoded: fs23.readFileSync(imgPath).toString("base64"), mimeType };
3869
+ }
3870
+ function veoFramesFromPaths(paths = []) {
3871
+ if (paths.length > 2) {
3872
+ throw new ValidationError(
3873
+ "--reference-images accepts at most two images (opening frame, closing frame)"
3874
+ );
3875
+ }
3876
+ const [first, last] = paths;
3877
+ return {
3878
+ image: first ? veoImageFromFile(first) : void 0,
3879
+ lastFrame: last ? veoImageFromFile(last) : void 0
3880
+ };
3881
+ }
3882
+ function modelPath(model) {
3883
+ return model.replace(/^models\//, "");
3884
+ }
3885
+ function buildVeoRequest(opts) {
3886
+ const instance = { prompt: opts.prompt };
3887
+ if (opts.image) instance.image = opts.image;
3888
+ if (opts.lastFrame) instance.lastFrame = opts.lastFrame;
3889
+ return {
3890
+ instances: [instance],
3891
+ parameters: { aspectRatio: opts.aspectRatio, resolution: opts.resolution }
3892
+ };
3893
+ }
3894
+ function extractVeoVideoUri(op) {
3895
+ const r = op.response ?? {};
3896
+ return r.generateVideoResponse?.generatedSamples?.[0]?.video?.uri ?? r.generatedSamples?.[0]?.video?.uri ?? r.generatedVideos?.[0]?.video?.uri;
3897
+ }
3898
+ function headers(apiKey) {
3899
+ return { "x-goog-api-key": apiKey };
3900
+ }
3901
+ async function submitVeo(apiKey, model, body) {
3902
+ try {
3903
+ return await httpJson({
3904
+ url: `${BASE2}/models/${modelPath(model)}:predictLongRunning`,
3905
+ method: "POST",
3906
+ headers: headers(apiKey),
3907
+ body
3908
+ });
3909
+ } catch (e) {
3910
+ const msg = e instanceof Error ? e.message : String(e);
3911
+ throw new ProviderError(`Veo submit failed: ${msg}`, "gemini");
3912
+ }
3913
+ }
3914
+ async function waitForVeo(apiKey, operation, logger, opts = {}) {
3915
+ if (operation.done) return checkVeoResult(operation);
3916
+ const finalOp = await pollUntil2({
3917
+ fetchStatus: ({ remainingMs }) => httpJson({
3918
+ url: `${BASE2}/${operation.name}`,
3919
+ headers: headers(apiKey),
3920
+ timeoutMs: Math.min(remainingMs, 6e4)
3921
+ }),
3922
+ isTerminal: (op) => op.done === true,
3923
+ timeoutMs: opts.timeoutMs ?? VEO_TIMEOUT_MS,
3924
+ intervalMs: opts.intervalMs ?? 1e4,
3925
+ maxIntervalMs: 2e4,
3926
+ sleep: opts.sleep,
3927
+ onPoll: (op, attempt) => {
3928
+ if (!op.done) logger.debug(`Still generating... (poll ${attempt})`);
3929
+ },
3930
+ resumeHint: `Operation (still running server-side): ${operation.name}`
3931
+ });
3932
+ return checkVeoResult(finalOp);
3933
+ }
3934
+ function checkVeoResult(op) {
3935
+ if (op.error) throw new ProviderError(`Veo error: ${JSON.stringify(op.error)}`, "gemini");
3936
+ return op;
3937
+ }
3938
+ async function downloadVeoVideo(apiKey, uri, dest) {
3939
+ let host = "";
3940
+ try {
3941
+ host = new URL(uri).hostname;
3942
+ } catch {
3943
+ throw new ProviderError("Veo returned an invalid video URI", "gemini");
3944
+ }
3945
+ await downloadFile(uri, dest, 3e5, host === API_HOST ? headers(apiKey) : void 0);
3946
+ }
3947
+ async function runVeo(apiKey, model, request, dest, logger) {
3948
+ const operation = await submitVeo(apiKey, model, buildVeoRequest(request));
3949
+ logger.debug(`Operation: ${operation.name}`);
3950
+ const finalOp = await waitForVeo(apiKey, operation, logger);
3951
+ const uri = extractVeoVideoUri(finalOp);
3952
+ if (!uri) {
3953
+ throw new ProviderError(
3954
+ `Unexpected Veo response structure: ${JSON.stringify(finalOp.response)}`,
3955
+ "gemini"
3956
+ );
3957
+ }
3958
+ await downloadVeoVideo(apiKey, uri, dest);
3959
+ return finalOp;
3960
+ }
3961
+
3962
+ // src/providers/gemini/commands/generate-video.ts
3821
3963
  function registerGenerateVideoCommand(parent) {
3822
3964
  parent.command("generate-video").description("Generate video with Veo via Gemini API [EXPERIMENTAL]").requiredOption("--prompt <text>", "Video generation prompt").option("--model <id>", "Veo model to use").option("--resolution <res>", "Video resolution: 720p|1080p", "1080p").option("--aspect-ratio <ratio>", "Aspect ratio", "16:9").option(
3823
3965
  "--reference-images <paths...>",
@@ -3841,72 +3983,24 @@ function registerGenerateVideoCommand(parent) {
3841
3983
  }
3842
3984
  logger.debug(`Submitting video generation with ${model}...`);
3843
3985
  logger.info("This may take 30s\u20136 minutes. Veo requires billing.");
3844
- const config = {
3845
- aspectRatio: opts.aspectRatio,
3846
- resolution: opts.resolution
3847
- };
3848
- const contentParts = [{ text: opts.prompt }];
3849
- if (opts.referenceImages?.length) {
3850
- for (const imgPath of opts.referenceImages.slice(0, 2)) {
3851
- if (!fs23.existsSync(imgPath))
3852
- throw new ValidationError(`Reference image not found: ${imgPath}`);
3853
- const data = fs23.readFileSync(imgPath).toString("base64");
3854
- const ext = path25.extname(imgPath).toLowerCase();
3855
- const mimeType = ext === ".png" ? "image/png" : "image/jpeg";
3856
- contentParts.push({ inlineData: { mimeType, data } });
3857
- }
3858
- }
3859
- const headers = { "x-goog-api-key": apiKey, "Content-Type": "application/json" };
3860
- let operation;
3861
- try {
3862
- operation = await httpJson({
3863
- url: `${BASE2}/v1beta/models/${model}:generateVideos`,
3864
- method: "POST",
3865
- headers,
3866
- body: {
3867
- contents: [{ parts: contentParts }],
3868
- generationConfig: config
3869
- }
3870
- });
3871
- } catch (e) {
3872
- const msg = e instanceof Error ? e.message : String(e);
3873
- throw new ProviderError(`Video generation submit failed: ${msg}`, "gemini");
3874
- }
3875
- const operationName = operation.name;
3876
- logger.debug(`Operation: ${operationName}`);
3877
- const deadline = Date.now() + 10 * 60 * 1e3;
3878
- let done = false;
3879
- let finalOp = operation;
3880
- while (!done && Date.now() < deadline) {
3881
- await sleep3(1e4);
3882
- finalOp = await httpJson({
3883
- url: `${BASE2}/v1beta/${operationName}`,
3884
- headers
3885
- });
3886
- done = finalOp.done === true;
3887
- if (!done)
3888
- logger.debug(
3889
- `Still generating... (${Math.round((deadline - Date.now()) / 1e3)}s left)`
3890
- );
3891
- }
3892
- if (!done) throw new ProviderError("Video generation timed out after 10 minutes", "gemini");
3893
- if (finalOp.error)
3894
- throw new ProviderError(`Veo error: ${JSON.stringify(finalOp.error)}`, "gemini");
3895
- const videoUri = finalOp.response?.generatedSamples?.[0]?.video?.uri ?? finalOp.response?.generatedVideos?.[0]?.video?.uri;
3896
- if (!videoUri) {
3897
- throw new ProviderError(
3898
- `Unexpected Veo response structure: ${JSON.stringify(finalOp.response)}`,
3899
- "gemini"
3900
- );
3901
- }
3902
- const outDir = getOutputDir();
3903
- const dest = path25.join(outDir, `veo_generated_${Date.now()}.mp4`);
3904
- const { downloadFile: downloadFile2 } = await Promise.resolve().then(() => (init_http_client(), http_client_exports));
3905
- await downloadFile2(videoUri, dest);
3986
+ const frames = veoFramesFromPaths(opts.referenceImages);
3987
+ const dest = path25.join(getOutputDir(), `veo_generated_${Date.now()}.mp4`);
3988
+ const finalOp = await runVeo(
3989
+ apiKey,
3990
+ model,
3991
+ {
3992
+ prompt: opts.prompt,
3993
+ ...frames,
3994
+ aspectRatio: opts.aspectRatio,
3995
+ resolution: opts.resolution
3996
+ },
3997
+ dest,
3998
+ logger
3999
+ );
3906
4000
  logger.success(`Saved video: ${dest}`);
3907
4001
  if (opts.output) {
3908
- fs23.mkdirSync(path25.dirname(path25.resolve(opts.output)), { recursive: true });
3909
- fs23.copyFileSync(dest, opts.output);
4002
+ fs24.mkdirSync(path25.dirname(path25.resolve(opts.output)), { recursive: true });
4003
+ fs24.copyFileSync(dest, opts.output);
3910
4004
  logger.success(`Copied to: ${opts.output}`);
3911
4005
  }
3912
4006
  await maybeDownloadThumb(finalOp.response, dest, {
@@ -3919,13 +4013,10 @@ Generated video: ${dest}`);
3919
4013
  }
3920
4014
  );
3921
4015
  }
3922
- function sleep3(ms) {
3923
- return new Promise((r) => setTimeout(r, ms));
3924
- }
3925
4016
 
3926
4017
  // src/providers/gemini/commands/generate.ts
3927
4018
  init_errors();
3928
- import fs24 from "fs";
4019
+ import fs25 from "fs";
3929
4020
  import path26 from "path";
3930
4021
  function registerGenerateCommand(parent) {
3931
4022
  parent.command("generate").description("Generate images with Gemini (Nano Banana / Imagen 4)").requiredOption("--prompt <text>", "Image generation prompt").option("--model <id>", "Gemini image model to use").option("--aspect-ratio <ratio>", `Aspect ratio (${ASPECT_RATIOS.join("|")})`, "1:1").option("--num-images <n>", "Number of images to generate (1-4)", "1").option("--size <sz>", `Image size (${IMAGE_SIZES.join("|")})`).option("--output <path>", "Copy first generated image to this path").addOption(imageFormatOption()).addOption(noWebpOption()).option("-v, --verbose", "Verbose logging").action(
@@ -3969,7 +4060,7 @@ function registerGenerateCommand(parent) {
3969
4060
  const ext = img.mimeType.includes("png") ? "png" : "jpg";
3970
4061
  const fname = `gemini_generated_${Date.now()}_${i}.${ext}`;
3971
4062
  const dest = path26.join(outDir, fname);
3972
- fs24.writeFileSync(dest, Buffer.from(img.data, "base64"));
4063
+ fs25.writeFileSync(dest, Buffer.from(img.data, "base64"));
3973
4064
  savedFiles.push(dest);
3974
4065
  logger.success(`Saved: ${dest}`);
3975
4066
  }
@@ -3984,8 +4075,8 @@ function registerGenerateCommand(parent) {
3984
4075
  process.exit(1);
3985
4076
  }
3986
4077
  if (opts.output && savedFiles[0]) {
3987
- fs24.mkdirSync(path26.dirname(path26.resolve(opts.output)), { recursive: true });
3988
- fs24.copyFileSync(savedFiles[0], opts.output);
4078
+ fs25.mkdirSync(path26.dirname(path26.resolve(opts.output)), { recursive: true });
4079
+ fs25.copyFileSync(savedFiles[0], opts.output);
3989
4080
  logger.success(`Copied to: ${opts.output}`);
3990
4081
  }
3991
4082
  const finalImages = finalizeGeneratedImages(savedFiles, { ...opts, logger });
@@ -3997,7 +4088,7 @@ Generated ${finalImages.length} image(s):`);
3997
4088
  }
3998
4089
 
3999
4090
  // src/providers/gemini/commands/image-to-image.ts
4000
- import fs25 from "fs";
4091
+ import fs26 from "fs";
4001
4092
  import path27 from "path";
4002
4093
  var SOFT_LIMIT_BYTES = 5 * 1024 * 1024;
4003
4094
  var HARD_LIMIT_BYTES = 7 * 1024 * 1024;
@@ -4060,13 +4151,13 @@ function registerGeminiImageToImageCommand(parent) {
4060
4151
  if (!img) continue;
4061
4152
  const ext = img.mimeType.includes("png") ? "png" : "jpg";
4062
4153
  const dest = path27.join(outDir, `gemini-i2i-${ts}-${i + 1}.${ext}`);
4063
- fs25.writeFileSync(dest, Buffer.from(img.data, "base64"));
4154
+ fs26.writeFileSync(dest, Buffer.from(img.data, "base64"));
4064
4155
  saved.push(dest);
4065
4156
  logger.success(`Saved: ${dest}`);
4066
4157
  }
4067
4158
  if (opts.output && saved[0]) {
4068
- fs25.mkdirSync(path27.dirname(path27.resolve(opts.output)), { recursive: true });
4069
- fs25.copyFileSync(saved[0], opts.output);
4159
+ fs26.mkdirSync(path27.dirname(path27.resolve(opts.output)), { recursive: true });
4160
+ fs26.copyFileSync(saved[0], opts.output);
4070
4161
  logger.success(`Copied to: ${opts.output}`);
4071
4162
  }
4072
4163
  const finalImages = finalizeGeneratedImages(saved, { ...opts, logger });
@@ -4102,23 +4193,9 @@ function sniffImageMime(buf, url) {
4102
4193
  }
4103
4194
 
4104
4195
  // src/providers/gemini/commands/image-to-video.ts
4105
- import fs26 from "fs";
4196
+ import fs27 from "fs";
4106
4197
  import path28 from "path";
4107
4198
  init_errors();
4108
- init_http_client();
4109
- var BASE3 = "https://generativelanguage.googleapis.com";
4110
- function imagePart(imgPath) {
4111
- if (!fs26.existsSync(imgPath)) {
4112
- throw new ValidationError(`Image not found: ${imgPath}`);
4113
- }
4114
- const data = fs26.readFileSync(imgPath).toString("base64");
4115
- const ext = path28.extname(imgPath).toLowerCase();
4116
- const mimeType = ext === ".png" ? "image/png" : "image/jpeg";
4117
- return { inlineData: { mimeType, data } };
4118
- }
4119
- function sleep4(ms) {
4120
- return new Promise((r) => setTimeout(r, ms));
4121
- }
4122
4199
  function registerGeminiImageToVideoCommand(parent) {
4123
4200
  parent.command("image-to-video <imagePath>").alias("i2v").description("Generate video from a starting image with Veo (image-to-video) [EXPERIMENTAL]").requiredOption("--prompt <text>", "Motion/scene prompt").option("--last-frame <path>", "Optional closing frame image (Veo 3.1)").option("--model <id>", "Veo model id").option("--resolution <res>", "720p|1080p", "1080p").option("--aspect-ratio <ratio>", "Aspect ratio", "16:9").option("--output <path>", "Copy generated video to this path").option("--no-thumb", "Skip downloading the thumbnail if the API returns one").option("-v, --verbose", "Verbose logging").action(
4124
4201
  async (imagePath, opts) => {
@@ -4137,60 +4214,27 @@ function registerGeminiImageToVideoCommand(parent) {
4137
4214
  if (!ASPECT_RATIOS.includes(opts.aspectRatio)) {
4138
4215
  throw new ValidationError(`Invalid aspect ratio: ${opts.aspectRatio}`);
4139
4216
  }
4140
- const contentParts = [
4141
- { text: opts.prompt },
4142
- imagePart(imagePath)
4143
- ];
4144
- if (opts.lastFrame) contentParts.push(imagePart(opts.lastFrame));
4145
- const headers = { "x-goog-api-key": apiKey, "Content-Type": "application/json" };
4146
4217
  logger.info(
4147
4218
  "Submitting image-to-video. This may take 30s\u20136 minutes. Veo requires billing."
4148
4219
  );
4149
- let operation;
4150
- try {
4151
- operation = await httpJson({
4152
- url: `${BASE3}/v1beta/models/${model}:generateVideos`,
4153
- method: "POST",
4154
- headers,
4155
- body: {
4156
- contents: [{ parts: contentParts }],
4157
- generationConfig: { aspectRatio: opts.aspectRatio, resolution: opts.resolution }
4158
- }
4159
- });
4160
- } catch (e) {
4161
- throw new ProviderError(
4162
- `Submit failed: ${e instanceof Error ? e.message : String(e)}`,
4163
- "gemini"
4164
- );
4165
- }
4166
- const operationName = operation.name;
4167
- logger.debug(`Operation: ${operationName}`);
4168
- const deadline = Date.now() + 10 * 60 * 1e3;
4169
- let finalOp = operation;
4170
- while (!finalOp.done && Date.now() < deadline) {
4171
- await sleep4(1e4);
4172
- finalOp = await httpJson({ url: `${BASE3}/v1beta/${operationName}`, headers });
4173
- if (!finalOp.done) {
4174
- logger.debug(`Still generating... ${Math.round((deadline - Date.now()) / 1e3)}s left`);
4175
- }
4176
- }
4177
- if (!finalOp.done) throw new ProviderError("Veo timed out after 10 minutes", "gemini");
4178
- if (finalOp.error) {
4179
- throw new ProviderError(`Veo error: ${JSON.stringify(finalOp.error)}`, "gemini");
4180
- }
4181
- const videoUri = finalOp.response?.generatedSamples?.[0]?.video?.uri ?? finalOp.response?.generatedVideos?.[0]?.video?.uri;
4182
- if (!videoUri) {
4183
- throw new ProviderError(
4184
- `Unexpected Veo response: ${JSON.stringify(finalOp.response)}`,
4185
- "gemini"
4186
- );
4187
- }
4188
4220
  const dest = path28.join(getOutputDir(), `veo_i2v_${Date.now()}.mp4`);
4189
- await downloadFile(videoUri, dest);
4221
+ const finalOp = await runVeo(
4222
+ apiKey,
4223
+ model,
4224
+ {
4225
+ prompt: opts.prompt,
4226
+ image: veoImageFromFile(imagePath),
4227
+ lastFrame: opts.lastFrame ? veoImageFromFile(opts.lastFrame) : void 0,
4228
+ aspectRatio: opts.aspectRatio,
4229
+ resolution: opts.resolution
4230
+ },
4231
+ dest,
4232
+ logger
4233
+ );
4190
4234
  logger.success(`Saved video: ${dest}`);
4191
4235
  if (opts.output) {
4192
- fs26.mkdirSync(path28.dirname(path28.resolve(opts.output)), { recursive: true });
4193
- fs26.copyFileSync(dest, opts.output);
4236
+ fs27.mkdirSync(path28.dirname(path28.resolve(opts.output)), { recursive: true });
4237
+ fs27.copyFileSync(dest, opts.output);
4194
4238
  logger.success(`Copied to: ${opts.output}`);
4195
4239
  }
4196
4240
  await maybeDownloadThumb(finalOp.response, dest, {
@@ -4206,14 +4250,14 @@ Generated video: ${dest}`);
4206
4250
 
4207
4251
  // src/providers/gemini/commands/transcribe.ts
4208
4252
  init_errors();
4209
- import fs27 from "fs";
4253
+ import fs28 from "fs";
4210
4254
  function registerTranscribeCommand2(parent) {
4211
4255
  parent.command("transcribe").description("Transcribe audio or video files with Gemini").requiredOption("--files <paths...>", "Audio/video file paths to transcribe").option("--prompt <text>", "Transcription prompt", "Generate a transcript with timestamps").option("--model <id>", "Gemini model to use").option("--format <fmt>", "Output format: text|json|csv|markdown", "text").option("--output <path>", "Write results to file").option("-v, --verbose", "Verbose logging").action(
4212
4256
  async (opts) => {
4213
4257
  const logger = createLogger({ verbose: opts.verbose ?? false });
4214
4258
  const model = opts.model ?? getDefaultModel("transcribe");
4215
4259
  for (const f of opts.files) {
4216
- if (!fs27.existsSync(f)) throw new ValidationError(`File not found: ${f}`);
4260
+ if (!fs28.existsSync(f)) throw new ValidationError(`File not found: ${f}`);
4217
4261
  }
4218
4262
  const results = [];
4219
4263
  for (let i = 0; i < opts.files.length; i++) {
@@ -4270,13 +4314,11 @@ function registerGeminiCommands(program2) {
4270
4314
  }
4271
4315
 
4272
4316
  // src/providers/leonardo/commands/generate.ts
4273
- init_http_client();
4274
- import fs28 from "fs";
4317
+ import fs29 from "fs";
4275
4318
  import path29 from "path";
4276
4319
 
4277
4320
  // src/providers/leonardo/client.ts
4278
4321
  init_errors();
4279
- init_http_client();
4280
4322
 
4281
4323
  // src/providers/leonardo/models.ts
4282
4324
  var DEFAULT_LEONARDO_BASE_URL = "https://cloud.leonardo.ai/api/rest/v1";
@@ -4483,8 +4525,8 @@ function registerLeonardoGenerateCommand(parent) {
4483
4525
  }
4484
4526
  if (opts.output && saved[0]) {
4485
4527
  const dest = path29.resolve(opts.output);
4486
- fs28.mkdirSync(path29.dirname(dest), { recursive: true });
4487
- fs28.copyFileSync(saved[0], dest);
4528
+ fs29.mkdirSync(path29.dirname(dest), { recursive: true });
4529
+ fs29.copyFileSync(saved[0], dest);
4488
4530
  logger.success(`Copied to ${dest}`);
4489
4531
  }
4490
4532
  const finalImages = finalizeGeneratedImages(saved, { ...opts, logger });
@@ -4496,8 +4538,7 @@ Generated ${finalImages.length} image(s):`);
4496
4538
  }
4497
4539
 
4498
4540
  // src/providers/leonardo/commands/image-to-image.ts
4499
- init_http_client();
4500
- import fs29 from "fs";
4541
+ import fs30 from "fs";
4501
4542
  import path30 from "path";
4502
4543
  function extFromUrl3(url, fallback = ".png") {
4503
4544
  try {
@@ -4574,8 +4615,8 @@ function registerLeonardoImageToImageCommand(parent) {
4574
4615
  }
4575
4616
  if (opts.output && saved[0]) {
4576
4617
  const dest = path30.resolve(opts.output);
4577
- fs29.mkdirSync(path30.dirname(dest), { recursive: true });
4578
- fs29.copyFileSync(saved[0], dest);
4618
+ fs30.mkdirSync(path30.dirname(dest), { recursive: true });
4619
+ fs30.copyFileSync(saved[0], dest);
4579
4620
  logger.success(`Copied to ${dest}`);
4580
4621
  }
4581
4622
  const finalImages = finalizeGeneratedImages(saved, { ...opts, logger });
@@ -4587,7 +4628,6 @@ Generated ${finalImages.length} image(s):`);
4587
4628
  }
4588
4629
 
4589
4630
  // src/providers/leonardo/commands/video.ts
4590
- init_http_client();
4591
4631
  import path31 from "path";
4592
4632
  function v2DimsFor(resolution) {
4593
4633
  if (resolution === "RESOLUTION_1080") return { width: 1920, height: 1080 };
@@ -4805,7 +4845,6 @@ function registerLeonardoModelsCommand(parent) {
4805
4845
  }
4806
4846
 
4807
4847
  // src/providers/leonardo/commands/status.ts
4808
- init_http_client();
4809
4848
  import path32 from "path";
4810
4849
  var VIDEO_KEYS = ["motionMP4URL", "url"];
4811
4850
  var IMAGE_EXT_RE2 = /\.(jpe?g|png|webp|gif)(\?|$)/i;
@@ -4963,7 +5002,6 @@ init_errors();
4963
5002
 
4964
5003
  // src/providers/minimax/client.ts
4965
5004
  init_errors();
4966
- init_http_client();
4967
5005
  var MINIMAX_BASE_URL = "https://api.minimax.io/v1";
4968
5006
  function requireMinimaxKey() {
4969
5007
  const key = resolveKey("MINIMAX_API_KEY");
@@ -5018,7 +5056,7 @@ async function pollAsyncTask(taskId, taskType, apiKey, opts = {}) {
5018
5056
  if (status === "Failed" || status === "Error") {
5019
5057
  throw new ProviderError(`Task ${taskId} failed: ${JSON.stringify(result)}`, "minimax");
5020
5058
  }
5021
- await sleep5(intervalMs);
5059
+ await sleep3(intervalMs);
5022
5060
  elapsed += intervalMs;
5023
5061
  }
5024
5062
  throw new ProviderError(`Task ${taskId} timed out after ${timeoutMs / 1e3}s`, "minimax");
@@ -5037,13 +5075,12 @@ async function downloadMinimaxFile(fileId, apiKey, dest, logger) {
5037
5075
  await downloadFile(downloadUrl, dest);
5038
5076
  return dest;
5039
5077
  }
5040
- function sleep5(ms) {
5078
+ function sleep3(ms) {
5041
5079
  return new Promise((r) => setTimeout(r, ms));
5042
5080
  }
5043
5081
 
5044
5082
  // src/providers/minimax/generators/music.ts
5045
- init_http_client();
5046
- import fs30 from "fs";
5083
+ import fs31 from "fs";
5047
5084
  import path33 from "path";
5048
5085
  async function generateMinimaxMusic(opts) {
5049
5086
  const {
@@ -5093,10 +5130,10 @@ async function generateMinimaxMusic(opts) {
5093
5130
  try {
5094
5131
  if (audioData.startsWith("http")) {
5095
5132
  const bytes = await fetchBytes(audioData);
5096
- fs30.writeFileSync(dest, bytes);
5133
+ fs31.writeFileSync(dest, bytes);
5097
5134
  } else {
5098
5135
  const audioBytes = Buffer.from(audioData, "hex");
5099
- fs30.writeFileSync(dest, audioBytes);
5136
+ fs31.writeFileSync(dest, audioBytes);
5100
5137
  }
5101
5138
  } catch (e) {
5102
5139
  return {
@@ -5107,8 +5144,8 @@ async function generateMinimaxMusic(opts) {
5107
5144
  const durSec = durationMs / 1e3;
5108
5145
  logger?.success(`Saved: ${dest} (${durSec.toFixed(1)}s)`);
5109
5146
  if (output) {
5110
- fs30.mkdirSync(path33.dirname(path33.resolve(output)), { recursive: true });
5111
- fs30.copyFileSync(dest, output);
5147
+ fs31.mkdirSync(path33.dirname(path33.resolve(output)), { recursive: true });
5148
+ fs31.copyFileSync(dest, output);
5112
5149
  }
5113
5150
  return { status: "success", generatedAudio: dest, durationMs, model };
5114
5151
  }
@@ -5182,7 +5219,7 @@ Generated music: ${result.generatedAudio}`);
5182
5219
  init_errors();
5183
5220
 
5184
5221
  // src/providers/minimax/generators/speech.ts
5185
- import fs31 from "fs";
5222
+ import fs32 from "fs";
5186
5223
  import path34 from "path";
5187
5224
  async function generateMinimaxSpeech(opts) {
5188
5225
  const {
@@ -5231,11 +5268,11 @@ async function generateMinimaxSpeech(opts) {
5231
5268
  const ext = ["mp3", "wav", "flac"].includes(outputFormat) ? outputFormat : "mp3";
5232
5269
  const outDir = getOutputDir();
5233
5270
  const dest = path34.join(outDir, `minimax_speech_${Date.now()}.${ext}`);
5234
- fs31.writeFileSync(dest, audioBytes);
5271
+ fs32.writeFileSync(dest, audioBytes);
5235
5272
  logger?.success(`Saved: ${dest} (${(audioBytes.length / 1024).toFixed(1)} KB)`);
5236
5273
  if (output) {
5237
- fs31.mkdirSync(path34.dirname(path34.resolve(output)), { recursive: true });
5238
- fs31.copyFileSync(dest, output);
5274
+ fs32.mkdirSync(path34.dirname(path34.resolve(output)), { recursive: true });
5275
+ fs32.copyFileSync(dest, output);
5239
5276
  }
5240
5277
  return { status: "success", generatedAudio: dest, model };
5241
5278
  }
@@ -5282,7 +5319,7 @@ Generated audio: ${result.generatedAudio}`);
5282
5319
  init_errors();
5283
5320
 
5284
5321
  // src/providers/minimax/generators/video.ts
5285
- import fs32 from "fs";
5322
+ import fs33 from "fs";
5286
5323
  import path35 from "path";
5287
5324
  async function generateMinimaxVideo(opts) {
5288
5325
  const {
@@ -5336,11 +5373,11 @@ async function generateMinimaxVideo(opts) {
5336
5373
  return { status: "error", error: e instanceof Error ? e.message : String(e) };
5337
5374
  }
5338
5375
  const elapsed = (Date.now() - start) / 1e3;
5339
- const stat = fs32.statSync(dest);
5376
+ const stat = fs33.statSync(dest);
5340
5377
  const fileSizeMb = stat.size / (1024 * 1024);
5341
5378
  if (output) {
5342
- fs32.mkdirSync(path35.dirname(path35.resolve(output)), { recursive: true });
5343
- fs32.copyFileSync(dest, output);
5379
+ fs33.mkdirSync(path35.dirname(path35.resolve(output)), { recursive: true });
5380
+ fs33.copyFileSync(dest, output);
5344
5381
  }
5345
5382
  await maybeDownloadThumb(pollResult, dest, {
5346
5383
  skip: thumb === false,
@@ -5402,8 +5439,7 @@ Generated video: ${result.generatedVideo}`);
5402
5439
 
5403
5440
  // src/providers/minimax/generators/image.ts
5404
5441
  init_errors();
5405
- init_http_client();
5406
- import fs33 from "fs";
5442
+ import fs34 from "fs";
5407
5443
  import path36 from "path";
5408
5444
  async function generateMinimaxImage(opts) {
5409
5445
  const {
@@ -5443,7 +5479,7 @@ async function generateMinimaxImage(opts) {
5443
5479
  const bytes = await fetchBytes(url);
5444
5480
  const fname = `minimax_image_${Date.now()}_${i}.png`;
5445
5481
  const dest = path36.join(outDir, fname);
5446
- fs33.writeFileSync(dest, bytes);
5482
+ fs34.writeFileSync(dest, bytes);
5447
5483
  savedFiles.push(dest);
5448
5484
  logger?.success(`Saved: ${dest}`);
5449
5485
  } catch (e) {
@@ -5454,8 +5490,8 @@ async function generateMinimaxImage(opts) {
5454
5490
  }
5455
5491
  }
5456
5492
  if (output && savedFiles[0]) {
5457
- fs33.mkdirSync(path36.dirname(path36.resolve(output)), { recursive: true });
5458
- fs33.copyFileSync(savedFiles[0], output);
5493
+ fs34.mkdirSync(path36.dirname(path36.resolve(output)), { recursive: true });
5494
+ fs34.copyFileSync(savedFiles[0], output);
5459
5495
  }
5460
5496
  return { status: "success", generatedImages: savedFiles, model };
5461
5497
  }
@@ -5494,8 +5530,7 @@ Generated ${finalImages.length} image(s):`);
5494
5530
 
5495
5531
  // src/providers/minimax/commands/image-to-image.ts
5496
5532
  init_errors();
5497
- init_http_client();
5498
- import fs34 from "fs";
5533
+ import fs35 from "fs";
5499
5534
  import path37 from "path";
5500
5535
  function registerMinimaxImageToImageCommand(parent) {
5501
5536
  parent.command("image-to-image").alias("i2i").description(
@@ -5552,7 +5587,7 @@ function registerMinimaxImageToImageCommand(parent) {
5552
5587
  try {
5553
5588
  const bytes = await fetchBytes(url);
5554
5589
  const dest = path37.join(outDir, `minimax-i2i-${ts}-${i + 1}.png`);
5555
- fs34.writeFileSync(dest, bytes);
5590
+ fs35.writeFileSync(dest, bytes);
5556
5591
  saved.push(dest);
5557
5592
  logger.success(`Saved: ${dest}`);
5558
5593
  } catch (e) {
@@ -5563,8 +5598,8 @@ function registerMinimaxImageToImageCommand(parent) {
5563
5598
  }
5564
5599
  }
5565
5600
  if (opts.output && saved[0]) {
5566
- fs34.mkdirSync(path37.dirname(path37.resolve(opts.output)), { recursive: true });
5567
- fs34.copyFileSync(saved[0], opts.output);
5601
+ fs35.mkdirSync(path37.dirname(path37.resolve(opts.output)), { recursive: true });
5602
+ fs35.copyFileSync(saved[0], opts.output);
5568
5603
  logger.success(`Copied to: ${opts.output}`);
5569
5604
  }
5570
5605
  const finalImages = finalizeGeneratedImages(saved, { ...opts, logger });
@@ -5698,15 +5733,13 @@ function validateKnownSpeakerPairing(names = [], refs = []) {
5698
5733
  }
5699
5734
 
5700
5735
  // src/providers/openai/openai-audio.ts
5701
- init_http_client();
5702
- import fs36 from "fs";
5736
+ import fs37 from "fs";
5703
5737
  import path39 from "path";
5704
5738
 
5705
5739
  // src/providers/openai/client.ts
5706
- import fs35 from "fs";
5740
+ import fs36 from "fs";
5707
5741
  import path38 from "path";
5708
5742
  init_errors();
5709
- init_http_client();
5710
5743
  var DEFAULT_TIMEOUT_MS3 = 24e4;
5711
5744
  function requireOpenAIKey() {
5712
5745
  const key = resolveKey("OPENAI_API_KEY");
@@ -5772,8 +5805,8 @@ async function postOpenAIBinary(pathname, body, apiKey, timeoutMs = DEFAULT_TIME
5772
5805
  }
5773
5806
  function fileFromPath(filePath) {
5774
5807
  const abs = path38.resolve(filePath);
5775
- if (!fs35.existsSync(abs)) throw new ValidationError(`File not found: ${abs}`);
5776
- const bytes = fs35.readFileSync(abs);
5808
+ if (!fs36.existsSync(abs)) throw new ValidationError(`File not found: ${abs}`);
5809
+ const bytes = fs36.readFileSync(abs);
5777
5810
  return new File([new Uint8Array(bytes)], path38.basename(abs));
5778
5811
  }
5779
5812
 
@@ -5833,11 +5866,11 @@ async function generateOpenAISpeech(opts) {
5833
5866
  const bytes = await postOpenAIBinary("/audio/speech", buildSpeechBody(opts), requireOpenAIKey());
5834
5867
  const ext = extFromAudioFormat(opts.outputFormat);
5835
5868
  const dest = path39.join(getOutputDir(), `openai-speech-${Date.now()}.${ext}`);
5836
- fs36.writeFileSync(dest, bytes);
5869
+ fs37.writeFileSync(dest, bytes);
5837
5870
  opts.logger?.success(`Saved: ${dest} (${(bytes.length / 1024).toFixed(1)} KB)`);
5838
5871
  if (opts.output) {
5839
- fs36.mkdirSync(path39.dirname(path39.resolve(opts.output)), { recursive: true });
5840
- fs36.copyFileSync(dest, opts.output);
5872
+ fs37.mkdirSync(path39.dirname(path39.resolve(opts.output)), { recursive: true });
5873
+ fs37.copyFileSync(dest, opts.output);
5841
5874
  }
5842
5875
  return dest;
5843
5876
  }
@@ -5860,8 +5893,8 @@ async function transcribeOpenAIAudio(opts) {
5860
5893
  const text = typeof data.text === "string" ? data.text : void 0;
5861
5894
  const ext = opts.format === "text" ? "txt" : "json";
5862
5895
  const dest = opts.output ? path39.resolve(opts.output) : path39.join(getOutputDir(), `openai-transcribe-${Date.now()}.${ext}`);
5863
- fs36.mkdirSync(path39.dirname(dest), { recursive: true });
5864
- fs36.writeFileSync(dest, opts.format === "text" ? text ?? "" : JSON.stringify(data, null, 2));
5896
+ fs37.mkdirSync(path39.dirname(dest), { recursive: true });
5897
+ fs37.writeFileSync(dest, opts.format === "text" ? text ?? "" : JSON.stringify(data, null, 2));
5865
5898
  opts.logger?.success(`Saved transcript: ${dest}`);
5866
5899
  return { path: dest, text };
5867
5900
  }
@@ -5888,7 +5921,7 @@ Generated audio: ${dest}`);
5888
5921
  }
5889
5922
 
5890
5923
  // src/providers/openai/codex-image-driver.ts
5891
- import fs37 from "fs";
5924
+ import fs38 from "fs";
5892
5925
  import os2 from "os";
5893
5926
  import path40 from "path";
5894
5927
  import { execa } from "execa";
@@ -5940,11 +5973,11 @@ async function runCodexImageGeneration(opts) {
5940
5973
  const runner = opts.runner ?? defaultRunner;
5941
5974
  const ext = extFromImageFormat(opts.outputFormat);
5942
5975
  const dest = opts.output ? path40.resolve(opts.output) : path40.join(getOutputDir(), `codex-image-${Date.now()}.${ext}`);
5943
- fs37.mkdirSync(path40.dirname(dest), { recursive: true });
5944
- const tmp = fs37.mkdtempSync(path40.join(os2.tmpdir(), "multix-codex-image-"));
5976
+ fs38.mkdirSync(path40.dirname(dest), { recursive: true });
5977
+ const tmp = fs38.mkdtempSync(path40.join(os2.tmpdir(), "multix-codex-image-"));
5945
5978
  const schema = path40.join(tmp, "schema.json");
5946
5979
  const result = path40.join(tmp, "result.json");
5947
- fs37.writeFileSync(
5980
+ fs38.writeFileSync(
5948
5981
  schema,
5949
5982
  JSON.stringify({
5950
5983
  type: "object",
@@ -5974,7 +6007,7 @@ async function runCodexImageGeneration(opts) {
5974
6007
  if (res.exitCode !== 0) {
5975
6008
  throw new ProviderError(`Codex image driver failed: ${res.stderr || res.stdout}`, "openai");
5976
6009
  }
5977
- if (!fs37.existsSync(dest) || fs37.statSync(dest).size === 0) {
6010
+ if (!fs38.existsSync(dest) || fs38.statSync(dest).size === 0) {
5978
6011
  throw new ProviderError(`Codex did not create a non-empty image at ${dest}`, "openai");
5979
6012
  }
5980
6013
  return { path: dest };
@@ -5991,8 +6024,7 @@ function buildCodexPrompt(userPrompt, outputPath) {
5991
6024
 
5992
6025
  // src/providers/openai/openai-image.ts
5993
6026
  init_errors();
5994
- init_http_client();
5995
- import fs38 from "fs";
6027
+ import fs39 from "fs";
5996
6028
  import path41 from "path";
5997
6029
  function buildImageGenerationBody(opts) {
5998
6030
  return {
@@ -6027,13 +6059,13 @@ function saveOpenAIImages(opts) {
6027
6059
  const b64 = items[i]?.b64_json;
6028
6060
  if (!b64) throw new ProviderError("No b64_json in OpenAI image response", "openai");
6029
6061
  const dest = path41.join(outDir, `openai-${opts.task}-${ts}-${i + 1}.${ext}`);
6030
- fs38.writeFileSync(dest, Buffer.from(b64, "base64"));
6062
+ fs39.writeFileSync(dest, Buffer.from(b64, "base64"));
6031
6063
  saved.push(dest);
6032
6064
  opts.logger?.success(`Saved: ${dest}`);
6033
6065
  }
6034
6066
  if (opts.output && saved[0]) {
6035
- fs38.mkdirSync(path41.dirname(path41.resolve(opts.output)), { recursive: true });
6036
- fs38.copyFileSync(saved[0], opts.output);
6067
+ fs39.mkdirSync(path41.dirname(path41.resolve(opts.output)), { recursive: true });
6068
+ fs39.copyFileSync(saved[0], opts.output);
6037
6069
  opts.logger?.success(`Copied to: ${opts.output}`);
6038
6070
  }
6039
6071
  return saved;
@@ -6231,10 +6263,9 @@ function registerOpenAICommands(program2) {
6231
6263
  }
6232
6264
 
6233
6265
  // src/providers/openrouter/client.ts
6234
- import fs39 from "fs";
6266
+ import fs40 from "fs";
6235
6267
  import path42 from "path";
6236
6268
  init_errors();
6237
- init_http_client();
6238
6269
 
6239
6270
  // src/providers/openrouter/payload.ts
6240
6271
  var IMAGE_ONLY_MODEL_PREFIXES = [
@@ -6247,15 +6278,15 @@ function resolveModalities(model) {
6247
6278
  return IMAGE_ONLY_MODEL_PREFIXES.some((p) => lower.startsWith(p)) ? ["image"] : ["image", "text"];
6248
6279
  }
6249
6280
  function buildOpenRouterHeaders(apiKey) {
6250
- const headers = {
6281
+ const headers2 = {
6251
6282
  Authorization: `Bearer ${apiKey}`,
6252
6283
  "Content-Type": "application/json"
6253
6284
  };
6254
6285
  const referer = resolveKey("OPENROUTER_SITE_URL");
6255
6286
  const title = resolveKey("OPENROUTER_APP_NAME") ?? "multix";
6256
- if (referer) headers["HTTP-Referer"] = referer;
6257
- if (title) headers["X-Title"] = title;
6258
- return headers;
6287
+ if (referer) headers2["HTTP-Referer"] = referer;
6288
+ if (title) headers2["X-Title"] = title;
6289
+ return headers2;
6259
6290
  }
6260
6291
  function extractImagesFromResponse(data) {
6261
6292
  const out = { urls: [] };
@@ -6398,7 +6429,7 @@ async function generateOpenRouterImage(opts) {
6398
6429
  try {
6399
6430
  const bytes = await fetchBytes(url);
6400
6431
  const dest = path42.join(outDir, `openrouter_image_${Date.now()}_${i}.png`);
6401
- fs39.writeFileSync(dest, bytes);
6432
+ fs40.writeFileSync(dest, bytes);
6402
6433
  savedFiles.push(dest);
6403
6434
  logger?.success(`Saved: ${dest}`);
6404
6435
  } catch (e) {
@@ -6409,8 +6440,8 @@ async function generateOpenRouterImage(opts) {
6409
6440
  }
6410
6441
  }
6411
6442
  if (output && savedFiles[0]) {
6412
- fs39.mkdirSync(path42.dirname(path42.resolve(output)), { recursive: true });
6413
- fs39.writeFileSync(output, fs39.readFileSync(savedFiles[0]));
6443
+ fs40.mkdirSync(path42.dirname(path42.resolve(output)), { recursive: true });
6444
+ fs40.writeFileSync(output, fs40.readFileSync(savedFiles[0]));
6414
6445
  }
6415
6446
  return { status: "success", generatedImages: savedFiles, model: usedModel };
6416
6447
  }
@@ -6451,10 +6482,9 @@ Generated ${finalImages.length} image(s):`);
6451
6482
  }
6452
6483
 
6453
6484
  // src/providers/openrouter/commands/image-to-image.ts
6454
- import fs40 from "fs";
6485
+ import fs41 from "fs";
6455
6486
  import path43 from "path";
6456
6487
  init_errors();
6457
- init_http_client();
6458
6488
  var DEFAULT_MODEL = "google/gemini-2.5-flash-image";
6459
6489
  var SOFT_LIMIT_BYTES2 = 5 * 1024 * 1024;
6460
6490
  var HARD_LIMIT_BYTES2 = 8 * 1024 * 1024;
@@ -6519,7 +6549,7 @@ function parseStrength(raw) {
6519
6549
  }
6520
6550
  async function runImageToImage(opts) {
6521
6551
  const apiKey = requireOpenRouterKey();
6522
- const headers = buildOpenRouterHeaders(apiKey);
6552
+ const headers2 = buildOpenRouterHeaders(apiKey);
6523
6553
  const payload = buildI2IPayload({
6524
6554
  prompt: opts.prompt,
6525
6555
  model: opts.model,
@@ -6530,7 +6560,7 @@ async function runImageToImage(opts) {
6530
6560
  const data = await httpJson({
6531
6561
  url: OPENROUTER_API_URL,
6532
6562
  method: "POST",
6533
- headers,
6563
+ headers: headers2,
6534
6564
  body: payload,
6535
6565
  timeoutMs: 24e4
6536
6566
  });
@@ -6546,31 +6576,30 @@ async function runImageToImage(opts) {
6546
6576
  if (!url) continue;
6547
6577
  const bytes = await fetchBytes(url);
6548
6578
  const dest = path43.join(outDir, `openrouter-i2i-${ts}-${i + 1}.png`);
6549
- fs40.writeFileSync(dest, bytes);
6579
+ fs41.writeFileSync(dest, bytes);
6550
6580
  saved.push(dest);
6551
6581
  opts.logger.success(`Saved: ${dest}`);
6552
6582
  }
6553
6583
  if (opts.output && saved[0]) {
6554
- fs40.mkdirSync(path43.dirname(path43.resolve(opts.output)), { recursive: true });
6555
- fs40.copyFileSync(saved[0], opts.output);
6584
+ fs41.mkdirSync(path43.dirname(path43.resolve(opts.output)), { recursive: true });
6585
+ fs41.copyFileSync(saved[0], opts.output);
6556
6586
  opts.logger.success(`Copied to: ${opts.output}`);
6557
6587
  }
6558
6588
  return saved;
6559
6589
  }
6560
6590
 
6561
6591
  // src/providers/openrouter/video-client.ts
6562
- init_http_client();
6563
6592
  var OPENROUTER_VIDEOS_URL = "https://openrouter.ai/api/v1/videos";
6564
6593
  function authHeaders5(apiKey) {
6565
- const headers = {
6594
+ const headers2 = {
6566
6595
  Authorization: `Bearer ${apiKey}`,
6567
6596
  "Content-Type": "application/json"
6568
6597
  };
6569
6598
  const referer = resolveKey("OPENROUTER_SITE_URL");
6570
6599
  const title = resolveKey("OPENROUTER_APP_NAME") ?? "multix";
6571
- if (referer) headers["HTTP-Referer"] = referer;
6572
- if (title) headers["X-Title"] = title;
6573
- return headers;
6600
+ if (referer) headers2["HTTP-Referer"] = referer;
6601
+ if (title) headers2["X-Title"] = title;
6602
+ return headers2;
6574
6603
  }
6575
6604
  async function submitVideoJob(body, apiKey = requireOpenRouterKey()) {
6576
6605
  return httpJson({
@@ -6598,7 +6627,7 @@ async function listVideoModels(apiKey = requireOpenRouterKey()) {
6598
6627
  }
6599
6628
 
6600
6629
  // src/providers/openrouter/commands/video-status.ts
6601
- import fs41 from "fs";
6630
+ import fs42 from "fs";
6602
6631
  import path44 from "path";
6603
6632
  async function downloadOpenRouterVideo(opts) {
6604
6633
  const { jobId, logger, source } = opts;
@@ -6614,14 +6643,14 @@ async function downloadOpenRouterVideo(opts) {
6614
6643
  logger.error("Empty response body");
6615
6644
  process.exit(1);
6616
6645
  }
6617
- fs41.mkdirSync(path44.dirname(dest), { recursive: true });
6618
- const ws = fs41.createWriteStream(dest);
6646
+ fs42.mkdirSync(path44.dirname(dest), { recursive: true });
6647
+ const ws = fs42.createWriteStream(dest);
6619
6648
  const { pipeline: pipeline2 } = await import("stream/promises");
6620
6649
  await pipeline2(r.body, ws);
6621
6650
  logger.success(`Saved ${dest}`);
6622
6651
  if (opts.output) {
6623
- fs41.mkdirSync(path44.dirname(path44.resolve(opts.output)), { recursive: true });
6624
- fs41.copyFileSync(dest, opts.output);
6652
+ fs42.mkdirSync(path44.dirname(path44.resolve(opts.output)), { recursive: true });
6653
+ fs42.copyFileSync(dest, opts.output);
6625
6654
  logger.success(`Copied to ${opts.output}`);
6626
6655
  }
6627
6656
  await maybeDownloadThumb(source, dest, {
@@ -6807,7 +6836,6 @@ async function checkBinary(binary, versionFlag = "--version") {
6807
6836
 
6808
6837
  // src/commands/check-helpers/gemini-ping.ts
6809
6838
  init_errors();
6810
- init_http_client();
6811
6839
  var MODELS_URL = "https://generativelanguage.googleapis.com/v1beta/models";
6812
6840
  async function pingGemini(apiKey) {
6813
6841
  try {
@@ -7040,7 +7068,7 @@ function registerCheckCommand(program2) {
7040
7068
 
7041
7069
  // src/commands/doc/convert.ts
7042
7070
  init_errors();
7043
- import fs42 from "fs";
7071
+ import fs43 from "fs";
7044
7072
  import path45 from "path";
7045
7073
 
7046
7074
  // src/commands/doc/default-prompt.ts
@@ -7062,7 +7090,7 @@ async function convertDocuments(opts) {
7062
7090
  const model = opts.model ?? DOC_MODEL_DEFAULT;
7063
7091
  const prompt = opts.prompt ?? DEFAULT_CONVERT_PROMPT;
7064
7092
  for (const f of inputs) {
7065
- if (!fs42.existsSync(f)) throw new ValidationError(`Input file not found: ${f}`);
7093
+ if (!fs43.existsSync(f)) throw new ValidationError(`Input file not found: ${f}`);
7066
7094
  }
7067
7095
  let outputPath;
7068
7096
  if (output) {
@@ -7073,7 +7101,7 @@ async function convertDocuments(opts) {
7073
7101
  } else {
7074
7102
  outputPath = path45.join(getOutputDir(), "document-extraction.md");
7075
7103
  }
7076
- fs42.mkdirSync(path45.dirname(outputPath), { recursive: true });
7104
+ fs43.mkdirSync(path45.dirname(outputPath), { recursive: true });
7077
7105
  const results = [];
7078
7106
  for (let i = 0; i < inputs.length; i++) {
7079
7107
  const filePath = inputs[i];
@@ -7105,7 +7133,7 @@ async function convertDocuments(opts) {
7105
7133
  if (attempt < 2) {
7106
7134
  const wait = 2 ** attempt * 1e3;
7107
7135
  logger?.debug(`Retry ${attempt + 1} after ${wait / 1e3}s: ${errorMsg}`);
7108
- await sleep6(wait);
7136
+ await sleep4(wait);
7109
7137
  }
7110
7138
  }
7111
7139
  }
@@ -7137,7 +7165,7 @@ async function convertDocuments(opts) {
7137
7165
  }
7138
7166
  lines.push("\n---\n");
7139
7167
  }
7140
- fs42.writeFileSync(outputPath, lines.join("\n"), "utf8");
7168
+ fs43.writeFileSync(outputPath, lines.join("\n"), "utf8");
7141
7169
  const succeeded = results.filter((r) => r.status === "success").length;
7142
7170
  console.log(`
7143
7171
  ${"=".repeat(50)}`);
@@ -7153,7 +7181,7 @@ ${"=".repeat(50)}`);
7153
7181
  const failed = results.filter((r) => r.status === "error").length;
7154
7182
  if (failed > 0) process.exit(1);
7155
7183
  }
7156
- function sleep6(ms) {
7184
+ function sleep4(ms) {
7157
7185
  return new Promise((r) => setTimeout(r, ms));
7158
7186
  }
7159
7187
 
@@ -7185,11 +7213,11 @@ function registerDocCommands(program2) {
7185
7213
 
7186
7214
  // src/commands/media/index.ts
7187
7215
  init_errors();
7188
- import fs45 from "fs";
7216
+ import fs46 from "fs";
7189
7217
  import path48 from "path";
7190
7218
 
7191
7219
  // src/commands/media/batch.ts
7192
- import fs43 from "fs";
7220
+ import fs44 from "fs";
7193
7221
  import path46 from "path";
7194
7222
 
7195
7223
  // src/commands/media/detect-type.ts
@@ -7386,8 +7414,8 @@ async function batchOptimize(opts) {
7386
7414
  verbose,
7387
7415
  logger
7388
7416
  } = opts;
7389
- fs43.mkdirSync(outputDir, { recursive: true });
7390
- const entries = fs43.readdirSync(inputDir);
7417
+ fs44.mkdirSync(outputDir, { recursive: true });
7418
+ const entries = fs44.readdirSync(inputDir);
7391
7419
  const mediaFiles = entries.filter((f) => {
7392
7420
  const ext = path46.extname(f).toLowerCase();
7393
7421
  return ALL_EXTS.has(ext);
@@ -7439,7 +7467,7 @@ async function batchOptimize(opts) {
7439
7467
 
7440
7468
  // src/commands/media/split-video.ts
7441
7469
  init_errors();
7442
- import fs44 from "fs";
7470
+ import fs45 from "fs";
7443
7471
  import path47 from "path";
7444
7472
  async function splitVideo(opts) {
7445
7473
  await assertFfmpeg();
@@ -7451,7 +7479,7 @@ async function splitVideo(opts) {
7451
7479
  logger?.info("Video is short enough \u2014 no splitting needed");
7452
7480
  return [input];
7453
7481
  }
7454
- fs44.mkdirSync(outputDir, { recursive: true });
7482
+ fs45.mkdirSync(outputDir, { recursive: true });
7455
7483
  const ext = path47.extname(input) || ".mp4";
7456
7484
  const baseName = path47.basename(input, ext);
7457
7485
  const outputPattern = path47.join(outputDir, `${baseName}_chunk_%03d${ext}`);
@@ -7474,7 +7502,7 @@ async function splitVideo(opts) {
7474
7502
  const files = [];
7475
7503
  for (let i = 0; i < numChunks + 1; i++) {
7476
7504
  const fname = path47.join(outputDir, `${baseName}_chunk_${String(i).padStart(3, "0")}${ext}`);
7477
- if (fs44.existsSync(fname)) files.push(fname);
7505
+ if (fs45.existsSync(fname)) files.push(fname);
7478
7506
  }
7479
7507
  logger?.success(`Created ${files.length} chunks in ${outputDir}`);
7480
7508
  return files;
@@ -7488,8 +7516,8 @@ function registerMediaCommands(program2) {
7488
7516
  media.command("optimize").description("Optimize a single media file (video, audio, or image)").requiredOption("--input <file>", "Input media file").requiredOption("--output <file>", "Output file path").option("--target-size <MB>", "Target file size in MB").option("--quality <n>", "Quality: video CRF (0-51) or image (1-100)", "85").option("--max-width <px>", "Max image/video width in pixels", "1920").option("--bitrate <rate>", "Audio bitrate (e.g. 64k)", "64k").option("--resolution <WxH>", "Force video resolution (e.g. 1920x1080)").option("-v, --verbose", "Verbose logging").action(
7489
7517
  async (opts) => {
7490
7518
  const logger = createLogger({ verbose: opts.verbose ?? false });
7491
- if (!fs45.existsSync(opts.input)) throw new ValidationError(`Input not found: ${opts.input}`);
7492
- fs45.mkdirSync(path48.dirname(path48.resolve(opts.output)), { recursive: true });
7519
+ if (!fs46.existsSync(opts.input)) throw new ValidationError(`Input not found: ${opts.input}`);
7520
+ fs46.mkdirSync(path48.dirname(path48.resolve(opts.output)), { recursive: true });
7493
7521
  const ext = path48.extname(opts.input).toLowerCase();
7494
7522
  const kind = extToKind(ext);
7495
7523
  if (!kind) throw new ValidationError(`Unsupported file type: ${ext}`);
@@ -7530,7 +7558,7 @@ function registerMediaCommands(program2) {
7530
7558
  media.command("split").description("Split a long video into fixed-duration chunks").requiredOption("--input <file>", "Input video file").option("--output-dir <dir>", "Output directory for chunks", "./chunks").option("--chunk-duration <sec>", "Chunk duration in seconds", "3600").option("-v, --verbose", "Verbose logging").action(
7531
7559
  async (opts) => {
7532
7560
  const logger = createLogger({ verbose: opts.verbose ?? false });
7533
- if (!fs45.existsSync(opts.input)) throw new ValidationError(`Input not found: ${opts.input}`);
7561
+ if (!fs46.existsSync(opts.input)) throw new ValidationError(`Input not found: ${opts.input}`);
7534
7562
  const chunks = await splitVideo({
7535
7563
  input: opts.input,
7536
7564
  outputDir: opts.outputDir,
@@ -7546,7 +7574,7 @@ Created ${chunks.length} chunk(s) in ${opts.outputDir}`);
7546
7574
  media.command("batch").description("Batch optimize all media files in a directory").requiredOption("--input-dir <dir>", "Input directory").requiredOption("--output-dir <dir>", "Output directory").option("--quality <n>", "Quality setting", "85").option("--max-width <px>", "Max image/video width", "1920").option("--bitrate <rate>", "Audio bitrate", "64k").option("-v, --verbose", "Verbose logging").action(
7547
7575
  async (opts) => {
7548
7576
  const logger = createLogger({ verbose: opts.verbose ?? false });
7549
- if (!fs45.existsSync(opts.inputDir))
7577
+ if (!fs46.existsSync(opts.inputDir))
7550
7578
  throw new ValidationError(`Input dir not found: ${opts.inputDir}`);
7551
7579
  const result = await batchOptimize({
7552
7580
  inputDir: opts.inputDir,
@@ -7569,7 +7597,6 @@ Processed: ${result.succeeded}/${result.total} files`);
7569
7597
 
7570
7598
  // src/commands/update.ts
7571
7599
  init_errors();
7572
- init_http_client();
7573
7600
  import { createRequire } from "module";
7574
7601
  import { execa as execa5 } from "execa";
7575
7602
 
@@ -7734,11 +7761,6 @@ function registerCommands(program2) {
7734
7761
  // src/core/index.ts
7735
7762
  init_errors();
7736
7763
  init_redact();
7737
- init_http_client();
7738
- init_fetch_json();
7739
-
7740
- // src/core/async-poll.ts
7741
- init_errors();
7742
7764
 
7743
7765
  // src/cli.ts
7744
7766
  loadEnv();