@mrgoonie/multix 1.0.2 → 1.0.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
@@ -307,6 +307,7 @@ function validateRawInput(spec, body) {
307
307
  }
308
308
 
309
309
  // src/providers/atlascloud/models.ts
310
+ var ATLAS_PROVIDER = "atlascloud";
310
311
  var DEFAULT_ATLAS_BASE_URL = "https://api.atlascloud.ai/api/v1";
311
312
  var ATLAS_ROUTING = {
312
313
  name: "ATLASCLOUD_BASE_URL",
@@ -626,6 +627,134 @@ var defaultLogger = createLogger({ verbose: false });
626
627
  // src/providers/atlascloud/commands/env.ts
627
628
  init_redact();
628
629
 
630
+ // src/core/url-safety.ts
631
+ init_errors();
632
+ init_redact();
633
+ function ipv4Octets(host) {
634
+ const parts = host.split(".");
635
+ if (parts.length !== 4 || !parts.every((p) => /^\d{1,3}$/.test(p))) return void 0;
636
+ const octets = parts.map(Number);
637
+ return octets.every((o) => o <= 255) ? octets : void 0;
638
+ }
639
+ function isNonPublicIpv4([a = 0, b = 0, c2 = 0]) {
640
+ return a === 0 || // "this network" / unspecified
641
+ a === 10 || a === 127 || // loopback
642
+ a === 100 && b >= 64 && b <= 127 || // carrier-grade NAT
643
+ a === 169 && b === 254 || // link-local (cloud metadata)
644
+ a === 172 && b >= 16 && b <= 31 || a === 192 && b === 0 && c2 === 0 || // IETF protocol assignments
645
+ a === 192 && b === 0 && c2 === 2 || // TEST-NET-1
646
+ a === 192 && b === 168 || a === 198 && (b === 18 || b === 19) || // benchmarking
647
+ a === 198 && b === 51 && c2 === 100 || // TEST-NET-2
648
+ a === 203 && b === 0 && c2 === 113 || // TEST-NET-3
649
+ a >= 224;
650
+ }
651
+ function ipv6Groups(host) {
652
+ if (!/^[0-9a-f:.]+$/i.test(host) || !host.includes(":")) return void 0;
653
+ let text = host;
654
+ const tail = /^(.*:)(\d+\.\d+\.\d+\.\d+)$/.exec(text);
655
+ if (tail) {
656
+ const octets = ipv4Octets(tail[2]);
657
+ if (!octets) return void 0;
658
+ const [a = 0, b = 0, c2 = 0, d = 0] = octets;
659
+ text = `${tail[1]}${(a << 8 | b).toString(16)}:${(c2 << 8 | d).toString(16)}`;
660
+ }
661
+ const halves = text.split("::");
662
+ if (halves.length > 2) return void 0;
663
+ const parse = (s) => s === "" ? [] : s.split(":");
664
+ const head = parse(halves[0]);
665
+ const rest = halves.length === 2 ? parse(halves[1]) : [];
666
+ const missing = 8 - head.length - rest.length;
667
+ if (halves.length === 2 ? missing < 1 : missing !== 0) return void 0;
668
+ const all = [...head, ...Array(halves.length === 2 ? missing : 0).fill("0"), ...rest];
669
+ if (!all.every((g) => /^[0-9a-f]{1,4}$/i.test(g))) return void 0;
670
+ return all.map((g) => Number.parseInt(g, 16));
671
+ }
672
+ function embeddedIpv4(hi, lo) {
673
+ return [hi >> 8, hi & 255, lo >> 8, lo & 255];
674
+ }
675
+ function isNonPublicIpv6(groups) {
676
+ const [g0 = 0, g1 = 0, g2 = 0, g3 = 0, g4 = 0, g5 = 0, g6 = 0, g7 = 0] = groups;
677
+ const zeroUpTo = (n) => groups.slice(0, n).every((g) => g === 0);
678
+ if (zeroUpTo(5) && g5 === 65535) return isNonPublicIpv4(embeddedIpv4(g6, g7));
679
+ if (g0 === 100 && g1 === 65435 && g2 === 0 && g3 === 0 && g4 === 0 && g5 === 0) {
680
+ return isNonPublicIpv4(embeddedIpv4(g6, g7));
681
+ }
682
+ if (g0 === 8194) return isNonPublicIpv4(embeddedIpv4(g1, g2));
683
+ return g0 === 0 || // unspecified (::), loopback (::1), IPv4-compatible and other reserved
684
+ g0 === 256 && g1 === 0 && g2 === 0 && g3 === 0 || // discard-only 100::/64
685
+ (g0 & 65024) === 64512 || // unique local fc00::/7
686
+ (g0 & 65472) === 65152 || // link-local fe80::/10
687
+ (g0 & 65472) === 65216 || // deprecated site-local fec0::/10
688
+ (g0 & 65280) === 65280;
689
+ }
690
+ function bareHost(hostname) {
691
+ const host = hostname.toLowerCase().replace(/\.$/, "");
692
+ return host.startsWith("[") && host.endsWith("]") ? host.slice(1, -1) : host;
693
+ }
694
+ function isLoopbackHost(hostname) {
695
+ const host = bareHost(hostname);
696
+ if (host === "localhost" || host.endsWith(".localhost")) return true;
697
+ const octets = ipv4Octets(host);
698
+ if (octets) return octets[0] === 127;
699
+ const groups = ipv6Groups(host);
700
+ if (!groups) return false;
701
+ if (groups.slice(0, 5).every((g) => g === 0) && groups[5] === 65535) {
702
+ return groups[6] !== void 0 && groups[6] >> 8 === 127;
703
+ }
704
+ return groups.slice(0, 7).every((g) => g === 0) && groups[7] === 1;
705
+ }
706
+ function isNonPublicHost(hostname) {
707
+ const host = bareHost(hostname);
708
+ if (host === "localhost" || host.endsWith(".localhost")) return true;
709
+ const octets = ipv4Octets(host);
710
+ if (octets) return isNonPublicIpv4(octets);
711
+ const groups = ipv6Groups(host);
712
+ return groups !== void 0 && isNonPublicIpv6(groups);
713
+ }
714
+ var LOOPBACK_BASE_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "[::1]"]);
715
+ function checkBaseUrl(raw, variable) {
716
+ const value = raw.trim();
717
+ let url;
718
+ try {
719
+ url = new URL(value);
720
+ } catch {
721
+ throw new ConfigError(`${variable} must be an absolute https:// URL`);
722
+ }
723
+ const loopbackHttp = url.protocol === "http:" && LOOPBACK_BASE_HOSTS.has(url.hostname);
724
+ if (url.protocol !== "https:" && !loopbackHttp) {
725
+ throw new ConfigError(
726
+ `${variable} must use https:// (got ${redactUrl(value)}); plain http:// is only allowed for localhost, 127.0.0.1, or [::1]`
727
+ );
728
+ }
729
+ return value;
730
+ }
731
+ function checkUrlSafety(url, opts) {
732
+ const action = opts.action ?? "download from";
733
+ let parsed;
734
+ try {
735
+ parsed = new URL(url);
736
+ } catch {
737
+ throw new ProviderError(`Refusing to ${action} a malformed URL`, opts.provider);
738
+ }
739
+ const host = parsed.hostname.toLowerCase().replace(/\.$/, "");
740
+ const localOk = opts.allowLocalhostHttp === true && isLoopbackHost(host);
741
+ const httpOk = parsed.protocol === "https:" || localOk && parsed.protocol === "http:";
742
+ if (!httpOk) {
743
+ throw new ProviderError(
744
+ `Refusing to ${action} ${host || "this URL"}: only https:// URLs are allowed`,
745
+ opts.provider
746
+ );
747
+ }
748
+ if (!localOk && isNonPublicHost(host)) {
749
+ throw new ProviderError(
750
+ `Refusing to ${action} ${host}: it points to a local or private address`,
751
+ opts.provider
752
+ );
753
+ }
754
+ const trusted = (opts.trustedHosts ?? []).some((t) => host === t || host.endsWith(`.${t}`));
755
+ return { host, trusted };
756
+ }
757
+
629
758
  // src/core/async-poll.ts
630
759
  init_errors();
631
760
  var PollTimeoutError = class extends MultixError {
@@ -868,6 +997,7 @@ async function fetchJson(opts) {
868
997
  }
869
998
 
870
999
  // src/providers/atlascloud/prediction-output.ts
1000
+ init_errors();
871
1001
  var SUCCESS_STATUSES = /* @__PURE__ */ new Set(["completed", "succeeded", "success"]);
872
1002
  var FAILURE_STATUSES = /* @__PURE__ */ new Set(["failed", "error", "canceled", "cancelled", "timeout"]);
873
1003
  function norm(status) {
@@ -917,6 +1047,9 @@ function collectOutputs(prediction) {
917
1047
  }
918
1048
  return out;
919
1049
  }
1050
+ function checkOutputUrl(url) {
1051
+ checkUrlSafety(url, { provider: ATLAS_PROVIDER });
1052
+ }
920
1053
  function isMediaOutput(value) {
921
1054
  const s = value.trim();
922
1055
  return /^https?:\/\//i.test(s) || /^data:[^,]*,/i.test(s);
@@ -941,6 +1074,7 @@ var EXT_CATEGORY = {
941
1074
  flac: "audio",
942
1075
  ogg: "audio",
943
1076
  opus: "audio",
1077
+ weba: "audio",
944
1078
  pcm: "audio",
945
1079
  glb: "3d",
946
1080
  gltf: "3d",
@@ -958,8 +1092,14 @@ var MIME_EXT = {
958
1092
  "image/jpeg": "jpg",
959
1093
  "image/webp": "webp",
960
1094
  "image/gif": "gif",
1095
+ "image/avif": "avif",
1096
+ "image/bmp": "bmp",
1097
+ "image/svg+xml": "svg",
961
1098
  "video/mp4": "mp4",
962
1099
  "video/webm": "webm",
1100
+ "video/x-matroska": "mkv",
1101
+ "video/matroska": "mkv",
1102
+ "audio/webm": "weba",
963
1103
  "audio/mpeg": "mp3",
964
1104
  "audio/mp3": "mp3",
965
1105
  "audio/wav": "wav",
@@ -976,6 +1116,7 @@ function extensionForContentType(contentType) {
976
1116
  const mime = contentType?.split(";")[0]?.trim().toLowerCase();
977
1117
  return mime ? MIME_EXT[mime] : void 0;
978
1118
  }
1119
+ var KNOWN_EXTENSIONS = /* @__PURE__ */ new Set([...Object.keys(EXT_CATEGORY), "svg"]);
979
1120
  function outputExtension(value) {
980
1121
  const s = value.trim();
981
1122
  const dataMatch = /^data:([^;,]+)/i.exec(s);
@@ -989,6 +1130,7 @@ function outputExtension(value) {
989
1130
  const m = /\.([a-z0-9]{2,5})$/i.exec(pathname);
990
1131
  if (!m?.[1]) return void 0;
991
1132
  const ext = m[1].toLowerCase();
1133
+ if (!KNOWN_EXTENSIONS.has(ext)) return void 0;
992
1134
  return ext === "jpeg" ? "jpg" : ext;
993
1135
  }
994
1136
  function outputCategory(value) {
@@ -1022,13 +1164,32 @@ function classifyOutputs(prediction) {
1022
1164
  function hasNsfwFlag(prediction) {
1023
1165
  return Array.isArray(prediction.has_nsfw_contents) && prediction.has_nsfw_contents.some(Boolean);
1024
1166
  }
1167
+ function thumbnailSource(prediction) {
1168
+ return prediction.outputs ?? prediction.output ?? null;
1169
+ }
1170
+ var MAX_PREDICTION_ID_LENGTH = 128;
1171
+ var NAME_ID_LENGTH = 64;
1172
+ function fnv1a(value) {
1173
+ let hash = 2166136261;
1174
+ for (let i = 0; i < value.length; i++) {
1175
+ hash ^= value.charCodeAt(i);
1176
+ hash = Math.imul(hash, 16777619) >>> 0;
1177
+ }
1178
+ return hash.toString(16).padStart(8, "0");
1179
+ }
1025
1180
  function outputFileName(predictionId, index, ext) {
1026
- const prefix = predictionId.replace(/[^a-zA-Z0-9_-]/g, "").slice(0, 8) || "output";
1027
- return `atlas-${prefix}-${String(index + 1).padStart(2, "0")}.${ext}`;
1181
+ if (predictionId.length === 0 || predictionId.length > MAX_PREDICTION_ID_LENGTH) {
1182
+ throw new ProviderError(
1183
+ `Atlas Cloud returned an unusable prediction id (${predictionId.length === 0 ? "empty" : `${predictionId.length} characters`}).`,
1184
+ ATLAS_PROVIDER
1185
+ );
1186
+ }
1187
+ const short = predictionId.replace(/[^a-zA-Z0-9_-]/g, "").slice(0, NAME_ID_LENGTH);
1188
+ const id = short === predictionId ? short : short ? `${short}-${fnv1a(predictionId)}` : fnv1a(predictionId);
1189
+ return `atlas-${id}-${String(index + 1).padStart(2, "0")}.${ext}`;
1028
1190
  }
1029
1191
 
1030
1192
  // src/providers/atlascloud/client.ts
1031
- var ATLAS_PROVIDER = "atlascloud";
1032
1193
  var DEFAULT_UPLOAD_TIMEOUT_MS = 3e5;
1033
1194
  var CATALOG_TIMEOUT_MS = 15e3;
1034
1195
  var PREDICTION_ID_RE = /^[A-Za-z0-9_-]{1,128}$/;
@@ -1122,13 +1283,13 @@ var AtlasCloudClient = class {
1122
1283
  }
1123
1284
  /** Submit a generation job. `body` holds `model` plus the model's own fields, flattened. */
1124
1285
  async submit(kind, body) {
1125
- const path57 = SUBMIT_PATHS[kind];
1126
- if (!path57) throw new ValidationError(`Unknown Atlas Cloud media type: ${String(kind)}`);
1286
+ const path58 = SUBMIT_PATHS[kind];
1287
+ if (!path58) throw new ValidationError(`Unknown Atlas Cloud media type: ${String(kind)}`);
1127
1288
  if (typeof body.model !== "string" || !body.model.trim()) {
1128
1289
  throw new ValidationError("Atlas Cloud request body needs a non-empty `model`");
1129
1290
  }
1130
1291
  const env = await fetchJson({
1131
- url: `${this.baseUrl}${path57}`,
1292
+ url: `${this.baseUrl}${path58}`,
1132
1293
  method: "POST",
1133
1294
  headers: this.authHeaders(),
1134
1295
  body,
@@ -1316,7 +1477,7 @@ function requireAtlasKey() {
1316
1477
  }
1317
1478
  function createAtlasClientFromEnv() {
1318
1479
  const apiKey = requireAtlasKey();
1319
- const baseUrl = resolveProviderBaseUrl(ATLAS_ROUTING);
1480
+ const baseUrl = checkBaseUrl(resolveProviderBaseUrl(ATLAS_ROUTING), "ATLASCLOUD_BASE_URL");
1320
1481
  return new AtlasCloudClient({ apiKey, baseUrl });
1321
1482
  }
1322
1483
  async function resolveMediaRef(client, ref, logger) {
@@ -1386,8 +1547,8 @@ async function resolveMediaRefs(client, refs, logger) {
1386
1547
 
1387
1548
  // src/providers/atlascloud/commands/prediction-runner.ts
1388
1549
  init_errors();
1389
- import fs8 from "fs";
1390
- import path7 from "path";
1550
+ import fs9 from "fs";
1551
+ import path8 from "path";
1391
1552
 
1392
1553
  // src/core/http-client.ts
1393
1554
  import fs3 from "fs";
@@ -1787,14 +1948,18 @@ function detectThumbUrl(obj) {
1787
1948
  }
1788
1949
  return null;
1789
1950
  }
1951
+ function resultDest(result) {
1952
+ const dest = result?.dest;
1953
+ return typeof dest === "string" ? dest : void 0;
1954
+ }
1790
1955
  async function downloadThumbBeside(thumbUrl, videoPath, logger, download = downloadFile) {
1791
1956
  const match = thumbUrl.match(IMAGE_EXT_RE);
1792
1957
  const ext = match?.[1] ? match[1].toLowerCase().replace("jpeg", "jpg") : "jpg";
1793
1958
  const dir = path6.dirname(videoPath);
1794
1959
  const base = path6.basename(videoPath, path6.extname(videoPath));
1795
- const dest = path6.join(dir, `${base}_thumb.${ext}`);
1960
+ const planned = path6.join(dir, `${base}_thumb.${ext}`);
1796
1961
  try {
1797
- await download(thumbUrl, dest);
1962
+ const dest = resultDest(await download(thumbUrl, planned)) ?? planned;
1798
1963
  logger?.success(`Saved thumb ${dest}`);
1799
1964
  return dest;
1800
1965
  } catch (e) {
@@ -1822,6 +1987,189 @@ async function maybeDownloadThumb(source, videoPath, opts = {}) {
1822
1987
  return saved;
1823
1988
  }
1824
1989
 
1990
+ // src/providers/atlascloud/commands/output-claims.ts
1991
+ init_errors();
1992
+ import fs8 from "fs";
1993
+ import os2 from "os";
1994
+ import path7 from "path";
1995
+ var PART = ".part";
1996
+ var PART_SUFFIX = /(?:\.\d+-\d+-[a-z0-9]+)?\.part$/i;
1997
+ var TEMP_TAIL = /^\.(\d+)-\d+-[a-z0-9]+\.part$/i;
1998
+ var MAX_UNIQUE_ATTEMPTS = 1e3;
1999
+ var UNREADABLE_LOCK_GRACE_MS = 1e4;
2000
+ var foldCase = process.platform === "win32" || process.platform === "darwin";
2001
+ var fold = (s) => foldCase ? s.toLowerCase() : s;
2002
+ function stemOf(name) {
2003
+ const base = name.replace(PART_SUFFIX, "");
2004
+ return fold(base.slice(0, base.length - path7.extname(base).length));
2005
+ }
2006
+ function listDir(dir) {
2007
+ try {
2008
+ return fs8.readdirSync(dir);
2009
+ } catch {
2010
+ return [];
2011
+ }
2012
+ }
2013
+ function removeQuietly2(file) {
2014
+ try {
2015
+ fs8.rmSync(file, { force: true });
2016
+ } catch {
2017
+ }
2018
+ }
2019
+ function savedOutput(file) {
2020
+ const dir = path7.dirname(file);
2021
+ const stem = stemOf(path7.basename(file));
2022
+ const matches = listDir(dir).filter((n) => {
2023
+ if (PART_SUFFIX.test(n) || stemOf(n) !== stem) return false;
2024
+ const st = fs8.statSync(path7.join(dir, n), { throwIfNoEntry: false });
2025
+ return st?.isFile() === true && st.size > 0;
2026
+ });
2027
+ const exact = matches.find((n) => n === path7.basename(file));
2028
+ const found = exact ?? matches.sort()[0];
2029
+ return found === void 0 ? void 0 : path7.join(dir, found);
2030
+ }
2031
+ function* candidateNames(file) {
2032
+ const dir = path7.dirname(file);
2033
+ const ext = path7.extname(file);
2034
+ const stem = path7.basename(file, ext);
2035
+ const taken = new Set(listDir(dir).map(stemOf));
2036
+ for (let n = 0; n < MAX_UNIQUE_ATTEMPTS; n++) {
2037
+ const name = n === 0 ? stem : `${stem}-${n}`;
2038
+ if (!taken.has(fold(name))) yield path7.join(dir, `${name}${ext}`);
2039
+ }
2040
+ }
2041
+ function readLock(lock) {
2042
+ try {
2043
+ const v = JSON.parse(fs8.readFileSync(lock, "utf8"));
2044
+ if (Number.isInteger(v.pid) && v.pid > 0 && typeof v.host === "string") {
2045
+ return v;
2046
+ }
2047
+ } catch {
2048
+ }
2049
+ return void 0;
2050
+ }
2051
+ function isProcessAlive(pid) {
2052
+ if (pid === process.pid) return true;
2053
+ try {
2054
+ process.kill(pid, 0);
2055
+ return true;
2056
+ } catch (e) {
2057
+ return e.code !== "ESRCH";
2058
+ }
2059
+ }
2060
+ function isStaleLock(lock) {
2061
+ const info = readLock(lock);
2062
+ if (!info) {
2063
+ const st = fs8.statSync(lock, { throwIfNoEntry: false });
2064
+ return st === void 0 || Date.now() - st.mtimeMs > UNREADABLE_LOCK_GRACE_MS;
2065
+ }
2066
+ if (info.host !== os2.hostname()) return false;
2067
+ return !isProcessAlive(info.pid);
2068
+ }
2069
+ var activeClaims = /* @__PURE__ */ new Set();
2070
+ function tryLock(dest) {
2071
+ const lock = `${dest}${PART}`;
2072
+ const content = JSON.stringify({
2073
+ pid: process.pid,
2074
+ host: os2.hostname(),
2075
+ startedAt: (/* @__PURE__ */ new Date()).toISOString()
2076
+ });
2077
+ for (let attempt = 0; attempt < 2; attempt++) {
2078
+ try {
2079
+ fs8.writeFileSync(lock, content, { flag: "wx" });
2080
+ activeClaims.add(dest);
2081
+ installInterruptCleanup();
2082
+ return true;
2083
+ } catch (e) {
2084
+ if (e.code !== "EEXIST") throw e;
2085
+ if (attempt > 0 || !isStaleLock(lock)) return false;
2086
+ removeQuietly2(lock);
2087
+ }
2088
+ }
2089
+ return false;
2090
+ }
2091
+ function removeOrphanedTemps(dest) {
2092
+ const base = path7.basename(dest);
2093
+ const dir = path7.dirname(dest);
2094
+ for (const n of listDir(dir)) {
2095
+ if (!n.startsWith(base)) continue;
2096
+ const m = TEMP_TAIL.exec(n.slice(base.length));
2097
+ if (m?.[1] && !isProcessAlive(Number(m[1]))) removeQuietly2(path7.join(dir, n));
2098
+ }
2099
+ }
2100
+ function releaseClaim(dest) {
2101
+ if (!activeClaims.delete(dest)) return;
2102
+ removeQuietly2(`${dest}${PART}`);
2103
+ }
2104
+ function cleanupActiveClaims() {
2105
+ for (const dest of [...activeClaims]) {
2106
+ const base = path7.basename(dest);
2107
+ const dir = path7.dirname(dest);
2108
+ for (const n of listDir(dir)) {
2109
+ if (!n.startsWith(base)) continue;
2110
+ const m = TEMP_TAIL.exec(n.slice(base.length));
2111
+ if (m?.[1] && Number(m[1]) === process.pid) removeQuietly2(path7.join(dir, n));
2112
+ }
2113
+ releaseClaim(dest);
2114
+ }
2115
+ }
2116
+ var INTERRUPT_EXIT_CODES = { SIGINT: 130, SIGTERM: 143 };
2117
+ function handleInterrupt(signal, exit = (code) => process.exit(code)) {
2118
+ cleanupActiveClaims();
2119
+ exit(INTERRUPT_EXIT_CODES[signal]);
2120
+ }
2121
+ var interruptCleanupInstalled = false;
2122
+ function installInterruptCleanup() {
2123
+ if (interruptCleanupInstalled) return;
2124
+ interruptCleanupInstalled = true;
2125
+ process.once("SIGINT", () => handleInterrupt("SIGINT"));
2126
+ process.once("SIGTERM", () => handleInterrupt("SIGTERM"));
2127
+ }
2128
+ function heldMessage(dest) {
2129
+ const info = readLock(`${dest}${PART}`);
2130
+ const holder = info ? ` (pid ${info.pid} on ${info.host})` : "";
2131
+ const advice = "Wait for it to finish, then retry. A lock left by a run that has stopped is replaced automatically.";
2132
+ return `${dest}${PART} is held by another multix run${holder} downloading this output. ${advice}`;
2133
+ }
2134
+ function claimOutputPath(file, policy) {
2135
+ if (policy === "overwrite") return file;
2136
+ fs8.mkdirSync(path7.dirname(file), { recursive: true });
2137
+ if (policy === "reuse") {
2138
+ if (!tryLock(file)) throw new ProviderError(heldMessage(file), ATLAS_PROVIDER);
2139
+ removeOrphanedTemps(file);
2140
+ return file;
2141
+ }
2142
+ for (const candidate of candidateNames(file)) {
2143
+ if (!tryLock(candidate)) continue;
2144
+ if (fs8.existsSync(candidate)) {
2145
+ releaseClaim(candidate);
2146
+ continue;
2147
+ }
2148
+ removeOrphanedTemps(candidate);
2149
+ return candidate;
2150
+ }
2151
+ throw new ProviderError(
2152
+ `No free file name for ${file} after ${MAX_UNIQUE_ATTEMPTS} attempts; choose another --output.`,
2153
+ ATLAS_PROVIDER
2154
+ );
2155
+ }
2156
+ async function downloadClaimed(url, choose, policy, opts) {
2157
+ let claimed;
2158
+ try {
2159
+ const { dest } = await downloadToFile(url, {
2160
+ ...opts,
2161
+ dest: (response) => {
2162
+ const dest2 = claimOutputPath(choose(response), policy);
2163
+ if (policy !== "overwrite") claimed = dest2;
2164
+ return dest2;
2165
+ }
2166
+ });
2167
+ return dest;
2168
+ } finally {
2169
+ if (claimed) releaseClaim(claimed);
2170
+ }
2171
+ }
2172
+
1825
2173
  // src/providers/atlascloud/commands/prediction-runner.ts
1826
2174
  function addRunOptions(cmd, cfg) {
1827
2175
  cmd.option("--async", "Submit and exit; print the prediction id (resume with `atlas status`)").option("--wait-timeout <ms>", "Max time to wait for completion", String(cfg.waitTimeoutMs)).option("--output <path>", "Output file (single output) or directory (path without extension)").option("--no-download", "Do not download media; print output URLs instead").option("--no-validate", "Skip client-side validation against the model's published schema");
@@ -1882,8 +2230,13 @@ function assertEndpoint(spec, expected, model) {
1882
2230
  function capitalize(s) {
1883
2231
  return s.charAt(0).toUpperCase() + s.slice(1);
1884
2232
  }
1885
- function resumeCommand(id) {
1886
- return `multix atlas status ${id} --wait --download`;
2233
+ function resumeCommand(id, output) {
2234
+ const base = `multix atlas status ${id} --wait --download`;
2235
+ return output ? `${base} --output ${shellQuote(output)}` : base;
2236
+ }
2237
+ function shellQuote(value) {
2238
+ if (/^[\w@%+=:,./\\-]+$/.test(value)) return value;
2239
+ return `"${value.replace(/(["\\$`])/g, "\\$1")}"`;
1887
2240
  }
1888
2241
  async function executePrediction(o) {
1889
2242
  const { client, logger, opts } = o;
@@ -1893,22 +2246,27 @@ async function executePrediction(o) {
1893
2246
  const id = submitted.id;
1894
2247
  if (opts.async) {
1895
2248
  console.log(id);
1896
- console.error(`Prediction submitted. Resume with: ${resumeCommand(id)}`);
2249
+ console.error(`Prediction submitted. Resume with: ${resumeCommand(id, opts.output)}`);
1897
2250
  return { predictionId: id, files: [], texts: [] };
1898
2251
  }
1899
2252
  logger.info(`Prediction ${id} \u2014 waiting up to ${Math.round(timeoutMs / 1e3)}s...`);
1900
2253
  const final = isTerminalStatus(submitted.status) ? submitted : await client.waitForPrediction(id, {
1901
2254
  timeoutMs,
1902
2255
  intervalMs: o.pollIntervalMs,
1903
- resumeHint: `Resume with: ${resumeCommand(id)}`,
2256
+ resumeHint: `Resume with: ${resumeCommand(id, opts.output)}`,
1904
2257
  onPoll: (p, n) => logger.debug(`poll ${n}: ${p.status}`)
1905
2258
  });
1906
2259
  assertPredictionSucceeded(final);
1907
- const delivered = await deliverPrediction(final, { logger, opts, hint: o.hint });
2260
+ const delivered = await deliverPrediction(final, {
2261
+ logger,
2262
+ opts,
2263
+ hint: o.hint,
2264
+ predictionId: id
2265
+ });
1908
2266
  return { predictionId: id, prediction: final, ...delivered };
1909
2267
  }
1910
2268
  function isDirectoryOutput(output) {
1911
- return /[\\/]$/.test(output) || path7.extname(output) === "" || fs8.existsSync(output) && fs8.statSync(output).isDirectory();
2269
+ return /[\\/]$/.test(output) || path8.extname(output) === "" || fs9.existsSync(output) && fs9.statSync(output).isDirectory();
1912
2270
  }
1913
2271
  function planOutputPaths(urls, cfg) {
1914
2272
  const exts = urls.map((u) => {
@@ -1917,32 +2275,34 @@ function planOutputPaths(urls, cfg) {
1917
2275
  });
1918
2276
  const output = cfg.output;
1919
2277
  if (!output || isDirectoryOutput(output)) {
1920
- const dir = output ? path7.resolve(output) : cfg.outDir();
1921
- return exts.map((ext, i) => path7.join(dir, outputFileName(cfg.predictionId, i, ext)));
2278
+ const dir = output ? path8.resolve(output) : cfg.outDir();
2279
+ return exts.map((ext, i) => path8.join(dir, outputFileName(cfg.predictionId, i, ext)));
1922
2280
  }
1923
- const abs = path7.resolve(output);
2281
+ const abs = path8.resolve(output);
1924
2282
  if (urls.length === 1) return [abs];
1925
- const base = abs.slice(0, -path7.extname(abs).length);
2283
+ const base = abs.slice(0, -path8.extname(abs).length);
1926
2284
  return exts.map((ext, i) => `${base}-${String(i + 1).padStart(2, "0")}.${ext}`);
1927
2285
  }
1928
2286
  async function deliverPrediction(prediction, cfg) {
1929
2287
  const { logger, opts } = cfg;
2288
+ const id = cfg.predictionId ?? prediction.id;
2289
+ const retry = resumeCommand(id, opts.output);
1930
2290
  if (prediction.price !== void 0 && prediction.price !== "") {
1931
2291
  logger.info(`Cost: $${prediction.price}`);
1932
2292
  }
1933
2293
  const classified = classifyOutputs(prediction);
1934
2294
  if (classified.mode === "empty") {
1935
2295
  throw new ProviderError(
1936
- hasNsfwFlag(prediction) ? `Prediction ${prediction.id} returned no output: blocked by content safety (has_nsfw_contents). Try a different prompt or input.` : `Prediction ${prediction.id} completed but returned no outputs.`,
2296
+ hasNsfwFlag(prediction) ? `Prediction ${id} returned no output: blocked by content safety (has_nsfw_contents). Try a different prompt or input.` : `Prediction ${id} completed but returned no outputs.`,
1937
2297
  ATLAS_PROVIDER
1938
2298
  );
1939
2299
  }
1940
2300
  for (const text of classified.texts) console.log(text);
1941
2301
  if (classified.mode === "text") {
1942
2302
  if (opts.output) {
1943
- const dest = isDirectoryOutput(opts.output) ? path7.resolve(opts.output, outputFileName(prediction.id, 0, "txt")) : path7.resolve(opts.output);
1944
- fs8.mkdirSync(path7.dirname(dest), { recursive: true });
1945
- fs8.writeFileSync(dest, `${classified.texts.join("\n\n")}
2303
+ const dest = isDirectoryOutput(opts.output) ? path8.resolve(opts.output, outputFileName(id, 0, "txt")) : path8.resolve(opts.output);
2304
+ fs9.mkdirSync(path8.dirname(dest), { recursive: true });
2305
+ fs9.writeFileSync(dest, `${classified.texts.join("\n\n")}
1946
2306
  `, "utf8");
1947
2307
  logger.success(`Saved text output to ${dest}`);
1948
2308
  return { files: [dest], texts: classified.texts };
@@ -1954,26 +2314,39 @@ async function deliverPrediction(prediction, cfg) {
1954
2314
  return { files: [], texts: classified.texts };
1955
2315
  }
1956
2316
  const dests = planOutputPaths(classified.urls, {
1957
- predictionId: prediction.id,
2317
+ predictionId: id,
1958
2318
  outDir: getOutputDir,
1959
2319
  output: opts.output,
1960
2320
  hint: cfg.hint
1961
2321
  });
1962
- const explicitFile = classified.urls.length === 1 && opts.output !== void 0 && !isDirectoryOutput(opts.output);
2322
+ const policy = namingPolicy(opts.output, classified.urls.length);
2323
+ const maxBytes = maxDownloadBytes();
1963
2324
  const saved = [];
2325
+ const downloaded = [];
2326
+ const reused = /* @__PURE__ */ new Set();
1964
2327
  let failed = 0;
1965
2328
  for (let i = 0; i < classified.urls.length; i++) {
1966
2329
  const url = classified.urls[i];
1967
2330
  const planned = dests[i];
1968
- const part = `${planned}.part`;
1969
2331
  try {
1970
- const contentType = await downloadFile(url, part);
1971
- const dest = explicitFile || outputExtension(url) ? planned : withContentTypeExtension(planned, contentType);
1972
- fs8.renameSync(part, dest);
2332
+ const existing = policy === "reuse" ? savedOutput(planned) : void 0;
2333
+ if (existing) {
2334
+ saved.push(existing);
2335
+ reused.add(existing);
2336
+ logger.info(`Already saved: ${existing}`);
2337
+ continue;
2338
+ }
2339
+ const dest = await downloadClaimed(
2340
+ url,
2341
+ (response) => policy === "overwrite" || outputExtension(url) ? planned : withContentTypeExtension(planned, response.headers.get("content-type")),
2342
+ policy,
2343
+ // Inline data: outputs are decoded by fetch, never sent over the network.
2344
+ { maxBytes, validateUrl: isDataUrl(url) ? void 0 : checkOutputUrl }
2345
+ );
1973
2346
  saved.push(dest);
2347
+ downloaded.push(dest);
1974
2348
  logger.debug(`Downloaded ${redactUrl(url)} -> ${dest}`);
1975
2349
  } catch (e) {
1976
- fs8.rmSync(part, { force: true });
1977
2350
  failed++;
1978
2351
  logger.warn(
1979
2352
  `Download failed (${redactUrl(url)}): ${e instanceof Error ? e.message : String(e)}`
@@ -1982,11 +2355,11 @@ async function deliverPrediction(prediction, cfg) {
1982
2355
  }
1983
2356
  if (saved.length === 0) {
1984
2357
  throw new ProviderError(
1985
- `Prediction ${prediction.id} produced ${classified.urls.length} output(s) but none downloaded. Retry with: ${resumeCommand(prediction.id)}`,
2358
+ `Prediction ${id} produced ${classified.urls.length} output(s) but none downloaded. Retry with: ${retry}`,
1986
2359
  ATLAS_PROVIDER
1987
2360
  );
1988
2361
  }
1989
- const images = saved.filter((f) => outputCategory(f) === "image");
2362
+ const images = downloaded.filter((f) => outputCategory(f) === "image");
1990
2363
  const fileOutput = opts.output && !isDirectoryOutput(opts.output) ? opts.output : void 0;
1991
2364
  const finalImages = finalizeGeneratedImages(images, {
1992
2365
  imageFormat: opts.imageFormat,
@@ -1997,23 +2370,42 @@ async function deliverPrediction(prediction, cfg) {
1997
2370
  const files = saved.map((f) => finalImages[images.indexOf(f)] ?? f);
1998
2371
  const firstVideo = files.find((f) => outputCategory(f) === "video");
1999
2372
  if (firstVideo) {
2000
- await maybeDownloadThumb(prediction, firstVideo, { skip: opts.thumb === false, logger });
2373
+ await maybeDownloadThumb(thumbnailSource(prediction), firstVideo, {
2374
+ skip: opts.thumb === false,
2375
+ logger,
2376
+ download: async (thumbUrl, planned) => {
2377
+ const existing = policy === "reuse" ? savedOutput(planned) : void 0;
2378
+ if (existing) return { dest: existing };
2379
+ const dest = await downloadClaimed(thumbUrl, () => planned, policy, {
2380
+ maxBytes,
2381
+ validateUrl: checkOutputUrl
2382
+ });
2383
+ return { dest };
2384
+ }
2385
+ });
2001
2386
  }
2002
2387
  console.log(`
2003
- Prediction ${prediction.id}: saved ${files.length} file(s)`);
2004
- for (const f of files) console.log(` ${f}`);
2388
+ Prediction ${id}: saved ${files.length} file(s)`);
2389
+ for (const f of files) console.log(` ${f}${reused.has(f) ? " (already saved)" : ""}`);
2005
2390
  if (failed > 0) {
2006
2391
  throw new ProviderError(
2007
- `Prediction ${prediction.id}: downloaded ${saved.length} of ${classified.urls.length} output(s); ${failed} failed. Saved: ${files.join(", ")}. Retry with: ${resumeCommand(prediction.id)}`,
2392
+ `Prediction ${id}: downloaded ${saved.length} of ${classified.urls.length} output(s); ${failed} failed. Saved: ${files.join(", ")}. Retry with: ${retry}`,
2008
2393
  ATLAS_PROVIDER
2009
2394
  );
2010
2395
  }
2011
2396
  return { files, texts: classified.texts };
2012
2397
  }
2398
+ function isDataUrl(url) {
2399
+ return /^data:/i.test(url.trim());
2400
+ }
2401
+ function namingPolicy(output, outputCount) {
2402
+ if (!output || isDirectoryOutput(output)) return "reuse";
2403
+ return outputCount === 1 ? "overwrite" : "unique";
2404
+ }
2013
2405
  function withContentTypeExtension(planned, contentType) {
2014
2406
  const ext = extensionForContentType(contentType);
2015
2407
  if (!ext) return planned;
2016
- const current = path7.extname(planned);
2408
+ const current = path8.extname(planned);
2017
2409
  return `${current ? planned.slice(0, -current.length) : planned}.${ext}`;
2018
2410
  }
2019
2411
 
@@ -2148,12 +2540,12 @@ init_errors();
2148
2540
 
2149
2541
  // src/core/json-input.ts
2150
2542
  init_errors();
2151
- import fs9 from "fs";
2543
+ import fs10 from "fs";
2152
2544
  function parseJsonInput(raw, flag = "--input") {
2153
2545
  let text;
2154
2546
  if (raw.startsWith("@")) {
2155
2547
  try {
2156
- text = fs9.readFileSync(raw.slice(1), "utf8");
2548
+ text = fs10.readFileSync(raw.slice(1), "utf8");
2157
2549
  } catch (e) {
2158
2550
  const msg = e instanceof Error ? e.message : String(e);
2159
2551
  throw new ValidationError(`Cannot read ${flag} file: ${msg}`);
@@ -2238,7 +2630,7 @@ function registerAtlasStatusCommand(parent) {
2238
2630
  const wait = Boolean(opts.wait || opts.download);
2239
2631
  const prediction = wait ? await client.waitForPrediction(predictionId, {
2240
2632
  timeoutMs: parseTimeoutMs(opts.waitTimeout, STATUS_WAIT_MS),
2241
- resumeHint: `Resume with: ${resumeCommand(predictionId)}`,
2633
+ resumeHint: `Resume with: ${resumeCommand(predictionId, opts.output)}`,
2242
2634
  onPoll: (p, n) => logger.debug(`poll ${n}: ${p.status}`)
2243
2635
  }) : await client.getPrediction(predictionId);
2244
2636
  if (!opts.download) {
@@ -2247,7 +2639,11 @@ function registerAtlasStatusCommand(parent) {
2247
2639
  return;
2248
2640
  }
2249
2641
  assertPredictionSucceeded(prediction);
2250
- await deliverPrediction(prediction, { logger, opts: { ...opts, download: true } });
2642
+ await deliverPrediction(prediction, {
2643
+ logger,
2644
+ opts: { ...opts, download: true },
2645
+ predictionId
2646
+ });
2251
2647
  });
2252
2648
  }
2253
2649
 
@@ -2377,11 +2773,11 @@ function registerAtlasCloudCommands(program2) {
2377
2773
  }
2378
2774
 
2379
2775
  // src/providers/byteplus/commands/generate-3d.ts
2380
- import path9 from "path";
2776
+ import path10 from "path";
2381
2777
 
2382
2778
  // src/core/image-input.ts
2383
- import fs10 from "fs/promises";
2384
- import path8 from "path";
2779
+ import fs11 from "fs/promises";
2780
+ import path9 from "path";
2385
2781
  var IMAGE_MIME_BY_EXT = {
2386
2782
  ".jpg": "image/jpeg",
2387
2783
  ".jpeg": "image/jpeg",
@@ -2408,7 +2804,7 @@ function isHttpUrl(input) {
2408
2804
  return /^https?:\/\//i.test(input);
2409
2805
  }
2410
2806
  async function readAsDataUrl(filePath, mimeTable, kindLabel, opts = {}) {
2411
- const ext = path8.extname(filePath).toLowerCase();
2807
+ const ext = path9.extname(filePath).toLowerCase();
2412
2808
  const mime = mimeTable[ext];
2413
2809
  if (!mime) {
2414
2810
  throw new Error(
@@ -2417,7 +2813,7 @@ async function readAsDataUrl(filePath, mimeTable, kindLabel, opts = {}) {
2417
2813
  }
2418
2814
  const hard = opts.hardLimitBytes ?? DEFAULT_HARD_LIMIT_BYTES;
2419
2815
  const soft = opts.softLimitBytes ?? DEFAULT_SOFT_LIMIT_BYTES;
2420
- const buf = await fs10.readFile(filePath);
2816
+ const buf = await fs11.readFile(filePath);
2421
2817
  if (buf.byteLength > hard) {
2422
2818
  throw new Error(
2423
2819
  `${kindLabel} file ${filePath} is ${(buf.byteLength / 1024 / 1024).toFixed(1)}MB \u2014 exceeds ${(hard / 1024 / 1024).toFixed(0)}MB limit. Upload to a URL instead.`
@@ -2546,9 +2942,9 @@ var BytePlusClient = class {
2546
2942
  }
2547
2943
  apiKey;
2548
2944
  baseUrl;
2549
- async request(path57, opts = {}) {
2945
+ async request(path58, opts = {}) {
2550
2946
  const base = this.baseUrl.replace(/\/$/, "");
2551
- const url = new URL(base + path57);
2947
+ const url = new URL(base + path58);
2552
2948
  if (opts.query) {
2553
2949
  for (const [k, v] of Object.entries(opts.query)) {
2554
2950
  if (v !== void 0 && v !== null) url.searchParams.set(k, String(v));
@@ -2566,14 +2962,14 @@ var BytePlusClient = class {
2566
2962
  timeoutMs: opts.timeoutMs ?? 12e4
2567
2963
  });
2568
2964
  }
2569
- get(path57, query, logger) {
2570
- return this.request(path57, { method: "GET", query, logger });
2965
+ get(path58, query, logger) {
2966
+ return this.request(path58, { method: "GET", query, logger });
2571
2967
  }
2572
- post(path57, body, logger) {
2573
- return this.request(path57, { method: "POST", body, logger });
2968
+ post(path58, body, logger) {
2969
+ return this.request(path58, { method: "POST", body, logger });
2574
2970
  }
2575
- delete(path57, logger) {
2576
- return this.request(path57, { method: "DELETE", logger });
2971
+ delete(path58, logger) {
2972
+ return this.request(path58, { method: "DELETE", logger });
2577
2973
  }
2578
2974
  };
2579
2975
  function createBytePlusClient() {
@@ -2678,7 +3074,7 @@ function registerBytePlusGenerate3DCommand(parent) {
2678
3074
  process.exit(1);
2679
3075
  }
2680
3076
  const ext = inferModelFileExt(url);
2681
- const outPath = opts.output ?? path9.join(getOutputDir(), `byteplus-3d-${taskId.slice(0, 8)}-${Date.now()}.${ext}`);
3077
+ const outPath = opts.output ?? path10.join(getOutputDir(), `byteplus-3d-${taskId.slice(0, 8)}-${Date.now()}.${ext}`);
2682
3078
  await downloadModelFile(url, outPath, logger);
2683
3079
  console.log(`
2684
3080
  Task: ${taskId}`);
@@ -2701,8 +3097,8 @@ Task: ${taskId}`);
2701
3097
  }
2702
3098
 
2703
3099
  // src/providers/byteplus/generators/image.ts
2704
- import fs11 from "fs";
2705
- import path10 from "path";
3100
+ import fs12 from "fs";
3101
+ import path11 from "path";
2706
3102
  var ASPECT_TO_SIZE = {
2707
3103
  "1:1": "2048x2048",
2708
3104
  "16:9": "2560x1440",
@@ -2766,14 +3162,14 @@ async function generateSeedream(client, opts) {
2766
3162
  }
2767
3163
  const ext = (() => {
2768
3164
  try {
2769
- const e = path10.extname(new URL(url).pathname);
3165
+ const e = path11.extname(new URL(url).pathname);
2770
3166
  return e || ".jpg";
2771
3167
  } catch {
2772
3168
  return ".jpg";
2773
3169
  }
2774
3170
  })();
2775
- const filename = opts.output && items.length === 1 ? path10.resolve(opts.output) : path10.join(outDir, `byteplus-image-${ts}-${i + 1}${ext}`);
2776
- fs11.mkdirSync(path10.dirname(filename), { recursive: true });
3171
+ const filename = opts.output && items.length === 1 ? path11.resolve(opts.output) : path11.join(outDir, `byteplus-image-${ts}-${i + 1}${ext}`);
3172
+ fs12.mkdirSync(path11.dirname(filename), { recursive: true });
2777
3173
  await downloadFile(url, filename);
2778
3174
  saved.push({ path: filename, url, seed: item.seed });
2779
3175
  logger?.success(`Saved ${filename}`);
@@ -2861,7 +3257,7 @@ function collect2(value, prev) {
2861
3257
  }
2862
3258
 
2863
3259
  // src/providers/byteplus/commands/image-to-video.ts
2864
- import path11 from "path";
3260
+ import path12 from "path";
2865
3261
 
2866
3262
  // src/providers/byteplus/generators/video.ts
2867
3263
  function buildFlagSuffix(opts) {
@@ -2984,7 +3380,7 @@ function registerBytePlusImageToVideoCommand(parent) {
2984
3380
  logger.error(`Task succeeded but no video_url: ${JSON.stringify(final)}`);
2985
3381
  process.exit(1);
2986
3382
  }
2987
- const outPath = opts.output ?? path11.join(getOutputDir(), `byteplus-i2v-${taskId.slice(0, 8)}-${Date.now()}.mp4`);
3383
+ const outPath = opts.output ?? path12.join(getOutputDir(), `byteplus-i2v-${taskId.slice(0, 8)}-${Date.now()}.mp4`);
2988
3384
  await downloadVideo(url, outPath, logger);
2989
3385
  await maybeDownloadThumb(final, outPath, {
2990
3386
  skip: opts.thumb === false,
@@ -3010,7 +3406,7 @@ Task: ${taskId}`);
3010
3406
  }
3011
3407
 
3012
3408
  // src/providers/byteplus/commands/reference-to-video.ts
3013
- import path12 from "path";
3409
+ import path13 from "path";
3014
3410
 
3015
3411
  // src/providers/byteplus/reference-input.ts
3016
3412
  var MAX_TOTAL_REFS = 12;
@@ -3042,10 +3438,10 @@ function toRef(kind, resolved, role) {
3042
3438
  return { kind, url: resolved.dataUrl, mime: resolved.mime, role };
3043
3439
  }
3044
3440
  async function resolveRefSpec(kind, spec, logger) {
3045
- const { path: path57, role } = parseRefSpec(spec);
3046
- if (kind === "image") return toRef(kind, await resolveImageInput(path57, logger), role);
3047
- if (kind === "video") return toRef(kind, await resolveVideoInput(path57, logger), role);
3048
- return toRef(kind, await resolveAudioInput(path57, logger), role);
3441
+ const { path: path58, role } = parseRefSpec(spec);
3442
+ if (kind === "image") return toRef(kind, await resolveImageInput(path58, logger), role);
3443
+ if (kind === "video") return toRef(kind, await resolveVideoInput(path58, logger), role);
3444
+ return toRef(kind, await resolveAudioInput(path58, logger), role);
3049
3445
  }
3050
3446
  async function resolveRefSpecs(specs, logger) {
3051
3447
  const out = [];
@@ -3142,7 +3538,7 @@ function registerBytePlusReferenceToVideoCommand(parent) {
3142
3538
  logger.error(`Task succeeded but no video_url: ${JSON.stringify(final)}`);
3143
3539
  process.exit(1);
3144
3540
  }
3145
- const outPath = opts.output ?? path12.join(getOutputDir(), `byteplus-r2v-${taskId.slice(0, 8)}-${Date.now()}.mp4`);
3541
+ const outPath = opts.output ?? path13.join(getOutputDir(), `byteplus-r2v-${taskId.slice(0, 8)}-${Date.now()}.mp4`);
3146
3542
  await downloadVideo(url, outPath, logger);
3147
3543
  await maybeDownloadThumb(final, outPath, {
3148
3544
  skip: opts.thumb === false,
@@ -3168,7 +3564,7 @@ Task: ${taskId}`);
3168
3564
  }
3169
3565
 
3170
3566
  // src/providers/byteplus/commands/status.ts
3171
- import path13 from "path";
3567
+ import path14 from "path";
3172
3568
  function registerBytePlusStatusCommand(parent) {
3173
3569
  parent.command("status <taskId>").description("Get a Seedance task's status; --wait + --download to fetch the result MP4").option("--wait", "Poll until terminal status").option("--wait-timeout <ms>", "Poll timeout in ms (with --wait)", "600000").option("--download", "Download MP4 on success (implies --wait)").option("--output <path>", "Output MP4 path (with --download)").option("--no-thumb", "Skip downloading the thumbnail if the API returns one").option("-v, --verbose", "Verbose logging").action(
3174
3570
  async (taskId, opts) => {
@@ -3186,7 +3582,7 @@ function registerBytePlusStatusCommand(parent) {
3186
3582
  const videoUrl2 = content?.video_url;
3187
3583
  const fileUrl = content?.file_url;
3188
3584
  if (videoUrl2) {
3189
- const outPath = opts.output ?? path13.join(getOutputDir(), `byteplus-video-${taskId.slice(0, 8)}.mp4`);
3585
+ const outPath = opts.output ?? path14.join(getOutputDir(), `byteplus-video-${taskId.slice(0, 8)}.mp4`);
3190
3586
  await downloadVideo(videoUrl2, outPath, logger);
3191
3587
  await maybeDownloadThumb(status, outPath, {
3192
3588
  skip: opts.thumb === false,
@@ -3196,7 +3592,7 @@ function registerBytePlusStatusCommand(parent) {
3196
3592
  console.log(`Saved: ${outPath}`);
3197
3593
  } else if (fileUrl) {
3198
3594
  const ext = inferModelFileExt(fileUrl);
3199
- const outPath = opts.output ?? path13.join(getOutputDir(), `byteplus-3d-${taskId.slice(0, 8)}.${ext}`);
3595
+ const outPath = opts.output ?? path14.join(getOutputDir(), `byteplus-3d-${taskId.slice(0, 8)}.${ext}`);
3200
3596
  await downloadModelFile(fileUrl, outPath, logger);
3201
3597
  console.log(`Saved: ${outPath}`);
3202
3598
  } else {
@@ -3220,7 +3616,7 @@ function registerBytePlusStatusCommand(parent) {
3220
3616
  }
3221
3617
 
3222
3618
  // src/providers/byteplus/commands/video.ts
3223
- import path14 from "path";
3619
+ import path15 from "path";
3224
3620
  function registerBytePlusVideoCommand(parent) {
3225
3621
  parent.command("video").description("Generate a video from a text prompt via BytePlus Seedance 2.0").requiredOption("--prompt <text>", "Text prompt").option("-m, --model <id>", "Model id", BYTEPLUS_DEFAULTS.videoModel).option("--resolution <r>", "480p|720p|1080p|2k", BYTEPLUS_DEFAULTS.videoResolution).option("--duration <n>", "Duration seconds (4-15)", String(BYTEPLUS_DEFAULTS.videoDuration)).option(
3226
3622
  "--aspect-ratio <r>",
@@ -3262,7 +3658,7 @@ function registerBytePlusVideoCommand(parent) {
3262
3658
  logger.error(`Task succeeded but no video_url in response: ${JSON.stringify(final)}`);
3263
3659
  process.exit(1);
3264
3660
  }
3265
- const outPath = opts.output ?? path14.join(getOutputDir(), `byteplus-video-${taskId.slice(0, 8)}-${Date.now()}.mp4`);
3661
+ const outPath = opts.output ?? path15.join(getOutputDir(), `byteplus-video-${taskId.slice(0, 8)}-${Date.now()}.mp4`);
3266
3662
  await downloadVideo(url, outPath, logger);
3267
3663
  await maybeDownloadThumb(final, outPath, {
3268
3664
  skip: opts.thumb === false,
@@ -3548,15 +3944,15 @@ async function downloadVideo2(url) {
3548
3944
 
3549
3945
  // src/providers/cloudflare/media-output.ts
3550
3946
  init_errors();
3551
- import fs12 from "fs";
3552
- import path15 from "path";
3947
+ import fs13 from "fs";
3948
+ import path16 from "path";
3553
3949
  function destination(filename, output) {
3554
- return output ? path15.resolve(output) : path15.join(getOutputDir(), filename);
3950
+ return output ? path16.resolve(output) : path16.join(getOutputDir(), filename);
3555
3951
  }
3556
3952
  function saveBytes(bytes, filename, output) {
3557
3953
  const dest = destination(filename, output);
3558
- fs12.mkdirSync(path15.dirname(dest), { recursive: true });
3559
- fs12.writeFileSync(dest, bytes);
3954
+ fs13.mkdirSync(path16.dirname(dest), { recursive: true });
3955
+ fs13.writeFileSync(dest, bytes);
3560
3956
  return dest;
3561
3957
  }
3562
3958
  function saveBase64Image(image, output) {
@@ -3739,8 +4135,8 @@ function registerCloudflareCommands(program2) {
3739
4135
  }
3740
4136
 
3741
4137
  // src/providers/elevenlabs/client.ts
3742
- import fs13 from "fs";
3743
- import path16 from "path";
4138
+ import fs14 from "fs";
4139
+ import path17 from "path";
3744
4140
  init_errors();
3745
4141
 
3746
4142
  // src/providers/elevenlabs/models.ts
@@ -3903,10 +4299,10 @@ async function apiPostMultipart(endpoint2, form, apiKey, opts = {}) {
3903
4299
  return res.json();
3904
4300
  }
3905
4301
  function readFileAsBlob(filePath, fieldFilename) {
3906
- const abs = path16.resolve(filePath);
3907
- if (!fs13.existsSync(abs)) throw new ValidationError(`File not found: ${abs}`);
3908
- const buf = fs13.readFileSync(abs);
3909
- const name = fieldFilename ?? path16.basename(abs);
4302
+ const abs = path17.resolve(filePath);
4303
+ if (!fs14.existsSync(abs)) throw new ValidationError(`File not found: ${abs}`);
4304
+ const buf = fs14.readFileSync(abs);
4305
+ const name = fieldFilename ?? path17.basename(abs);
3910
4306
  return new File([new Uint8Array(buf)], name);
3911
4307
  }
3912
4308
  async function pollUntil2(fetchStatus, isDone, isFailed, opts = {}) {
@@ -3930,12 +4326,12 @@ async function pollUntil2(fetchStatus, isDone, isFailed, opts = {}) {
3930
4326
  function saveBytes2(opts) {
3931
4327
  const { bytes, task, ext, outputCopy, logger } = opts;
3932
4328
  const dir = getOutputDir();
3933
- const dest = path16.join(dir, `elevenlabs_${task}_${Date.now()}.${ext}`);
3934
- fs13.writeFileSync(dest, bytes);
4329
+ const dest = path17.join(dir, `elevenlabs_${task}_${Date.now()}.${ext}`);
4330
+ fs14.writeFileSync(dest, bytes);
3935
4331
  logger?.success(`Saved: ${dest} (${(bytes.length / 1024).toFixed(1)} KB)`);
3936
4332
  if (outputCopy) {
3937
- fs13.mkdirSync(path16.dirname(path16.resolve(outputCopy)), { recursive: true });
3938
- fs13.copyFileSync(dest, outputCopy);
4333
+ fs14.mkdirSync(path17.dirname(path17.resolve(outputCopy)), { recursive: true });
4334
+ fs14.copyFileSync(dest, outputCopy);
3939
4335
  }
3940
4336
  return dest;
3941
4337
  }
@@ -3974,14 +4370,14 @@ ${models.length} models`);
3974
4370
  }
3975
4371
 
3976
4372
  // src/providers/elevenlabs/commands/align.ts
3977
- import fs14 from "fs";
3978
- import path17 from "path";
4373
+ import fs15 from "fs";
4374
+ import path18 from "path";
3979
4375
  function registerAlignCommand(parent) {
3980
4376
  parent.command("align").description("Forced alignment of audio to a transcript (returns word/char timestamps)").requiredOption("--input <path>", "Source audio file").requiredOption("--text <str>", "Transcript text (or pass --text-file)").option("--text-file <path>", "Read transcript from file").option("--output <path>", "Save JSON to this path (default: multix-output)").option("-v, --verbose", "Verbose logging").action(async (opts) => {
3981
4377
  const logger = createLogger({ verbose: opts.verbose ?? false });
3982
4378
  const apiKey = requireElevenLabsKey();
3983
4379
  let text = opts.text;
3984
- if (opts.textFile) text = fs14.readFileSync(path17.resolve(opts.textFile), "utf8");
4380
+ if (opts.textFile) text = fs15.readFileSync(path18.resolve(opts.textFile), "utf8");
3985
4381
  const form = new FormData();
3986
4382
  form.append("file", readFileAsBlob(opts.input));
3987
4383
  form.append("text", text);
@@ -3989,9 +4385,9 @@ function registerAlignCommand(parent) {
3989
4385
  const data = await apiPostMultipart("forced-alignment", form, apiKey, {
3990
4386
  timeoutMs: 6e5
3991
4387
  });
3992
- const dest = opts.output ? path17.resolve(opts.output) : path17.join(getOutputDir(), `elevenlabs_align_${Date.now()}.json`);
3993
- fs14.mkdirSync(path17.dirname(dest), { recursive: true });
3994
- fs14.writeFileSync(dest, JSON.stringify(data, null, 2));
4388
+ const dest = opts.output ? path18.resolve(opts.output) : path18.join(getOutputDir(), `elevenlabs_align_${Date.now()}.json`);
4389
+ fs15.mkdirSync(path18.dirname(dest), { recursive: true });
4390
+ fs15.writeFileSync(dest, JSON.stringify(data, null, 2));
3995
4391
  logger.success(`Saved alignment: ${dest}`);
3996
4392
  if (data.words) console.log(`Aligned ${data.words.length} words (loss=${data.loss ?? "?"})`);
3997
4393
  });
@@ -4033,8 +4429,8 @@ function registerCloneCommand(parent) {
4033
4429
 
4034
4430
  // src/providers/elevenlabs/commands/dub.ts
4035
4431
  init_errors();
4036
- import fs15 from "fs";
4037
- import path18 from "path";
4432
+ import fs16 from "fs";
4433
+ import path19 from "path";
4038
4434
  function registerDubCommand(parent) {
4039
4435
  parent.command("dub").description("Dub a video/audio file into target languages (async)").option("--input <path>", "Source media file (use --input or --source-url)").option("--source-url <url>", "Public URL of source media").requiredOption("--target-lang <code>", "Target language ISO code (e.g. es, fr, de)").option("--source-lang <code>", "Source language (auto if omitted)").option("--num-speakers <n>", "Speaker count (auto if omitted)").option("--watermark", "Add ElevenLabs watermark", false).option("--start-time <s>", "Start time in seconds").option("--end-time <s>", "End time in seconds").option("--highest-resolution", "Use highest resolution video", false).option("--name <n>", "Project display name").option("--async", "Return job id immediately, do not wait", false).option("--wait", "Wait until status=dubbed (implied by --download)", false).option("--download", "Download dubbed media when ready", false).option("--poll-interval <s>", "Poll interval seconds", "10").option("--wait-timeout <ms>", "Poll timeout", "1800000").option("--output <path>", "Output path for downloaded dub").option("-v, --verbose", "Verbose logging").action(async (opts) => {
4040
4436
  const logger = createLogger({ verbose: opts.verbose ?? false });
@@ -4077,7 +4473,7 @@ function registerDubCommand(parent) {
4077
4473
  );
4078
4474
  logger.success(`Dub complete: ${start.dubbing_id}`);
4079
4475
  if (opts.download) {
4080
- const dest = opts.output ? path18.resolve(opts.output) : path18.join(getOutputDir(), `elevenlabs_dub_${start.dubbing_id}_${opts.targetLang}.mp4`);
4476
+ const dest = opts.output ? path19.resolve(opts.output) : path19.join(getOutputDir(), `elevenlabs_dub_${start.dubbing_id}_${opts.targetLang}.mp4`);
4081
4477
  const url = `${ELEVENLABS_BASE_URL}/dubbing/${start.dubbing_id}/audio/${opts.targetLang}`;
4082
4478
  await downloadDub(url, apiKey, dest);
4083
4479
  logger.success(`Saved: ${dest}`);
@@ -4092,7 +4488,7 @@ Dubbed file: ${dest}`);
4092
4488
  const status = await apiGet(`dubbing/${dubbingId}`, apiKey);
4093
4489
  console.log(JSON.stringify(status, null, 2));
4094
4490
  if (opts.download) {
4095
- const dest = opts.output ? path18.resolve(opts.output) : path18.join(getOutputDir(), `elevenlabs_dub_${dubbingId}_${opts.download}.mp4`);
4491
+ const dest = opts.output ? path19.resolve(opts.output) : path19.join(getOutputDir(), `elevenlabs_dub_${dubbingId}_${opts.download}.mp4`);
4096
4492
  const url = `${ELEVENLABS_BASE_URL}/dubbing/${dubbingId}/audio/${opts.download}`;
4097
4493
  await downloadDub(url, apiKey, dest);
4098
4494
  console.log(`Saved: ${dest}`);
@@ -4100,7 +4496,7 @@ Dubbed file: ${dest}`);
4100
4496
  });
4101
4497
  }
4102
4498
  async function downloadDub(url, apiKey, dest) {
4103
- fs15.mkdirSync(path18.dirname(dest), { recursive: true });
4499
+ fs16.mkdirSync(path19.dirname(dest), { recursive: true });
4104
4500
  const { response: res } = await fetchWithValidatedRedirects(url, {
4105
4501
  headers: { "xi-api-key": apiKey },
4106
4502
  signal: AbortSignal.timeout(6e5)
@@ -4109,7 +4505,7 @@ async function downloadDub(url, apiKey, dest) {
4109
4505
  throw new Error(`Failed to download dub: HTTP ${res.status}`);
4110
4506
  }
4111
4507
  const arr = new Uint8Array(await res.arrayBuffer());
4112
- fs15.writeFileSync(dest, arr);
4508
+ fs16.writeFileSync(dest, arr);
4113
4509
  }
4114
4510
 
4115
4511
  // src/providers/elevenlabs/commands/isolate.ts
@@ -4137,8 +4533,8 @@ Isolated audio: ${dest}`);
4137
4533
 
4138
4534
  // src/providers/elevenlabs/commands/music.ts
4139
4535
  init_errors();
4140
- import fs16 from "fs";
4141
- import path19 from "path";
4536
+ import fs17 from "fs";
4537
+ import path20 from "path";
4142
4538
  function registerMusicCommand(parent) {
4143
4539
  parent.command("music").description("Generate music from a text prompt or composition plan").option("--prompt <str>", "Music prompt (mutually exclusive with --plan)").option("--plan <path>", "Path to composition plan JSON file").option("--music-length-ms <n>", "Total length in milliseconds (default 30000)").option("--model <id>", "Music model", "music_v1").option(
4144
4540
  "--format <fmt>",
@@ -4153,9 +4549,9 @@ function registerMusicCommand(parent) {
4153
4549
  const payload = { model_id: opts.model };
4154
4550
  if (opts.prompt) payload.prompt = opts.prompt;
4155
4551
  if (opts.plan) {
4156
- const planPath = path19.resolve(opts.plan);
4157
- if (!fs16.existsSync(planPath)) throw new ValidationError(`Plan file not found: ${planPath}`);
4158
- const planJson = JSON.parse(fs16.readFileSync(planPath, "utf8"));
4552
+ const planPath = path20.resolve(opts.plan);
4553
+ if (!fs17.existsSync(planPath)) throw new ValidationError(`Plan file not found: ${planPath}`);
4554
+ const planJson = JSON.parse(fs17.readFileSync(planPath, "utf8"));
4159
4555
  payload.composition_plan = planJson;
4160
4556
  }
4161
4557
  if (opts.musicLengthMs) payload.music_length_ms = Number.parseInt(opts.musicLengthMs, 10);
@@ -4214,8 +4610,8 @@ Generated SFX: ${dest}`);
4214
4610
  }
4215
4611
 
4216
4612
  // src/providers/elevenlabs/commands/transcribe.ts
4217
- import fs17 from "fs";
4218
- import path20 from "path";
4613
+ import fs18 from "fs";
4614
+ import path21 from "path";
4219
4615
  function registerTranscribeCommand(parent) {
4220
4616
  parent.command("transcribe").description("Speech-to-text transcription with Scribe").requiredOption("--input <path>", "Audio or video file to transcribe").option(
4221
4617
  "--model <id>",
@@ -4238,7 +4634,7 @@ function registerTranscribeCommand(parent) {
4238
4634
  });
4239
4635
  const ext = opts.format === "json" ? "json" : opts.format === "srt" ? "srt" : opts.format === "vtt" ? "vtt" : "txt";
4240
4636
  const dir = getOutputDir();
4241
- const dest = opts.output ? path20.resolve(opts.output) : path20.join(dir, `elevenlabs_transcribe_${Date.now()}.${ext}`);
4637
+ const dest = opts.output ? path21.resolve(opts.output) : path21.join(dir, `elevenlabs_transcribe_${Date.now()}.${ext}`);
4242
4638
  let body;
4243
4639
  switch (opts.format) {
4244
4640
  case "json":
@@ -4253,8 +4649,8 @@ function registerTranscribeCommand(parent) {
4253
4649
  default:
4254
4650
  body = data.text ?? "";
4255
4651
  }
4256
- fs17.mkdirSync(path20.dirname(dest), { recursive: true });
4257
- fs17.writeFileSync(dest, body);
4652
+ fs18.mkdirSync(path21.dirname(dest), { recursive: true });
4653
+ fs18.writeFileSync(dest, body);
4258
4654
  logger.success(`Saved transcript: ${dest}`);
4259
4655
  if (data.language_code) {
4260
4656
  logger.info(
@@ -4500,8 +4896,8 @@ function registerElevenLabsCommands(program2) {
4500
4896
  }
4501
4897
 
4502
4898
  // src/providers/fal/commands/image.ts
4503
- import fs19 from "fs";
4504
- import path22 from "path";
4899
+ import fs20 from "fs";
4900
+ import path23 from "path";
4505
4901
 
4506
4902
  // src/providers/fal/models.ts
4507
4903
  var DEFAULT_FAL_BASE_URL = "https://queue.fal.run";
@@ -4521,8 +4917,8 @@ var FAL_DEFAULTS = {
4521
4917
 
4522
4918
  // src/providers/fal/run-helpers.ts
4523
4919
  init_errors();
4524
- import fs18 from "fs";
4525
- import path21 from "path";
4920
+ import fs19 from "fs";
4921
+ import path22 from "path";
4526
4922
  init_redact();
4527
4923
 
4528
4924
  // src/providers/fal/client.ts
@@ -4575,9 +4971,9 @@ var FalClient = class {
4575
4971
  apiKey;
4576
4972
  baseUrl;
4577
4973
  /** `path` is appended verbatim to the base queue URL, e.g. "/fal-ai/flux/schnell". */
4578
- async request(path57, opts = {}) {
4974
+ async request(path58, opts = {}) {
4579
4975
  const base = this.baseUrl.replace(/\/$/, "");
4580
- const suffix = path57.startsWith("/") ? path57 : `/${path57}`;
4976
+ const suffix = path58.startsWith("/") ? path58 : `/${path58}`;
4581
4977
  return this.send(base + suffix, opts);
4582
4978
  }
4583
4979
  /**
@@ -4606,11 +5002,11 @@ var FalClient = class {
4606
5002
  timeoutMs: opts.timeoutMs ?? 12e4
4607
5003
  });
4608
5004
  }
4609
- get(path57, logger) {
4610
- return this.request(path57, { method: "GET", logger });
5005
+ get(path58, logger) {
5006
+ return this.request(path58, { method: "GET", logger });
4611
5007
  }
4612
- post(path57, body, logger) {
4613
- return this.request(path57, { method: "POST", body, logger });
5008
+ post(path58, body, logger) {
5009
+ return this.request(path58, { method: "POST", body, logger });
4614
5010
  }
4615
5011
  getAbsolute(url, logger) {
4616
5012
  return this.requestAbsolute(url, { method: "GET", logger });
@@ -4759,7 +5155,7 @@ function extractMediaUrls(result) {
4759
5155
  function extFromUrl(url, fallback = ".bin") {
4760
5156
  try {
4761
5157
  const u = new URL(url);
4762
- const e = path21.extname(u.pathname);
5158
+ const e = path22.extname(u.pathname);
4763
5159
  return e.length > 0 ? e : fallback;
4764
5160
  } catch {
4765
5161
  return fallback;
@@ -4772,13 +5168,13 @@ async function downloadResultMedia(urls, outDir, requestId, logger) {
4772
5168
  if (!url) continue;
4773
5169
  const ext = extFromUrl(url);
4774
5170
  const filename = `fal-${requestId.slice(0, 8)}-${String(i + 1).padStart(2, "0")}${ext}`;
4775
- const filepath = path21.join(outDir, filename);
5171
+ const filepath = path22.join(outDir, filename);
4776
5172
  try {
4777
5173
  await downloadFile(url, filepath);
4778
5174
  saved.push(filepath);
4779
5175
  logger.success(`Saved ${filepath}`);
4780
5176
  } catch (e) {
4781
- fs18.rmSync(filepath, { force: true });
5177
+ fs19.rmSync(filepath, { force: true });
4782
5178
  logger.warn(`Download failed for ${redactUrl(url)}: ${e instanceof Error ? e.message : e}`);
4783
5179
  }
4784
5180
  }
@@ -4823,9 +5219,9 @@ function registerFalImageCommand(parent) {
4823
5219
  process.exit(1);
4824
5220
  }
4825
5221
  if (opts.output && saved[0]) {
4826
- const dest = path22.resolve(opts.output);
4827
- fs19.mkdirSync(path22.dirname(dest), { recursive: true });
4828
- fs19.copyFileSync(saved[0], dest);
5222
+ const dest = path23.resolve(opts.output);
5223
+ fs20.mkdirSync(path23.dirname(dest), { recursive: true });
5224
+ fs20.copyFileSync(saved[0], dest);
4829
5225
  logger.success(`Copied to ${dest}`);
4830
5226
  }
4831
5227
  const finalImages = finalizeGeneratedImages(saved, { ...opts, logger });
@@ -4980,9 +5376,9 @@ function registerFalCommands(program2) {
4980
5376
 
4981
5377
  // src/core/stdin-file.ts
4982
5378
  init_errors();
4983
- import fs20 from "fs";
4984
- import os2 from "os";
4985
- import path23 from "path";
5379
+ import fs21 from "fs";
5380
+ import os3 from "os";
5381
+ import path24 from "path";
4986
5382
 
4987
5383
  // src/core/limits.ts
4988
5384
  var FILES_API_MAX_BYTES = 2e9;
@@ -5087,7 +5483,7 @@ function trackTempDir(dir) {
5087
5483
  let done = false;
5088
5484
  const remove = () => {
5089
5485
  try {
5090
- fs20.rmSync(dir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
5486
+ fs21.rmSync(dir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
5091
5487
  } catch {
5092
5488
  }
5093
5489
  };
@@ -5119,9 +5515,9 @@ async function stdinToTempFile(opts = {}) {
5119
5515
  }
5120
5516
  const forcedExt = opts.ext ? normalizeExt(opts.ext) : void 0;
5121
5517
  const maxBytes = opts.maxBytes ?? MAX_STDIN_BYTES;
5122
- const dir = fs20.mkdtempSync(path23.join(os2.tmpdir(), "multix-stdin-"));
5518
+ const dir = fs21.mkdtempSync(path24.join(os3.tmpdir(), "multix-stdin-"));
5123
5519
  const cleanup = trackTempDir(dir);
5124
- const rawPath = path23.join(dir, "input");
5520
+ const rawPath = path24.join(dir, "input");
5125
5521
  const hint = setTimeout(() => {
5126
5522
  process.stderr.write(`Waiting for data on stdin (\`${flag} -\`)... press Ctrl+C to cancel
5127
5523
  `);
@@ -5130,7 +5526,7 @@ async function stdinToTempFile(opts = {}) {
5130
5526
  let head = Buffer.alloc(0);
5131
5527
  let size = 0;
5132
5528
  try {
5133
- const fd = fs20.openSync(rawPath, "w", 384);
5529
+ const fd = fs21.openSync(rawPath, "w", 384);
5134
5530
  try {
5135
5531
  for await (const chunk of input) {
5136
5532
  clearTimeout(hint);
@@ -5142,17 +5538,17 @@ async function stdinToTempFile(opts = {}) {
5142
5538
  head = Buffer.concat([head, buf.subarray(0, HEAD_BYTES - head.length)]);
5143
5539
  }
5144
5540
  let offset = 0;
5145
- while (offset < buf.length) offset += fs20.writeSync(fd, buf, offset);
5541
+ while (offset < buf.length) offset += fs21.writeSync(fd, buf, offset);
5146
5542
  size += buf.length;
5147
5543
  }
5148
5544
  } finally {
5149
5545
  clearTimeout(hint);
5150
- fs20.closeSync(fd);
5546
+ fs21.closeSync(fd);
5151
5547
  }
5152
5548
  if (size === 0) throw new ValidationError(`\`${flag} -\` was given but stdin was empty`);
5153
5549
  const detectedExt = forcedExt ?? detectExtension(head);
5154
- const finalPath = path23.join(dir, `stdin${detectedExt ?? ".bin"}`);
5155
- fs20.renameSync(rawPath, finalPath);
5550
+ const finalPath = path24.join(dir, `stdin${detectedExt ?? ".bin"}`);
5551
+ fs21.renameSync(rawPath, finalPath);
5156
5552
  return { path: finalPath, size, detected: detectedExt !== void 0, cleanup };
5157
5553
  } catch (error) {
5158
5554
  cleanup();
@@ -5170,7 +5566,7 @@ async function resolveFileInputs(files, opts = {}) {
5170
5566
  throw new ValidationError(`--stdin-ext only applies when ${flag} includes \`-\``);
5171
5567
  }
5172
5568
  for (const f of files) {
5173
- if (f !== STDIN_ARG && !fs20.existsSync(f)) throw new ValidationError(`File not found: ${f}`);
5569
+ if (f !== STDIN_ARG && !fs21.existsSync(f)) throw new ValidationError(`File not found: ${f}`);
5174
5570
  }
5175
5571
  if (stdinCount === 0) {
5176
5572
  return { inputs: files.map((f) => ({ path: f, label: f })), cleanup: () => {
@@ -5458,12 +5854,12 @@ async function withGeminiKey(fn, opts = {}) {
5458
5854
  }
5459
5855
 
5460
5856
  // src/providers/gemini/local-files.ts
5461
- import fs21 from "fs";
5462
- import path25 from "path";
5857
+ import fs22 from "fs";
5858
+ import path26 from "path";
5463
5859
  init_errors();
5464
5860
 
5465
5861
  // src/providers/gemini/task-resolver.ts
5466
- import path24 from "path";
5862
+ import path25 from "path";
5467
5863
  var MIME_MAP = {
5468
5864
  // Audio
5469
5865
  ".mp3": "audio/mp3",
@@ -5504,7 +5900,7 @@ var MIME_MAP = {
5504
5900
  ".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation"
5505
5901
  };
5506
5902
  function getMimeType(filePath) {
5507
- const ext = path24.extname(filePath).toLowerCase();
5903
+ const ext = path25.extname(filePath).toLowerCase();
5508
5904
  return MIME_MAP[ext] ?? "application/octet-stream";
5509
5905
  }
5510
5906
 
@@ -5524,10 +5920,10 @@ function minUploadBytesPerSec() {
5524
5920
  }
5525
5921
  async function uploadLocalFile(filePath, opts = {}) {
5526
5922
  const bytesPerSec = minUploadBytesPerSec();
5527
- const blob = await fs21.openAsBlob(filePath);
5923
+ const blob = await fs22.openAsBlob(filePath);
5528
5924
  const source = {
5529
5925
  blob,
5530
- displayName: path25.basename(filePath),
5926
+ displayName: path26.basename(filePath),
5531
5927
  mimeType: getMimeType(filePath),
5532
5928
  label: filePath
5533
5929
  };
@@ -5535,12 +5931,12 @@ async function uploadLocalFile(filePath, opts = {}) {
5535
5931
  return opts.apiKey ? upload(opts.apiKey) : withGeminiKey(upload, { logger: opts.logger });
5536
5932
  }
5537
5933
  function inlinePart(filePath) {
5538
- const data = fs21.readFileSync(filePath).toString("base64");
5934
+ const data = fs22.readFileSync(filePath).toString("base64");
5539
5935
  const mimeType = getMimeType(filePath);
5540
5936
  return { inlineData: { mimeType, data } };
5541
5937
  }
5542
5938
  function shouldUseFileApi(filePath) {
5543
- const stat = fs21.statSync(filePath);
5939
+ const stat = fs22.statSync(filePath);
5544
5940
  return stat.size > INLINE_MAX_BYTES;
5545
5941
  }
5546
5942
 
@@ -5591,13 +5987,13 @@ function getDefaultModel(task) {
5591
5987
  }
5592
5988
 
5593
5989
  // src/providers/gemini/result-formatter.ts
5594
- import fs22 from "fs";
5595
- import path26 from "path";
5990
+ import fs23 from "fs";
5991
+ import path27 from "path";
5596
5992
  function formatResults(results, format, outputPath) {
5597
5993
  const content = renderFormat(results, format);
5598
5994
  if (outputPath) {
5599
- fs22.mkdirSync(path26.dirname(path26.resolve(outputPath)), { recursive: true });
5600
- fs22.writeFileSync(outputPath, content, "utf8");
5995
+ fs23.mkdirSync(path27.dirname(path27.resolve(outputPath)), { recursive: true });
5996
+ fs23.writeFileSync(outputPath, content, "utf8");
5601
5997
  } else {
5602
5998
  process.stdout.write(content);
5603
5999
  }
@@ -5781,8 +6177,8 @@ function registerExtractCommand(parent) {
5781
6177
  init_errors();
5782
6178
 
5783
6179
  // src/providers/gemini/generators/speech.ts
5784
- import fs23 from "fs";
5785
- import path27 from "path";
6180
+ import fs24 from "fs";
6181
+ import path28 from "path";
5786
6182
 
5787
6183
  // src/providers/gemini/voices.ts
5788
6184
  var GEMINI_TTS_VOICES = [
@@ -5954,12 +6350,12 @@ function extractWavData(wav) {
5954
6350
  }
5955
6351
  function saveSpeech(outBytes, outputFormat, output, logger) {
5956
6352
  const outDir = getOutputDir();
5957
- const dest = path27.join(outDir, `gemini_speech_${Date.now()}.${outputFormat}`);
5958
- fs23.writeFileSync(dest, outBytes);
6353
+ const dest = path28.join(outDir, `gemini_speech_${Date.now()}.${outputFormat}`);
6354
+ fs24.writeFileSync(dest, outBytes);
5959
6355
  logger?.success(`Saved: ${dest} (${(outBytes.length / 1024).toFixed(1)} KB)`);
5960
6356
  if (output) {
5961
- fs23.mkdirSync(path27.dirname(path27.resolve(output)), { recursive: true });
5962
- fs23.copyFileSync(dest, output);
6357
+ fs24.mkdirSync(path28.dirname(path28.resolve(output)), { recursive: true });
6358
+ fs24.copyFileSync(dest, output);
5963
6359
  logger?.success(`Copied to: ${output}`);
5964
6360
  }
5965
6361
  return dest;
@@ -6072,24 +6468,24 @@ function collectSpeakers(value, prev) {
6072
6468
 
6073
6469
  // src/providers/gemini/commands/generate-video.ts
6074
6470
  init_errors();
6075
- import fs25 from "fs";
6076
- import path28 from "path";
6471
+ import fs26 from "fs";
6472
+ import path29 from "path";
6077
6473
 
6078
6474
  // src/providers/gemini/veo.ts
6079
- import fs24 from "fs";
6475
+ import fs25 from "fs";
6080
6476
  init_errors();
6081
6477
  var API_HOST = "generativelanguage.googleapis.com";
6082
6478
  var BASE2 = `https://${API_HOST}/v1beta`;
6083
6479
  var VEO_IMAGE_TYPES = /* @__PURE__ */ new Set(["image/jpeg", "image/png", "image/webp"]);
6084
6480
  var VEO_TIMEOUT_MS = 10 * 60 * 1e3;
6085
6481
  function veoImageFromFile(imgPath) {
6086
- if (!fs24.existsSync(imgPath)) throw new ValidationError(`Image not found: ${imgPath}`);
6482
+ if (!fs25.existsSync(imgPath)) throw new ValidationError(`Image not found: ${imgPath}`);
6087
6483
  const detected = getMimeType(imgPath);
6088
6484
  const mimeType = detected === "application/octet-stream" ? "image/jpeg" : detected;
6089
6485
  if (!VEO_IMAGE_TYPES.has(mimeType)) {
6090
6486
  throw new ValidationError(`Veo accepts JPEG, PNG or WebP images: ${imgPath} (${mimeType})`);
6091
6487
  }
6092
- return { bytesBase64Encoded: fs24.readFileSync(imgPath).toString("base64"), mimeType };
6488
+ return { bytesBase64Encoded: fs25.readFileSync(imgPath).toString("base64"), mimeType };
6093
6489
  }
6094
6490
  function veoFramesFromPaths(paths = []) {
6095
6491
  if (paths.length > 2) {
@@ -6215,7 +6611,7 @@ function registerGenerateVideoCommand(parent) {
6215
6611
  logger.debug(`Submitting video generation with ${model}...`);
6216
6612
  logger.info("This may take 30s\u20136 minutes. Veo requires billing.");
6217
6613
  const frames = veoFramesFromPaths(opts.referenceImages);
6218
- const dest = path28.join(getOutputDir(), `veo_generated_${Date.now()}.mp4`);
6614
+ const dest = path29.join(getOutputDir(), `veo_generated_${Date.now()}.mp4`);
6219
6615
  const finalOp = await runVeo(
6220
6616
  model,
6221
6617
  {
@@ -6229,8 +6625,8 @@ function registerGenerateVideoCommand(parent) {
6229
6625
  );
6230
6626
  logger.success(`Saved video: ${dest}`);
6231
6627
  if (opts.output) {
6232
- fs25.mkdirSync(path28.dirname(path28.resolve(opts.output)), { recursive: true });
6233
- fs25.copyFileSync(dest, opts.output);
6628
+ fs26.mkdirSync(path29.dirname(path29.resolve(opts.output)), { recursive: true });
6629
+ fs26.copyFileSync(dest, opts.output);
6234
6630
  logger.success(`Copied to: ${opts.output}`);
6235
6631
  }
6236
6632
  await maybeDownloadThumb(finalOp.response, dest, {
@@ -6246,8 +6642,8 @@ Generated video: ${dest}`);
6246
6642
 
6247
6643
  // src/providers/gemini/commands/generate.ts
6248
6644
  init_errors();
6249
- import fs26 from "fs";
6250
- import path29 from "path";
6645
+ import fs27 from "fs";
6646
+ import path30 from "path";
6251
6647
  function registerGenerateCommand(parent) {
6252
6648
  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(
6253
6649
  async (opts) => {
@@ -6295,8 +6691,8 @@ function registerGenerateCommand(parent) {
6295
6691
  const outDir = getOutputDir();
6296
6692
  const ext = img.mimeType.includes("png") ? "png" : "jpg";
6297
6693
  const fname = `gemini_generated_${Date.now()}_${i}.${ext}`;
6298
- const dest = path29.join(outDir, fname);
6299
- fs26.writeFileSync(dest, Buffer.from(img.data, "base64"));
6694
+ const dest = path30.join(outDir, fname);
6695
+ fs27.writeFileSync(dest, Buffer.from(img.data, "base64"));
6300
6696
  savedFiles.push(dest);
6301
6697
  logger.success(`Saved: ${dest}`);
6302
6698
  }
@@ -6311,8 +6707,8 @@ function registerGenerateCommand(parent) {
6311
6707
  process.exit(1);
6312
6708
  }
6313
6709
  if (opts.output && savedFiles[0]) {
6314
- fs26.mkdirSync(path29.dirname(path29.resolve(opts.output)), { recursive: true });
6315
- fs26.copyFileSync(savedFiles[0], opts.output);
6710
+ fs27.mkdirSync(path30.dirname(path30.resolve(opts.output)), { recursive: true });
6711
+ fs27.copyFileSync(savedFiles[0], opts.output);
6316
6712
  logger.success(`Copied to: ${opts.output}`);
6317
6713
  }
6318
6714
  const finalImages = finalizeGeneratedImages(savedFiles, { ...opts, logger });
@@ -6324,8 +6720,8 @@ Generated ${finalImages.length} image(s):`);
6324
6720
  }
6325
6721
 
6326
6722
  // src/providers/gemini/commands/image-to-image.ts
6327
- import fs27 from "fs";
6328
- import path30 from "path";
6723
+ import fs28 from "fs";
6724
+ import path31 from "path";
6329
6725
  var SOFT_LIMIT_BYTES = 5 * 1024 * 1024;
6330
6726
  var HARD_LIMIT_BYTES = 7 * 1024 * 1024;
6331
6727
  function registerGeminiImageToImageCommand(parent) {
@@ -6388,14 +6784,14 @@ function registerGeminiImageToImageCommand(parent) {
6388
6784
  const img = images[i];
6389
6785
  if (!img) continue;
6390
6786
  const ext = img.mimeType.includes("png") ? "png" : "jpg";
6391
- const dest = path30.join(outDir, `gemini-i2i-${ts}-${i + 1}.${ext}`);
6392
- fs27.writeFileSync(dest, Buffer.from(img.data, "base64"));
6787
+ const dest = path31.join(outDir, `gemini-i2i-${ts}-${i + 1}.${ext}`);
6788
+ fs28.writeFileSync(dest, Buffer.from(img.data, "base64"));
6393
6789
  saved.push(dest);
6394
6790
  logger.success(`Saved: ${dest}`);
6395
6791
  }
6396
6792
  if (opts.output && saved[0]) {
6397
- fs27.mkdirSync(path30.dirname(path30.resolve(opts.output)), { recursive: true });
6398
- fs27.copyFileSync(saved[0], opts.output);
6793
+ fs28.mkdirSync(path31.dirname(path31.resolve(opts.output)), { recursive: true });
6794
+ fs28.copyFileSync(saved[0], opts.output);
6399
6795
  logger.success(`Copied to: ${opts.output}`);
6400
6796
  }
6401
6797
  const finalImages = finalizeGeneratedImages(saved, { ...opts, logger });
@@ -6423,7 +6819,7 @@ function sniffImageMime(buf, url) {
6423
6819
  if (buf.length >= 12 && buf.slice(0, 4).toString() === "RIFF" && buf.slice(8, 12).toString() === "WEBP")
6424
6820
  return "image/webp";
6425
6821
  }
6426
- const ext = path30.extname(new URL(url).pathname).toLowerCase();
6822
+ const ext = path31.extname(new URL(url).pathname).toLowerCase();
6427
6823
  if (ext === ".png") return "image/png";
6428
6824
  if (ext === ".webp") return "image/webp";
6429
6825
  if (ext === ".gif") return "image/gif";
@@ -6432,8 +6828,8 @@ function sniffImageMime(buf, url) {
6432
6828
 
6433
6829
  // src/providers/gemini/commands/image-to-video.ts
6434
6830
  init_errors();
6435
- import fs28 from "fs";
6436
- import path31 from "path";
6831
+ import fs29 from "fs";
6832
+ import path32 from "path";
6437
6833
  function registerGeminiImageToVideoCommand(parent) {
6438
6834
  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(
6439
6835
  async (imagePath, opts) => {
@@ -6453,7 +6849,7 @@ function registerGeminiImageToVideoCommand(parent) {
6453
6849
  logger.info(
6454
6850
  "Submitting image-to-video. This may take 30s\u20136 minutes. Veo requires billing."
6455
6851
  );
6456
- const dest = path31.join(getOutputDir(), `veo_i2v_${Date.now()}.mp4`);
6852
+ const dest = path32.join(getOutputDir(), `veo_i2v_${Date.now()}.mp4`);
6457
6853
  const finalOp = await runVeo(
6458
6854
  model,
6459
6855
  {
@@ -6468,8 +6864,8 @@ function registerGeminiImageToVideoCommand(parent) {
6468
6864
  );
6469
6865
  logger.success(`Saved video: ${dest}`);
6470
6866
  if (opts.output) {
6471
- fs28.mkdirSync(path31.dirname(path31.resolve(opts.output)), { recursive: true });
6472
- fs28.copyFileSync(dest, opts.output);
6867
+ fs29.mkdirSync(path32.dirname(path32.resolve(opts.output)), { recursive: true });
6868
+ fs29.copyFileSync(dest, opts.output);
6473
6869
  logger.success(`Copied to: ${opts.output}`);
6474
6870
  }
6475
6871
  await maybeDownloadThumb(finalOp.response, dest, {
@@ -6674,8 +7070,8 @@ var HEYGEN_OPERATIONS = {
6674
7070
  };
6675
7071
 
6676
7072
  // src/providers/heygen/node-helpers.ts
6677
- import fs29 from "fs";
6678
- import path32 from "path";
7073
+ import fs30 from "fs";
7074
+ import path33 from "path";
6679
7075
  init_errors();
6680
7076
  init_redact();
6681
7077
 
@@ -6788,12 +7184,12 @@ function describePayload(data) {
6788
7184
  );
6789
7185
  return (text ?? String(data)).slice(0, 500);
6790
7186
  }
6791
- function requireJobId(data, field, path57) {
7187
+ function requireJobId(data, field, path58) {
6792
7188
  const value = data?.[field];
6793
7189
  const ok2 = Array.isArray(value) ? value.length > 0 && value.every((v) => typeof v === "string" && v.length > 0) : typeof value === "string" && value.length > 0;
6794
7190
  if (!ok2) {
6795
7191
  throw new ProviderError(
6796
- `HeyGen ${path57} returned no ${field}; a job may still have been created, so check the HeyGen dashboard. Response: ${describePayload(data)}`,
7192
+ `HeyGen ${path58} returned no ${field}; a job may still have been created, so check the HeyGen dashboard. Response: ${describePayload(data)}`,
6797
7193
  "heygen"
6798
7194
  );
6799
7195
  }
@@ -6818,8 +7214,8 @@ var HeygenClient = class {
6818
7214
  this.newIdempotencyKey = opts.newIdempotencyKey ?? (() => crypto.randomUUID());
6819
7215
  }
6820
7216
  /** Build an absolute URL for a v3 path plus query parameters. */
6821
- url(path57, query) {
6822
- const suffix = path57.startsWith("/") ? path57 : `/${path57}`;
7217
+ url(path58, query) {
7218
+ const suffix = path58.startsWith("/") ? path58 : `/${path58}`;
6823
7219
  const params = new URLSearchParams();
6824
7220
  for (const [k, v] of Object.entries(query ?? {})) {
6825
7221
  if (v !== void 0 && v !== "") params.set(k, String(v));
@@ -6841,10 +7237,10 @@ var HeygenClient = class {
6841
7237
  * `request_in_progress` are retried only when an Idempotency-Key makes the
6842
7238
  * replay safe. GET requests are left to the caller's polling loop.
6843
7239
  */
6844
- async send(path57, opts = {}) {
7240
+ async send(path58, opts = {}) {
6845
7241
  const method = opts.method ?? "GET";
6846
7242
  const key = typeof opts.idempotencyKey === "string" ? opts.idempotencyKey : opts.idempotencyKey ? this.newIdempotencyKey() : void 0;
6847
- const url = this.url(path57, opts.query);
7243
+ const url = this.url(path58, opts.query);
6848
7244
  for (let attempt = 1; ; attempt++) {
6849
7245
  try {
6850
7246
  return await fetchJson({
@@ -6878,18 +7274,18 @@ var HeygenClient = class {
6878
7274
  return void 0;
6879
7275
  }
6880
7276
  /** Send and unwrap `{ data }`. A 2xx body carrying `error` is treated as a failure. */
6881
- async request(path57, opts = {}) {
6882
- const json = await this.send(path57, opts);
6883
- return unwrapData(json, path57);
7277
+ async request(path58, opts = {}) {
7278
+ const json = await this.send(path58, opts);
7279
+ return unwrapData(json, path58);
6884
7280
  }
6885
- async page(path57, query) {
6886
- const json = await this.send(path57, {
7281
+ async page(path58, query) {
7282
+ const json = await this.send(path58, {
6887
7283
  query
6888
7284
  });
6889
- const items = unwrapData(json, path57);
7285
+ const items = unwrapData(json, path58);
6890
7286
  if (!Array.isArray(items)) {
6891
7287
  throw new ProviderError(
6892
- `Unexpected HeyGen response from ${path57}: data is not a list`,
7288
+ `Unexpected HeyGen response from ${path58}: data is not a list`,
6893
7289
  "heygen"
6894
7290
  );
6895
7291
  }
@@ -6907,9 +7303,9 @@ var HeygenClient = class {
6907
7303
  // ── Video Agent ───────────────────────────────────────────────────────
6908
7304
  /** POST /v3/video-agents. No Idempotency-Key: the endpoint does not document one. */
6909
7305
  async createVideoAgent(body) {
6910
- const path57 = "/v3/video-agents";
6911
- const data = await this.request(path57, { method: "POST", body });
6912
- return requireJobId(data, "session_id", path57);
7306
+ const path58 = "/v3/video-agents";
7307
+ const data = await this.request(path58, { method: "POST", body });
7308
+ return requireJobId(data, "session_id", path58);
6913
7309
  }
6914
7310
  async getVideoAgentSession(sessionId, opts = {}) {
6915
7311
  return this.request(`/v3/video-agents/${seg(sessionId, "session id")}`, opts);
@@ -6919,36 +7315,36 @@ var HeygenClient = class {
6919
7315
  }
6920
7316
  // ── Videos ────────────────────────────────────────────────────────────
6921
7317
  async createVideo(body, idempotencyKey = true) {
6922
- const path57 = "/v3/videos";
6923
- const data = await this.request(path57, {
7318
+ const path58 = "/v3/videos";
7319
+ const data = await this.request(path58, {
6924
7320
  method: "POST",
6925
7321
  body,
6926
7322
  idempotencyKey
6927
7323
  });
6928
- return requireJobId(data, "video_id", path57);
7324
+ return requireJobId(data, "video_id", path58);
6929
7325
  }
6930
7326
  async getVideo(videoId, opts = {}) {
6931
7327
  return this.request(`/v3/videos/${seg(videoId, "video id")}`, opts);
6932
7328
  }
6933
7329
  /** POST /v3/templates/{id}. Returns the created video's `VideoDetail`. */
6934
7330
  async generateFromTemplate(templateId, body, idempotencyKey = true) {
6935
- const path57 = `/v3/templates/${seg(templateId, "template id")}`;
6936
- const data = await this.request(path57, {
7331
+ const path58 = `/v3/templates/${seg(templateId, "template id")}`;
7332
+ const data = await this.request(path58, {
6937
7333
  method: "POST",
6938
7334
  body,
6939
7335
  idempotencyKey
6940
7336
  });
6941
- return requireJobId(data, "id", path57);
7337
+ return requireJobId(data, "id", path58);
6942
7338
  }
6943
7339
  // ── HeyGen Video model ────────────────────────────────────────────────
6944
7340
  async createModelVideo(body, idempotencyKey = true) {
6945
- const path57 = "/v3/models/videos";
6946
- const data = await this.request(path57, {
7341
+ const path58 = "/v3/models/videos";
7342
+ const data = await this.request(path58, {
6947
7343
  method: "POST",
6948
7344
  body,
6949
7345
  idempotencyKey
6950
7346
  });
6951
- return requireJobId(data, "video_id", path57);
7347
+ return requireJobId(data, "video_id", path58);
6952
7348
  }
6953
7349
  async getModelVideo(videoId, opts = {}) {
6954
7350
  return this.request(`/v3/models/videos/${seg(videoId, "video id")}`, opts);
@@ -6970,13 +7366,13 @@ var HeygenClient = class {
6970
7366
  }
6971
7367
  // ── Translation & lipsync ─────────────────────────────────────────────
6972
7368
  async createVideoTranslation(body, idempotencyKey = true) {
6973
- const path57 = "/v3/video-translations";
6974
- const data = await this.request(path57, {
7369
+ const path58 = "/v3/video-translations";
7370
+ const data = await this.request(path58, {
6975
7371
  method: "POST",
6976
7372
  body,
6977
7373
  idempotencyKey
6978
7374
  });
6979
- return requireJobId(data, "video_translation_ids", path57);
7375
+ return requireJobId(data, "video_translation_ids", path58);
6980
7376
  }
6981
7377
  async getVideoTranslation(id, opts = {}) {
6982
7378
  return this.request(`/v3/video-translations/${seg(id, "video translation id")}`, opts);
@@ -6989,13 +7385,13 @@ var HeygenClient = class {
6989
7385
  return data.languages.filter((l) => typeof l === "string");
6990
7386
  }
6991
7387
  async createLipsync(body, idempotencyKey = true) {
6992
- const path57 = "/v3/lipsyncs";
6993
- const data = await this.request(path57, {
7388
+ const path58 = "/v3/lipsyncs";
7389
+ const data = await this.request(path58, {
6994
7390
  method: "POST",
6995
7391
  body,
6996
7392
  idempotencyKey
6997
7393
  });
6998
- return requireJobId(data, "lipsync_id", path57);
7394
+ return requireJobId(data, "lipsync_id", path58);
6999
7395
  }
7000
7396
  async getLipsync(id, opts = {}) {
7001
7397
  return this.request(`/v3/lipsyncs/${seg(id, "lipsync id")}`, opts);
@@ -7060,9 +7456,9 @@ var HeygenClient = class {
7060
7456
  }
7061
7457
  }
7062
7458
  };
7063
- function unwrapData(json, path57) {
7459
+ function unwrapData(json, path58) {
7064
7460
  if (json === null || typeof json !== "object") {
7065
- throw new ProviderError(`Unexpected HeyGen response from ${path57}: not a JSON object`, "heygen");
7461
+ throw new ProviderError(`Unexpected HeyGen response from ${path58}: not a JSON object`, "heygen");
7066
7462
  }
7067
7463
  const record = json;
7068
7464
  const error = record.error;
@@ -7074,7 +7470,7 @@ function unwrapData(json, path57) {
7074
7470
  );
7075
7471
  }
7076
7472
  if (!("data" in record) || record.data === null || record.data === void 0) {
7077
- throw new ProviderError(`Unexpected HeyGen response from ${path57}: missing data`, "heygen");
7473
+ throw new ProviderError(`Unexpected HeyGen response from ${path58}: missing data`, "heygen");
7078
7474
  }
7079
7475
  return record.data;
7080
7476
  }
@@ -7287,7 +7683,6 @@ function resumeCommand2(kind, id, opts = {}) {
7287
7683
 
7288
7684
  // src/providers/heygen/request-builders.ts
7289
7685
  init_errors();
7290
- init_redact();
7291
7686
  function oneOf(value, allowed, flag) {
7292
7687
  if (value === void 0) return void 0;
7293
7688
  if (!allowed.includes(value)) {
@@ -7316,23 +7711,6 @@ function isHttpsUrl(url) {
7316
7711
  return false;
7317
7712
  }
7318
7713
  }
7319
- var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "[::1]"]);
7320
- function checkBaseUrl(raw, variable) {
7321
- const value = raw.trim();
7322
- let url;
7323
- try {
7324
- url = new URL(value);
7325
- } catch {
7326
- throw new ConfigError(`${variable} must be an absolute https:// URL`);
7327
- }
7328
- const loopbackHttp = url.protocol === "http:" && LOOPBACK_HOSTS.has(url.hostname);
7329
- if (url.protocol !== "https:" && !loopbackHttp) {
7330
- throw new ConfigError(
7331
- `${variable} must use https:// (got ${redactUrl(value)}); plain http:// is only allowed for localhost, 127.0.0.1, or [::1]`
7332
- );
7333
- }
7334
- return value;
7335
- }
7336
7714
  function classifyMediaRef(value) {
7337
7715
  const v = value.trim();
7338
7716
  if (!v) throw new ValidationError("Media reference must not be empty");
@@ -7603,17 +7981,17 @@ function createHeygenClientFromEnv() {
7603
7981
  return new HeygenClient({ apiKey, baseUrl: checkBaseUrl(baseUrl, "HEYGEN_BASE_URL") });
7604
7982
  }
7605
7983
  async function uploadLocalFile2(client, filePath, logger) {
7606
- const abs = path32.resolve(filePath);
7984
+ const abs = path33.resolve(filePath);
7607
7985
  let stat;
7608
7986
  try {
7609
- stat = fs29.statSync(abs);
7987
+ stat = fs30.statSync(abs);
7610
7988
  } catch {
7611
7989
  throw new ValidationError(
7612
7990
  `File not found: ${filePath}. Use an https:// URL, asset:<asset_id>, or an existing file.`
7613
7991
  );
7614
7992
  }
7615
7993
  if (!stat.isFile()) throw new ValidationError(`Not a file: ${filePath}`);
7616
- const ext = path32.extname(abs).slice(1).toLowerCase();
7994
+ const ext = path33.extname(abs).slice(1).toLowerCase();
7617
7995
  const mimeType = ASSET_MIME_BY_EXT[ext];
7618
7996
  if (!mimeType) {
7619
7997
  throw new ValidationError(
@@ -7625,10 +8003,10 @@ async function uploadLocalFile2(client, filePath, logger) {
7625
8003
  `${filePath} is ${(stat.size / 1024 / 1024).toFixed(1)} MB; HeyGen asset upload accepts at most 32 MB. Host it at a public https:// URL instead.`
7626
8004
  );
7627
8005
  }
7628
- logger.info(`Uploading ${path32.basename(abs)} to HeyGen assets...`);
8006
+ logger.info(`Uploading ${path33.basename(abs)} to HeyGen assets...`);
7629
8007
  const res = await client.uploadAsset({
7630
- data: new Uint8Array(fs29.readFileSync(abs)),
7631
- filename: path32.basename(abs),
8008
+ data: new Uint8Array(fs30.readFileSync(abs)),
8009
+ filename: path33.basename(abs),
7632
8010
  mimeType
7633
8011
  });
7634
8012
  logger.debug(`Uploaded asset ${res.asset_id} (${res.mime_type}, ${res.size_bytes} bytes)`);
@@ -7672,7 +8050,7 @@ var MEDIA_EXTS = /* @__PURE__ */ new Set([...VIDEO_EXTS, ...AUDIO_EXTS]);
7672
8050
  var OUTPUT_EXTS = /* @__PURE__ */ new Set([...MEDIA_EXTS, "srt", "vtt"]);
7673
8051
  function inferExtension(url, fallback) {
7674
8052
  try {
7675
- const ext = path32.extname(new URL(url).pathname).slice(1).toLowerCase();
8053
+ const ext = path33.extname(new URL(url).pathname).slice(1).toLowerCase();
7676
8054
  return OUTPUT_EXTS.has(ext) ? ext : fallback;
7677
8055
  } catch {
7678
8056
  return fallback;
@@ -7710,11 +8088,11 @@ async function downloadOutput(url, dest, resumeHint) {
7710
8088
  }
7711
8089
  function defaultOutputPath(state, ext) {
7712
8090
  const lang = state.outputLanguage ? `-${safeSlug(state.outputLanguage)}` : "";
7713
- return path32.join(getOutputDir(), `heygen-${state.kind}${lang}-${safeSlug(state.id)}.${ext}`);
8091
+ return path33.join(getOutputDir(), `heygen-${state.kind}${lang}-${safeSlug(state.id)}.${ext}`);
7714
8092
  }
7715
8093
  function statOrUndefined(p) {
7716
8094
  try {
7717
- return fs29.statSync(p);
8095
+ return fs30.statSync(p);
7718
8096
  } catch {
7719
8097
  return void 0;
7720
8098
  }
@@ -7723,29 +8101,29 @@ function isDirectoryOutput2(output, target) {
7723
8101
  if (/[\\/]$/.test(output)) return true;
7724
8102
  const stat = statOrUndefined(target);
7725
8103
  if (stat) return stat.isDirectory();
7726
- return path32.extname(target) === "";
8104
+ return path33.extname(target) === "";
7727
8105
  }
7728
8106
  function withMediaExt(file, ext) {
7729
- const given = path32.extname(file).slice(1).toLowerCase();
8107
+ const given = path33.extname(file).slice(1).toLowerCase();
7730
8108
  const mismatch = VIDEO_EXTS.has(given) && AUDIO_EXTS.has(ext) || AUDIO_EXTS.has(given) && VIDEO_EXTS.has(ext);
7731
8109
  return mismatch ? `${file.slice(0, -(given.length + 1))}.${ext}` : file;
7732
8110
  }
7733
8111
  function outputPathFor(state, ext, output, count) {
7734
8112
  if (!output) return defaultOutputPath(state, ext);
7735
- const target = path32.resolve(output);
7736
- const givenExt = path32.extname(target);
8113
+ const target = path33.resolve(output);
8114
+ const givenExt = path33.extname(target);
7737
8115
  const isTemplate = count > 1 && MEDIA_EXTS.has(givenExt.slice(1).toLowerCase());
7738
8116
  if (isDirectoryOutput2(output, target) || count > 1 && !isTemplate) {
7739
- fs29.mkdirSync(target, { recursive: true });
7740
- return path32.join(target, path32.basename(defaultOutputPath(state, ext)));
8117
+ fs30.mkdirSync(target, { recursive: true });
8118
+ return path33.join(target, path33.basename(defaultOutputPath(state, ext)));
7741
8119
  }
7742
8120
  if (!isTemplate) return withMediaExt(target, ext);
7743
- const base = path32.basename(target, givenExt);
8121
+ const base = path33.basename(target, givenExt);
7744
8122
  const suffix = safeSlug(state.outputLanguage ?? state.id);
7745
- return withMediaExt(path32.join(path32.dirname(target), `${base}-${suffix}${givenExt}`), ext);
8123
+ return withMediaExt(path33.join(path33.dirname(target), `${base}-${suffix}${givenExt}`), ext);
7746
8124
  }
7747
8125
  function claimPath(dest, state, used) {
7748
- const ext = path32.extname(dest);
8126
+ const ext = path33.extname(dest);
7749
8127
  const stem = dest.slice(0, dest.length - ext.length);
7750
8128
  let candidate = dest;
7751
8129
  for (let n = 1; used.has(candidate.toLowerCase()); n++) {
@@ -7756,7 +8134,7 @@ function claimPath(dest, state, used) {
7756
8134
  return candidate;
7757
8135
  }
7758
8136
  function captionPathFor(dest, capExt) {
7759
- const stem = path32.join(path32.dirname(dest), path32.basename(dest, path32.extname(dest)));
8137
+ const stem = path33.join(path33.dirname(dest), path33.basename(dest, path33.extname(dest)));
7760
8138
  const capDest = `${stem}.${capExt}`;
7761
8139
  return capDest.toLowerCase() === dest.toLowerCase() ? `${stem}.captions.${capExt}` : capDest;
7762
8140
  }
@@ -8134,7 +8512,7 @@ function registerHeygenModelVideoCommand(parent) {
8134
8512
 
8135
8513
  // src/providers/heygen/commands/run.ts
8136
8514
  init_errors();
8137
- import path33 from "path";
8515
+ import path34 from "path";
8138
8516
  function extractJobIds(operation, data) {
8139
8517
  const op = HEYGEN_OPERATIONS[operation];
8140
8518
  if (!op) return [];
@@ -8172,7 +8550,7 @@ function registerHeygenRunCommand(parent) {
8172
8550
  return;
8173
8551
  }
8174
8552
  const ext = inferExtension(audioUrl, "mp3");
8175
- const dest = opts.output ? path33.resolve(opts.output) : path33.join(getOutputDir(), `heygen-speech-${Date.now()}.${ext}`);
8553
+ const dest = opts.output ? path34.resolve(opts.output) : path34.join(getOutputDir(), `heygen-speech-${Date.now()}.${ext}`);
8176
8554
  await downloadOutput(audioUrl, dest, "Re-run the command to generate a fresh URL.");
8177
8555
  console.log(`Saved: ${dest}`);
8178
8556
  return;
@@ -8302,7 +8680,7 @@ function registerHeygenTranslateCommand(parent) {
8302
8680
  }
8303
8681
 
8304
8682
  // src/providers/heygen/commands/tts.ts
8305
- import path34 from "path";
8683
+ import path35 from "path";
8306
8684
  init_redact();
8307
8685
  function registerHeygenTtsCommand(parent) {
8308
8686
  parent.command("tts <text>").description("Text-to-speech with a HeyGen catalog voice (1-5000 characters)").requiredOption("--voice <voice-id>", "Voice ID from `heygen voices`").option("--ssml", "Treat the text as SSML").option("--speed <n>", "Speed multiplier 0.5-2.0").option("--language <code>", "Base language code, e.g. en (auto-detected when omitted)").option("--locale <tag>", "Locale tag for Starfish voices, e.g. zh-HK").option("--engine <e>", "starfish | orca | elevenlabs (defaults to the voice's engine)").option("--output <path>", "Output audio path").option("--no-download", "Print the audio URL instead of downloading").option("--json", "Print a JSON summary").option("-v, --verbose", "Verbose logging").action(async (text, opts) => {
@@ -8333,7 +8711,7 @@ function registerHeygenTtsCommand(parent) {
8333
8711
  }
8334
8712
  const ext = inferExtension(res.audio_url, "mp3");
8335
8713
  const name = `heygen-tts-${res.request_id ?? Date.now()}.${ext}`.replace(/[^\w.-]+/g, "-");
8336
- const dest = opts.output ? path34.resolve(opts.output) : path34.join(getOutputDir(), name);
8714
+ const dest = opts.output ? path35.resolve(opts.output) : path35.join(getOutputDir(), name);
8337
8715
  await downloadOutput(res.audio_url, dest, "Re-run the command to generate a fresh URL.");
8338
8716
  if (opts.json) console.log(JSON.stringify({ ...summary, file: dest }, null, 2));
8339
8717
  else console.log(`Saved: ${dest} (${res.duration}s, ${res.engine})`);
@@ -8452,8 +8830,8 @@ function registerHeygenCommands(program2) {
8452
8830
  }
8453
8831
 
8454
8832
  // src/providers/leonardo/commands/generate.ts
8455
- import fs30 from "fs";
8456
- import path35 from "path";
8833
+ import fs31 from "fs";
8834
+ import path36 from "path";
8457
8835
 
8458
8836
  // src/providers/leonardo/client.ts
8459
8837
  init_errors();
@@ -8531,11 +8909,11 @@ var LeonardoClient = class {
8531
8909
  }
8532
8910
  apiKey;
8533
8911
  baseUrl;
8534
- async request(path57, opts = {}) {
8912
+ async request(path58, opts = {}) {
8535
8913
  const base = this.baseUrl.replace(/\/$/, "");
8536
- const v2Override = path57.startsWith("v2:");
8914
+ const v2Override = path58.startsWith("v2:");
8537
8915
  const finalBase = v2Override ? base.replace(/\/v1$/, "/v2") : base;
8538
- const finalPath = v2Override ? path57.slice(3) : path57;
8916
+ const finalPath = v2Override ? path58.slice(3) : path58;
8539
8917
  const url = new URL(finalBase + finalPath);
8540
8918
  if (opts.query) {
8541
8919
  for (const [k, v] of Object.entries(opts.query)) {
@@ -8554,14 +8932,14 @@ var LeonardoClient = class {
8554
8932
  timeoutMs: opts.timeoutMs ?? 12e4
8555
8933
  });
8556
8934
  }
8557
- get(path57, query, logger) {
8558
- return this.request(path57, { method: "GET", query, logger });
8935
+ get(path58, query, logger) {
8936
+ return this.request(path58, { method: "GET", query, logger });
8559
8937
  }
8560
- post(path57, body, logger) {
8561
- return this.request(path57, { method: "POST", body, logger });
8938
+ post(path58, body, logger) {
8939
+ return this.request(path58, { method: "POST", body, logger });
8562
8940
  }
8563
- delete(path57, logger) {
8564
- return this.request(path57, { method: "DELETE", logger });
8941
+ delete(path58, logger) {
8942
+ return this.request(path58, { method: "DELETE", logger });
8565
8943
  }
8566
8944
  };
8567
8945
  function createLeonardoClient() {
@@ -8572,7 +8950,7 @@ function createLeonardoClient() {
8572
8950
  function extFromUrl2(url, fallback = ".png") {
8573
8951
  try {
8574
8952
  const u = new URL(url);
8575
- const e = path35.extname(u.pathname);
8953
+ const e = path36.extname(u.pathname);
8576
8954
  return e.length > 0 ? e : fallback;
8577
8955
  } catch {
8578
8956
  return fallback;
@@ -8657,7 +9035,7 @@ function registerLeonardoGenerateCommand(parent) {
8657
9035
  if (!img) continue;
8658
9036
  const ext = extFromUrl2(img.url);
8659
9037
  const filename = autoFilename(generationId, i + 1, ext);
8660
- const filepath = path35.join(outDir, filename);
9038
+ const filepath = path36.join(outDir, filename);
8661
9039
  try {
8662
9040
  await downloadFile(img.url, filepath);
8663
9041
  saved.push(filepath);
@@ -8667,9 +9045,9 @@ function registerLeonardoGenerateCommand(parent) {
8667
9045
  }
8668
9046
  }
8669
9047
  if (opts.output && saved[0]) {
8670
- const dest = path35.resolve(opts.output);
8671
- fs30.mkdirSync(path35.dirname(dest), { recursive: true });
8672
- fs30.copyFileSync(saved[0], dest);
9048
+ const dest = path36.resolve(opts.output);
9049
+ fs31.mkdirSync(path36.dirname(dest), { recursive: true });
9050
+ fs31.copyFileSync(saved[0], dest);
8673
9051
  logger.success(`Copied to ${dest}`);
8674
9052
  }
8675
9053
  const finalImages = finalizeGeneratedImages(saved, { ...opts, logger });
@@ -8681,11 +9059,11 @@ Generated ${finalImages.length} image(s):`);
8681
9059
  }
8682
9060
 
8683
9061
  // src/providers/leonardo/commands/image-to-image.ts
8684
- import fs31 from "fs";
8685
- import path36 from "path";
9062
+ import fs32 from "fs";
9063
+ import path37 from "path";
8686
9064
  function extFromUrl3(url, fallback = ".png") {
8687
9065
  try {
8688
- return path36.extname(new URL(url).pathname) || fallback;
9066
+ return path37.extname(new URL(url).pathname) || fallback;
8689
9067
  } catch {
8690
9068
  return fallback;
8691
9069
  }
@@ -8747,7 +9125,7 @@ function registerLeonardoImageToImageCommand(parent) {
8747
9125
  if (!img) continue;
8748
9126
  const ext = extFromUrl3(img.url);
8749
9127
  const filename = `leonardo-i2i-${generationId.slice(0, 8)}-${String(i + 1).padStart(2, "0")}${ext}`;
8750
- const filepath = path36.join(outDir, filename);
9128
+ const filepath = path37.join(outDir, filename);
8751
9129
  try {
8752
9130
  await downloadFile(img.url, filepath);
8753
9131
  saved.push(filepath);
@@ -8757,9 +9135,9 @@ function registerLeonardoImageToImageCommand(parent) {
8757
9135
  }
8758
9136
  }
8759
9137
  if (opts.output && saved[0]) {
8760
- const dest = path36.resolve(opts.output);
8761
- fs31.mkdirSync(path36.dirname(dest), { recursive: true });
8762
- fs31.copyFileSync(saved[0], dest);
9138
+ const dest = path37.resolve(opts.output);
9139
+ fs32.mkdirSync(path37.dirname(dest), { recursive: true });
9140
+ fs32.copyFileSync(saved[0], dest);
8763
9141
  logger.success(`Copied to ${dest}`);
8764
9142
  }
8765
9143
  const finalImages = finalizeGeneratedImages(saved, { ...opts, logger });
@@ -8771,7 +9149,7 @@ Generated ${finalImages.length} image(s):`);
8771
9149
  }
8772
9150
 
8773
9151
  // src/providers/leonardo/commands/video.ts
8774
- import path37 from "path";
9152
+ import path38 from "path";
8775
9153
  function v2DimsFor(resolution) {
8776
9154
  if (resolution === "RESOLUTION_1080") return { width: 1920, height: 1080 };
8777
9155
  if (resolution === "RESOLUTION_480") return { width: 854, height: 480 };
@@ -8882,7 +9260,7 @@ async function pollAndDownload(client, jobId, opts) {
8882
9260
  logger.error(`No video URL in completed generation: ${JSON.stringify(gen)}`);
8883
9261
  process.exit(1);
8884
9262
  }
8885
- const outPath = opts.output ?? path37.join(getOutputDir(), `${opts.basename}-${jobId.slice(0, 8)}.mp4`);
9263
+ const outPath = opts.output ?? path38.join(getOutputDir(), `${opts.basename}-${jobId.slice(0, 8)}.mp4`);
8886
9264
  await downloadFile(videoUrl2, outPath);
8887
9265
  logger.success(`Saved ${outPath}`);
8888
9266
  await maybeDownloadThumb(gen, outPath, {
@@ -8988,7 +9366,7 @@ function registerLeonardoModelsCommand(parent) {
8988
9366
  }
8989
9367
 
8990
9368
  // src/providers/leonardo/commands/status.ts
8991
- import path38 from "path";
9369
+ import path39 from "path";
8992
9370
  var VIDEO_KEYS = ["motionMP4URL", "url"];
8993
9371
  var IMAGE_EXT_RE2 = /\.(jpe?g|png|webp|gif)(\?|$)/i;
8994
9372
  var VIDEO_EXT_RE = /\.(mp4|webm|mov|m4v)(\?|$)/i;
@@ -9053,7 +9431,7 @@ function registerLeonardoStatusCommand(parent) {
9053
9431
  const outDir = getOutputDir();
9054
9432
  let idx = 0;
9055
9433
  for (const url of videos) {
9056
- const dest = opts.output && videos.length === 1 && images.length === 0 ? opts.output : path38.join(outDir, `leonardo-${generationId.slice(0, 8)}-${idx}.mp4`);
9434
+ const dest = opts.output && videos.length === 1 && images.length === 0 ? opts.output : path39.join(outDir, `leonardo-${generationId.slice(0, 8)}-${idx}.mp4`);
9057
9435
  await downloadFile(url, dest);
9058
9436
  logger.success(`Saved ${dest}`);
9059
9437
  await maybeDownloadThumb(gen, dest, {
@@ -9066,7 +9444,7 @@ function registerLeonardoStatusCommand(parent) {
9066
9444
  for (const url of images) {
9067
9445
  const m = url.match(IMAGE_EXT_RE2);
9068
9446
  const ext = m?.[1] ? m[1].toLowerCase().replace("jpeg", "jpg") : "png";
9069
- const dest = path38.join(outDir, `leonardo-${generationId.slice(0, 8)}-${idx}.${ext}`);
9447
+ const dest = path39.join(outDir, `leonardo-${generationId.slice(0, 8)}-${idx}.${ext}`);
9070
9448
  await downloadFile(url, dest);
9071
9449
  logger.success(`Saved ${dest}`);
9072
9450
  idx++;
@@ -9223,8 +9601,8 @@ function sleep3(ms) {
9223
9601
  }
9224
9602
 
9225
9603
  // src/providers/minimax/generators/music.ts
9226
- import fs32 from "fs";
9227
- import path39 from "path";
9604
+ import fs33 from "fs";
9605
+ import path40 from "path";
9228
9606
  async function generateMinimaxMusic(opts) {
9229
9607
  const {
9230
9608
  apiKey,
@@ -9269,14 +9647,14 @@ async function generateMinimaxMusic(opts) {
9269
9647
  }
9270
9648
  const durationMs = resp.extra_info?.music_duration ?? 0;
9271
9649
  const outDir = getOutputDir();
9272
- const dest = path39.join(outDir, `minimax_music_${Date.now()}.${outputFormat}`);
9650
+ const dest = path40.join(outDir, `minimax_music_${Date.now()}.${outputFormat}`);
9273
9651
  try {
9274
9652
  if (audioData.startsWith("http")) {
9275
9653
  const bytes = await fetchBytes(audioData);
9276
- fs32.writeFileSync(dest, bytes);
9654
+ fs33.writeFileSync(dest, bytes);
9277
9655
  } else {
9278
9656
  const audioBytes = Buffer.from(audioData, "hex");
9279
- fs32.writeFileSync(dest, audioBytes);
9657
+ fs33.writeFileSync(dest, audioBytes);
9280
9658
  }
9281
9659
  } catch (e) {
9282
9660
  return {
@@ -9287,8 +9665,8 @@ async function generateMinimaxMusic(opts) {
9287
9665
  const durSec = durationMs / 1e3;
9288
9666
  logger?.success(`Saved: ${dest} (${durSec.toFixed(1)}s)`);
9289
9667
  if (output) {
9290
- fs32.mkdirSync(path39.dirname(path39.resolve(output)), { recursive: true });
9291
- fs32.copyFileSync(dest, output);
9668
+ fs33.mkdirSync(path40.dirname(path40.resolve(output)), { recursive: true });
9669
+ fs33.copyFileSync(dest, output);
9292
9670
  }
9293
9671
  return { status: "success", generatedAudio: dest, durationMs, model };
9294
9672
  }
@@ -9362,8 +9740,8 @@ Generated music: ${result.generatedAudio}`);
9362
9740
  init_errors();
9363
9741
 
9364
9742
  // src/providers/minimax/generators/speech.ts
9365
- import fs33 from "fs";
9366
- import path40 from "path";
9743
+ import fs34 from "fs";
9744
+ import path41 from "path";
9367
9745
  async function generateMinimaxSpeech(opts) {
9368
9746
  const {
9369
9747
  apiKey,
@@ -9410,12 +9788,12 @@ async function generateMinimaxSpeech(opts) {
9410
9788
  const audioBytes = Buffer.from(audioHex, "hex");
9411
9789
  const ext = ["mp3", "wav", "flac"].includes(outputFormat) ? outputFormat : "mp3";
9412
9790
  const outDir = getOutputDir();
9413
- const dest = path40.join(outDir, `minimax_speech_${Date.now()}.${ext}`);
9414
- fs33.writeFileSync(dest, audioBytes);
9791
+ const dest = path41.join(outDir, `minimax_speech_${Date.now()}.${ext}`);
9792
+ fs34.writeFileSync(dest, audioBytes);
9415
9793
  logger?.success(`Saved: ${dest} (${(audioBytes.length / 1024).toFixed(1)} KB)`);
9416
9794
  if (output) {
9417
- fs33.mkdirSync(path40.dirname(path40.resolve(output)), { recursive: true });
9418
- fs33.copyFileSync(dest, output);
9795
+ fs34.mkdirSync(path41.dirname(path41.resolve(output)), { recursive: true });
9796
+ fs34.copyFileSync(dest, output);
9419
9797
  }
9420
9798
  return { status: "success", generatedAudio: dest, model };
9421
9799
  }
@@ -9462,8 +9840,8 @@ Generated audio: ${result.generatedAudio}`);
9462
9840
  init_errors();
9463
9841
 
9464
9842
  // src/providers/minimax/generators/video.ts
9465
- import fs34 from "fs";
9466
- import path41 from "path";
9843
+ import fs35 from "fs";
9844
+ import path42 from "path";
9467
9845
  async function generateMinimaxVideo(opts) {
9468
9846
  const {
9469
9847
  apiKey,
@@ -9509,18 +9887,18 @@ async function generateMinimaxVideo(opts) {
9509
9887
  return { status: "error", error: `No file_id in poll result: ${JSON.stringify(pollResult)}` };
9510
9888
  }
9511
9889
  const outDir = getOutputDir();
9512
- const dest = path41.join(outDir, `minimax_video_${Date.now()}.mp4`);
9890
+ const dest = path42.join(outDir, `minimax_video_${Date.now()}.mp4`);
9513
9891
  try {
9514
9892
  await downloadMinimaxFile(fileId, apiKey, dest, logger);
9515
9893
  } catch (e) {
9516
9894
  return { status: "error", error: e instanceof Error ? e.message : String(e) };
9517
9895
  }
9518
9896
  const elapsed = (Date.now() - start) / 1e3;
9519
- const stat = fs34.statSync(dest);
9897
+ const stat = fs35.statSync(dest);
9520
9898
  const fileSizeMb = stat.size / (1024 * 1024);
9521
9899
  if (output) {
9522
- fs34.mkdirSync(path41.dirname(path41.resolve(output)), { recursive: true });
9523
- fs34.copyFileSync(dest, output);
9900
+ fs35.mkdirSync(path42.dirname(path42.resolve(output)), { recursive: true });
9901
+ fs35.copyFileSync(dest, output);
9524
9902
  }
9525
9903
  await maybeDownloadThumb(pollResult, dest, {
9526
9904
  skip: thumb === false,
@@ -9582,8 +9960,8 @@ Generated video: ${result.generatedVideo}`);
9582
9960
 
9583
9961
  // src/providers/minimax/generators/image.ts
9584
9962
  init_errors();
9585
- import fs35 from "fs";
9586
- import path42 from "path";
9963
+ import fs36 from "fs";
9964
+ import path43 from "path";
9587
9965
  async function generateMinimaxImage(opts) {
9588
9966
  const {
9589
9967
  apiKey,
@@ -9621,8 +9999,8 @@ async function generateMinimaxImage(opts) {
9621
9999
  try {
9622
10000
  const bytes = await fetchBytes(url);
9623
10001
  const fname = `minimax_image_${Date.now()}_${i}.png`;
9624
- const dest = path42.join(outDir, fname);
9625
- fs35.writeFileSync(dest, bytes);
10002
+ const dest = path43.join(outDir, fname);
10003
+ fs36.writeFileSync(dest, bytes);
9626
10004
  savedFiles.push(dest);
9627
10005
  logger?.success(`Saved: ${dest}`);
9628
10006
  } catch (e) {
@@ -9633,8 +10011,8 @@ async function generateMinimaxImage(opts) {
9633
10011
  }
9634
10012
  }
9635
10013
  if (output && savedFiles[0]) {
9636
- fs35.mkdirSync(path42.dirname(path42.resolve(output)), { recursive: true });
9637
- fs35.copyFileSync(savedFiles[0], output);
10014
+ fs36.mkdirSync(path43.dirname(path43.resolve(output)), { recursive: true });
10015
+ fs36.copyFileSync(savedFiles[0], output);
9638
10016
  }
9639
10017
  return { status: "success", generatedImages: savedFiles, model };
9640
10018
  }
@@ -9673,8 +10051,8 @@ Generated ${finalImages.length} image(s):`);
9673
10051
 
9674
10052
  // src/providers/minimax/commands/image-to-image.ts
9675
10053
  init_errors();
9676
- import fs36 from "fs";
9677
- import path43 from "path";
10054
+ import fs37 from "fs";
10055
+ import path44 from "path";
9678
10056
  function registerMinimaxImageToImageCommand(parent) {
9679
10057
  parent.command("image-to-image").alias("i2i").description(
9680
10058
  "MiniMax character/subject preservation. CAVEAT: NOT free-form image editing \u2014 the --ref image is used only as a character/face reference for a NEW scene described by --prompt."
@@ -9729,8 +10107,8 @@ function registerMinimaxImageToImageCommand(parent) {
9729
10107
  if (!url) continue;
9730
10108
  try {
9731
10109
  const bytes = await fetchBytes(url);
9732
- const dest = path43.join(outDir, `minimax-i2i-${ts}-${i + 1}.png`);
9733
- fs36.writeFileSync(dest, bytes);
10110
+ const dest = path44.join(outDir, `minimax-i2i-${ts}-${i + 1}.png`);
10111
+ fs37.writeFileSync(dest, bytes);
9734
10112
  saved.push(dest);
9735
10113
  logger.success(`Saved: ${dest}`);
9736
10114
  } catch (e) {
@@ -9741,8 +10119,8 @@ function registerMinimaxImageToImageCommand(parent) {
9741
10119
  }
9742
10120
  }
9743
10121
  if (opts.output && saved[0]) {
9744
- fs36.mkdirSync(path43.dirname(path43.resolve(opts.output)), { recursive: true });
9745
- fs36.copyFileSync(saved[0], opts.output);
10122
+ fs37.mkdirSync(path44.dirname(path44.resolve(opts.output)), { recursive: true });
10123
+ fs37.copyFileSync(saved[0], opts.output);
9746
10124
  logger.success(`Copied to: ${opts.output}`);
9747
10125
  }
9748
10126
  const finalImages = finalizeGeneratedImages(saved, { ...opts, logger });
@@ -9876,12 +10254,12 @@ function validateKnownSpeakerPairing(names = [], refs = []) {
9876
10254
  }
9877
10255
 
9878
10256
  // src/providers/openai/openai-audio.ts
9879
- import fs38 from "fs";
9880
- import path45 from "path";
10257
+ import fs39 from "fs";
10258
+ import path46 from "path";
9881
10259
 
9882
10260
  // src/providers/openai/client.ts
9883
- import fs37 from "fs";
9884
- import path44 from "path";
10261
+ import fs38 from "fs";
10262
+ import path45 from "path";
9885
10263
  init_errors();
9886
10264
  var DEFAULT_TIMEOUT_MS4 = 24e4;
9887
10265
  function requireOpenAIKey() {
@@ -9949,10 +10327,10 @@ async function postOpenAIBinary(pathname, body, apiKey, timeoutMs = DEFAULT_TIME
9949
10327
  return Buffer.from(await res.arrayBuffer());
9950
10328
  }
9951
10329
  function fileFromPath(filePath) {
9952
- const abs = path44.resolve(filePath);
9953
- if (!fs37.existsSync(abs)) throw new ValidationError(`File not found: ${abs}`);
9954
- const bytes = fs37.readFileSync(abs);
9955
- return new File([new Uint8Array(bytes)], path44.basename(abs));
10330
+ const abs = path45.resolve(filePath);
10331
+ if (!fs38.existsSync(abs)) throw new ValidationError(`File not found: ${abs}`);
10332
+ const bytes = fs38.readFileSync(abs);
10333
+ return new File([new Uint8Array(bytes)], path45.basename(abs));
9956
10334
  }
9957
10335
 
9958
10336
  // src/providers/openai/openai-audio.ts
@@ -9991,7 +10369,7 @@ async function resolveAudioReferenceDataUrl(ref) {
9991
10369
  }
9992
10370
  function audioMimeFromReference(ref) {
9993
10371
  const pathname = new URL(ref).pathname;
9994
- const ext = path45.extname(pathname).toLowerCase();
10372
+ const ext = path46.extname(pathname).toLowerCase();
9995
10373
  switch (ext) {
9996
10374
  case ".mp3":
9997
10375
  return "audio/mpeg";
@@ -10010,12 +10388,12 @@ function audioMimeFromReference(ref) {
10010
10388
  async function generateOpenAISpeech(opts) {
10011
10389
  const bytes = await postOpenAIBinary("/audio/speech", buildSpeechBody(opts), requireOpenAIKey());
10012
10390
  const ext = extFromAudioFormat(opts.outputFormat);
10013
- const dest = path45.join(getOutputDir(), `openai-speech-${Date.now()}.${ext}`);
10014
- fs38.writeFileSync(dest, bytes);
10391
+ const dest = path46.join(getOutputDir(), `openai-speech-${Date.now()}.${ext}`);
10392
+ fs39.writeFileSync(dest, bytes);
10015
10393
  opts.logger?.success(`Saved: ${dest} (${(bytes.length / 1024).toFixed(1)} KB)`);
10016
10394
  if (opts.output) {
10017
- fs38.mkdirSync(path45.dirname(path45.resolve(opts.output)), { recursive: true });
10018
- fs38.copyFileSync(dest, opts.output);
10395
+ fs39.mkdirSync(path46.dirname(path46.resolve(opts.output)), { recursive: true });
10396
+ fs39.copyFileSync(dest, opts.output);
10019
10397
  }
10020
10398
  return dest;
10021
10399
  }
@@ -10037,9 +10415,9 @@ async function transcribeOpenAIAudio(opts) {
10037
10415
  );
10038
10416
  const text = typeof data.text === "string" ? data.text : void 0;
10039
10417
  const ext = opts.format === "text" ? "txt" : "json";
10040
- const dest = opts.output ? path45.resolve(opts.output) : path45.join(getOutputDir(), `openai-transcribe-${Date.now()}.${ext}`);
10041
- fs38.mkdirSync(path45.dirname(dest), { recursive: true });
10042
- fs38.writeFileSync(dest, opts.format === "text" ? text ?? "" : JSON.stringify(data, null, 2));
10418
+ const dest = opts.output ? path46.resolve(opts.output) : path46.join(getOutputDir(), `openai-transcribe-${Date.now()}.${ext}`);
10419
+ fs39.mkdirSync(path46.dirname(dest), { recursive: true });
10420
+ fs39.writeFileSync(dest, opts.format === "text" ? text ?? "" : JSON.stringify(data, null, 2));
10043
10421
  opts.logger?.success(`Saved transcript: ${dest}`);
10044
10422
  return { path: dest, text };
10045
10423
  }
@@ -10066,9 +10444,9 @@ Generated audio: ${dest}`);
10066
10444
  }
10067
10445
 
10068
10446
  // src/providers/openai/codex-image-driver.ts
10069
- import fs39 from "fs";
10070
- import os3 from "os";
10071
- import path46 from "path";
10447
+ import fs40 from "fs";
10448
+ import os4 from "os";
10449
+ import path47 from "path";
10072
10450
  import { execa } from "execa";
10073
10451
  init_errors();
10074
10452
  var defaultRunner = async (cmd, args, opts) => {
@@ -10117,12 +10495,12 @@ function sanitizeCodexEnv() {
10117
10495
  async function runCodexImageGeneration(opts) {
10118
10496
  const runner = opts.runner ?? defaultRunner;
10119
10497
  const ext = extFromImageFormat(opts.outputFormat);
10120
- const dest = opts.output ? path46.resolve(opts.output) : path46.join(getOutputDir(), `codex-image-${Date.now()}.${ext}`);
10121
- fs39.mkdirSync(path46.dirname(dest), { recursive: true });
10122
- const tmp = fs39.mkdtempSync(path46.join(os3.tmpdir(), "multix-codex-image-"));
10123
- const schema = path46.join(tmp, "schema.json");
10124
- const result = path46.join(tmp, "result.json");
10125
- fs39.writeFileSync(
10498
+ const dest = opts.output ? path47.resolve(opts.output) : path47.join(getOutputDir(), `codex-image-${Date.now()}.${ext}`);
10499
+ fs40.mkdirSync(path47.dirname(dest), { recursive: true });
10500
+ const tmp = fs40.mkdtempSync(path47.join(os4.tmpdir(), "multix-codex-image-"));
10501
+ const schema = path47.join(tmp, "schema.json");
10502
+ const result = path47.join(tmp, "result.json");
10503
+ fs40.writeFileSync(
10126
10504
  schema,
10127
10505
  JSON.stringify({
10128
10506
  type: "object",
@@ -10152,7 +10530,7 @@ async function runCodexImageGeneration(opts) {
10152
10530
  if (res.exitCode !== 0) {
10153
10531
  throw new ProviderError(`Codex image driver failed: ${res.stderr || res.stdout}`, "openai");
10154
10532
  }
10155
- if (!fs39.existsSync(dest) || fs39.statSync(dest).size === 0) {
10533
+ if (!fs40.existsSync(dest) || fs40.statSync(dest).size === 0) {
10156
10534
  throw new ProviderError(`Codex did not create a non-empty image at ${dest}`, "openai");
10157
10535
  }
10158
10536
  return { path: dest };
@@ -10169,8 +10547,8 @@ function buildCodexPrompt(userPrompt, outputPath) {
10169
10547
 
10170
10548
  // src/providers/openai/openai-image.ts
10171
10549
  init_errors();
10172
- import fs40 from "fs";
10173
- import path47 from "path";
10550
+ import fs41 from "fs";
10551
+ import path48 from "path";
10174
10552
  function buildImageGenerationBody(opts) {
10175
10553
  return {
10176
10554
  model: opts.model ?? DEFAULT_OPENAI_IMAGE_MODEL,
@@ -10203,14 +10581,14 @@ function saveOpenAIImages(opts) {
10203
10581
  for (let i = 0; i < items.length; i++) {
10204
10582
  const b64 = items[i]?.b64_json;
10205
10583
  if (!b64) throw new ProviderError("No b64_json in OpenAI image response", "openai");
10206
- const dest = path47.join(outDir, `openai-${opts.task}-${ts}-${i + 1}.${ext}`);
10207
- fs40.writeFileSync(dest, Buffer.from(b64, "base64"));
10584
+ const dest = path48.join(outDir, `openai-${opts.task}-${ts}-${i + 1}.${ext}`);
10585
+ fs41.writeFileSync(dest, Buffer.from(b64, "base64"));
10208
10586
  saved.push(dest);
10209
10587
  opts.logger?.success(`Saved: ${dest}`);
10210
10588
  }
10211
10589
  if (opts.output && saved[0]) {
10212
- fs40.mkdirSync(path47.dirname(path47.resolve(opts.output)), { recursive: true });
10213
- fs40.copyFileSync(saved[0], opts.output);
10590
+ fs41.mkdirSync(path48.dirname(path48.resolve(opts.output)), { recursive: true });
10591
+ fs41.copyFileSync(saved[0], opts.output);
10214
10592
  opts.logger?.success(`Copied to: ${opts.output}`);
10215
10593
  }
10216
10594
  return saved;
@@ -10219,7 +10597,7 @@ async function resolveImageRefBytes(ref) {
10219
10597
  if (isHttpUrl(ref)) {
10220
10598
  const bytes2 = await fetchBytes(ref);
10221
10599
  const pathname = new URL(ref).pathname;
10222
- const filename = path47.basename(pathname) || "reference.png";
10600
+ const filename = path48.basename(pathname) || "reference.png";
10223
10601
  return { filename, bytes: bytes2, mime: sniffImageMime2(bytes2, filename) };
10224
10602
  }
10225
10603
  const file = fileFromPath(ref);
@@ -10264,7 +10642,7 @@ function sniffImageMime2(buf, name) {
10264
10642
  if (buf[0] === 137 && buf[1] === 80 && buf[2] === 78 && buf[3] === 71) return "image/png";
10265
10643
  if (buf.slice(0, 4).toString() === "RIFF" && buf.slice(8, 12).toString() === "WEBP")
10266
10644
  return "image/webp";
10267
- const ext = path47.extname(name).toLowerCase();
10645
+ const ext = path48.extname(name).toLowerCase();
10268
10646
  if (ext === ".jpg" || ext === ".jpeg") return "image/jpeg";
10269
10647
  if (ext === ".webp") return "image/webp";
10270
10648
  return "image/png";
@@ -10408,8 +10786,8 @@ function registerOpenAICommands(program2) {
10408
10786
  }
10409
10787
 
10410
10788
  // src/providers/openrouter/client.ts
10411
- import fs41 from "fs";
10412
- import path48 from "path";
10789
+ import fs42 from "fs";
10790
+ import path49 from "path";
10413
10791
  init_errors();
10414
10792
 
10415
10793
  // src/providers/openrouter/payload.ts
@@ -10573,8 +10951,8 @@ async function generateOpenRouterImage(opts) {
10573
10951
  if (!url) continue;
10574
10952
  try {
10575
10953
  const bytes = await fetchBytes(url);
10576
- const dest = path48.join(outDir, `openrouter_image_${Date.now()}_${i}.png`);
10577
- fs41.writeFileSync(dest, bytes);
10954
+ const dest = path49.join(outDir, `openrouter_image_${Date.now()}_${i}.png`);
10955
+ fs42.writeFileSync(dest, bytes);
10578
10956
  savedFiles.push(dest);
10579
10957
  logger?.success(`Saved: ${dest}`);
10580
10958
  } catch (e) {
@@ -10585,8 +10963,8 @@ async function generateOpenRouterImage(opts) {
10585
10963
  }
10586
10964
  }
10587
10965
  if (output && savedFiles[0]) {
10588
- fs41.mkdirSync(path48.dirname(path48.resolve(output)), { recursive: true });
10589
- fs41.writeFileSync(output, fs41.readFileSync(savedFiles[0]));
10966
+ fs42.mkdirSync(path49.dirname(path49.resolve(output)), { recursive: true });
10967
+ fs42.writeFileSync(output, fs42.readFileSync(savedFiles[0]));
10590
10968
  }
10591
10969
  return { status: "success", generatedImages: savedFiles, model: usedModel };
10592
10970
  }
@@ -10627,8 +11005,8 @@ Generated ${finalImages.length} image(s):`);
10627
11005
  }
10628
11006
 
10629
11007
  // src/providers/openrouter/commands/image-to-image.ts
10630
- import fs42 from "fs";
10631
- import path49 from "path";
11008
+ import fs43 from "fs";
11009
+ import path50 from "path";
10632
11010
  init_errors();
10633
11011
  var DEFAULT_MODEL = "google/gemini-2.5-flash-image";
10634
11012
  var SOFT_LIMIT_BYTES2 = 5 * 1024 * 1024;
@@ -10720,14 +11098,14 @@ async function runImageToImage(opts) {
10720
11098
  const url = parsed.urls[i];
10721
11099
  if (!url) continue;
10722
11100
  const bytes = await fetchBytes(url);
10723
- const dest = path49.join(outDir, `openrouter-i2i-${ts}-${i + 1}.png`);
10724
- fs42.writeFileSync(dest, bytes);
11101
+ const dest = path50.join(outDir, `openrouter-i2i-${ts}-${i + 1}.png`);
11102
+ fs43.writeFileSync(dest, bytes);
10725
11103
  saved.push(dest);
10726
11104
  opts.logger.success(`Saved: ${dest}`);
10727
11105
  }
10728
11106
  if (opts.output && saved[0]) {
10729
- fs42.mkdirSync(path49.dirname(path49.resolve(opts.output)), { recursive: true });
10730
- fs42.copyFileSync(saved[0], opts.output);
11107
+ fs43.mkdirSync(path50.dirname(path50.resolve(opts.output)), { recursive: true });
11108
+ fs43.copyFileSync(saved[0], opts.output);
10731
11109
  opts.logger.success(`Copied to: ${opts.output}`);
10732
11110
  }
10733
11111
  return saved;
@@ -10772,13 +11150,13 @@ async function listVideoModels(apiKey = requireOpenRouterKey()) {
10772
11150
  }
10773
11151
 
10774
11152
  // src/providers/openrouter/commands/video-status.ts
10775
- import fs43 from "fs";
10776
- import path50 from "path";
11153
+ import fs44 from "fs";
11154
+ import path51 from "path";
10777
11155
  async function downloadOpenRouterVideo(opts) {
10778
11156
  const { jobId, logger, source } = opts;
10779
11157
  const apiKey = requireOpenRouterKey();
10780
11158
  const url = videoContentUrl(jobId);
10781
- const dest = path50.join(getOutputDir(), `openrouter_video_${jobId}.mp4`);
11159
+ const dest = path51.join(getOutputDir(), `openrouter_video_${jobId}.mp4`);
10782
11160
  const { response: r } = await fetchWithValidatedRedirects(url, {
10783
11161
  headers: { Authorization: `Bearer ${apiKey}` }
10784
11162
  });
@@ -10790,14 +11168,14 @@ async function downloadOpenRouterVideo(opts) {
10790
11168
  logger.error("Empty response body");
10791
11169
  process.exit(1);
10792
11170
  }
10793
- fs43.mkdirSync(path50.dirname(dest), { recursive: true });
10794
- const ws = fs43.createWriteStream(dest);
11171
+ fs44.mkdirSync(path51.dirname(dest), { recursive: true });
11172
+ const ws = fs44.createWriteStream(dest);
10795
11173
  const { pipeline: pipeline2 } = await import("stream/promises");
10796
11174
  await pipeline2(r.body, ws);
10797
11175
  logger.success(`Saved ${dest}`);
10798
11176
  if (opts.output) {
10799
- fs43.mkdirSync(path50.dirname(path50.resolve(opts.output)), { recursive: true });
10800
- fs43.copyFileSync(dest, opts.output);
11177
+ fs44.mkdirSync(path51.dirname(path51.resolve(opts.output)), { recursive: true });
11178
+ fs44.copyFileSync(dest, opts.output);
10801
11179
  logger.success(`Copied to ${opts.output}`);
10802
11180
  }
10803
11181
  await maybeDownloadThumb(source, dest, {
@@ -10995,8 +11373,8 @@ function parseModelRef(ref, explicitVersion) {
10995
11373
  }
10996
11374
  return { kind: "version", version: trimmed };
10997
11375
  }
10998
- const [path57, inlineVersion, ...rest] = trimmed.split(":");
10999
- const [owner, name, ...extra] = (path57 ?? "").split("/");
11376
+ const [path58, inlineVersion, ...rest] = trimmed.split(":");
11377
+ const [owner, name, ...extra] = (path58 ?? "").split("/");
11000
11378
  if (rest.length > 0 || extra.length > 0 || !owner || !name) {
11001
11379
  throw new ValidationError(
11002
11380
  `Invalid Replicate model "${ref}" \u2014 expected owner/name, owner/name:version, or a 64-char version id`
@@ -11156,9 +11534,9 @@ var ReplicateClient = class {
11156
11534
  return { accept: "application/json", authorization: `Bearer ${this.apiKey}`, ...extra };
11157
11535
  }
11158
11536
  /** Raw request: throws HttpError so polling can classify transient failures. */
11159
- raw(path57, method, body, headers2, timeoutMs = REQUEST_TIMEOUT_MS) {
11537
+ raw(path58, method, body, headers2, timeoutMs = REQUEST_TIMEOUT_MS) {
11160
11538
  return fetchJson({
11161
- url: `${this.baseUrl}${path57}`,
11539
+ url: `${this.baseUrl}${path58}`,
11162
11540
  method,
11163
11541
  headers: this.headers(headers2),
11164
11542
  body,
@@ -11170,7 +11548,7 @@ var ReplicateClient = class {
11170
11548
  async createPrediction(opts) {
11171
11549
  const { model, input } = opts;
11172
11550
  const isOfficial = model.kind === "official";
11173
- const path57 = isOfficial ? `/models/${model.owner}/${model.name}/predictions` : "/predictions";
11551
+ const path58 = isOfficial ? `/models/${model.owner}/${model.name}/predictions` : "/predictions";
11174
11552
  const body = isOfficial ? { input } : { version: model.version, input };
11175
11553
  const subject = isOfficial ? `model ${model.owner}/${model.name}` : `model version ${model.version}`;
11176
11554
  const headers2 = {};
@@ -11182,7 +11560,7 @@ var ReplicateClient = class {
11182
11560
  }
11183
11561
  for (let attempt = 0; ; attempt++) {
11184
11562
  try {
11185
- const res = await this.raw(path57, "POST", body, headers2, timeoutMs);
11563
+ const res = await this.raw(path58, "POST", body, headers2, timeoutMs);
11186
11564
  return validatePrediction(res);
11187
11565
  } catch (error) {
11188
11566
  if (error instanceof HttpError && error.status === 429 && attempt < CREATE_MAX_RETRIES) {
@@ -11345,145 +11723,33 @@ import { InvalidArgumentError, Option as Option2 } from "commander";
11345
11723
 
11346
11724
  // src/providers/replicate/node-outputs.ts
11347
11725
  init_errors();
11348
- import fs44 from "fs";
11349
- import path51 from "path";
11726
+ import fs45 from "fs";
11727
+ import path52 from "path";
11350
11728
  init_redact();
11351
11729
 
11352
11730
  // src/providers/replicate/output.ts
11353
11731
  init_redact();
11354
-
11355
- // src/core/url-safety.ts
11356
- init_errors();
11357
- function ipv4Octets(host) {
11358
- const parts = host.split(".");
11359
- if (parts.length !== 4 || !parts.every((p) => /^\d{1,3}$/.test(p))) return void 0;
11360
- const octets = parts.map(Number);
11361
- return octets.every((o) => o <= 255) ? octets : void 0;
11362
- }
11363
- function isNonPublicIpv4([a = 0, b = 0, c2 = 0]) {
11364
- return a === 0 || // "this network" / unspecified
11365
- a === 10 || a === 127 || // loopback
11366
- a === 100 && b >= 64 && b <= 127 || // carrier-grade NAT
11367
- a === 169 && b === 254 || // link-local (cloud metadata)
11368
- a === 172 && b >= 16 && b <= 31 || a === 192 && b === 0 && c2 === 0 || // IETF protocol assignments
11369
- a === 192 && b === 0 && c2 === 2 || // TEST-NET-1
11370
- a === 192 && b === 168 || a === 198 && (b === 18 || b === 19) || // benchmarking
11371
- a === 198 && b === 51 && c2 === 100 || // TEST-NET-2
11372
- a === 203 && b === 0 && c2 === 113 || // TEST-NET-3
11373
- a >= 224;
11374
- }
11375
- function ipv6Groups(host) {
11376
- if (!/^[0-9a-f:.]+$/i.test(host) || !host.includes(":")) return void 0;
11377
- let text = host;
11378
- const tail = /^(.*:)(\d+\.\d+\.\d+\.\d+)$/.exec(text);
11379
- if (tail) {
11380
- const octets = ipv4Octets(tail[2]);
11381
- if (!octets) return void 0;
11382
- const [a = 0, b = 0, c2 = 0, d = 0] = octets;
11383
- text = `${tail[1]}${(a << 8 | b).toString(16)}:${(c2 << 8 | d).toString(16)}`;
11384
- }
11385
- const halves = text.split("::");
11386
- if (halves.length > 2) return void 0;
11387
- const parse = (s) => s === "" ? [] : s.split(":");
11388
- const head = parse(halves[0]);
11389
- const rest = halves.length === 2 ? parse(halves[1]) : [];
11390
- const missing = 8 - head.length - rest.length;
11391
- if (halves.length === 2 ? missing < 1 : missing !== 0) return void 0;
11392
- const all = [...head, ...Array(halves.length === 2 ? missing : 0).fill("0"), ...rest];
11393
- if (!all.every((g) => /^[0-9a-f]{1,4}$/i.test(g))) return void 0;
11394
- return all.map((g) => Number.parseInt(g, 16));
11395
- }
11396
- function embeddedIpv4(hi, lo) {
11397
- return [hi >> 8, hi & 255, lo >> 8, lo & 255];
11398
- }
11399
- function isNonPublicIpv6(groups) {
11400
- const [g0 = 0, g1 = 0, g2 = 0, g3 = 0, g4 = 0, g5 = 0, g6 = 0, g7 = 0] = groups;
11401
- const zeroUpTo = (n) => groups.slice(0, n).every((g) => g === 0);
11402
- if (zeroUpTo(5) && g5 === 65535) return isNonPublicIpv4(embeddedIpv4(g6, g7));
11403
- if (g0 === 100 && g1 === 65435 && g2 === 0 && g3 === 0 && g4 === 0 && g5 === 0) {
11404
- return isNonPublicIpv4(embeddedIpv4(g6, g7));
11405
- }
11406
- if (g0 === 8194) return isNonPublicIpv4(embeddedIpv4(g1, g2));
11407
- return g0 === 0 || // unspecified (::), loopback (::1), IPv4-compatible and other reserved
11408
- g0 === 256 && g1 === 0 && g2 === 0 && g3 === 0 || // discard-only 100::/64
11409
- (g0 & 65024) === 64512 || // unique local fc00::/7
11410
- (g0 & 65472) === 65152 || // link-local fe80::/10
11411
- (g0 & 65472) === 65216 || // deprecated site-local fec0::/10
11412
- (g0 & 65280) === 65280;
11413
- }
11414
- function bareHost(hostname) {
11415
- const host = hostname.toLowerCase().replace(/\.$/, "");
11416
- return host.startsWith("[") && host.endsWith("]") ? host.slice(1, -1) : host;
11417
- }
11418
- function isLoopbackHost(hostname) {
11419
- const host = bareHost(hostname);
11420
- if (host === "localhost" || host.endsWith(".localhost")) return true;
11421
- const octets = ipv4Octets(host);
11422
- if (octets) return octets[0] === 127;
11423
- const groups = ipv6Groups(host);
11424
- if (!groups) return false;
11425
- if (groups.slice(0, 5).every((g) => g === 0) && groups[5] === 65535) {
11426
- return groups[6] !== void 0 && groups[6] >> 8 === 127;
11427
- }
11428
- return groups.slice(0, 7).every((g) => g === 0) && groups[7] === 1;
11429
- }
11430
- function isNonPublicHost(hostname) {
11431
- const host = bareHost(hostname);
11432
- if (host === "localhost" || host.endsWith(".localhost")) return true;
11433
- const octets = ipv4Octets(host);
11434
- if (octets) return isNonPublicIpv4(octets);
11435
- const groups = ipv6Groups(host);
11436
- return groups !== void 0 && isNonPublicIpv6(groups);
11437
- }
11438
- function checkUrlSafety(url, opts) {
11439
- const action = opts.action ?? "download from";
11440
- let parsed;
11441
- try {
11442
- parsed = new URL(url);
11443
- } catch {
11444
- throw new ProviderError(`Refusing to ${action} a malformed URL`, opts.provider);
11445
- }
11446
- const host = parsed.hostname.toLowerCase().replace(/\.$/, "");
11447
- const localOk = opts.allowLocalhostHttp === true && isLoopbackHost(host);
11448
- const httpOk = parsed.protocol === "https:" || localOk && parsed.protocol === "http:";
11449
- if (!httpOk) {
11450
- throw new ProviderError(
11451
- `Refusing to ${action} ${host || "this URL"}: only https:// URLs are allowed`,
11452
- opts.provider
11453
- );
11454
- }
11455
- if (!localOk && isNonPublicHost(host)) {
11456
- throw new ProviderError(
11457
- `Refusing to ${action} ${host}: it points to a local or private address`,
11458
- opts.provider
11459
- );
11460
- }
11461
- const trusted = (opts.trustedHosts ?? []).some((t) => host === t || host.endsWith(`.${t}`));
11462
- return { host, trusted };
11463
- }
11464
-
11465
- // src/providers/replicate/output.ts
11466
11732
  var HTTP_RE = /^https?:\/\//i;
11467
11733
  var DATA_URI_RE = /^data:[^,]*,/i;
11468
11734
  function isMediaRef(value) {
11469
11735
  return typeof value === "string" && (HTTP_RE.test(value) || DATA_URI_RE.test(value));
11470
11736
  }
11471
11737
  var REPLICATE_DELIVERY_HOST = "replicate.delivery";
11472
- function checkOutputUrl(url) {
11738
+ function checkOutputUrl2(url) {
11473
11739
  return checkUrlSafety(url, { provider: "replicate", trustedHosts: [REPLICATE_DELIVERY_HOST] });
11474
11740
  }
11475
11741
  function extractOutput(output) {
11476
11742
  const result = { media: [], texts: [] };
11477
11743
  const seen = /* @__PURE__ */ new Set();
11478
- const visit = (value, path57) => {
11744
+ const visit = (value, path58) => {
11479
11745
  if (value === null || value === void 0) return;
11480
11746
  if (typeof value === "string") {
11481
- if (isMediaRef(value)) result.media.push({ url: value, path: path57 || "output" });
11482
- else if (value.length > 0) result.texts.push(path57 ? `${path57}: ${value}` : value);
11747
+ if (isMediaRef(value)) result.media.push({ url: value, path: path58 || "output" });
11748
+ else if (value.length > 0) result.texts.push(path58 ? `${path58}: ${value}` : value);
11483
11749
  return;
11484
11750
  }
11485
11751
  if (typeof value !== "object") {
11486
- result.texts.push(path57 ? `${path57}: ${String(value)}` : String(value));
11752
+ result.texts.push(path58 ? `${path58}: ${String(value)}` : String(value));
11487
11753
  return;
11488
11754
  }
11489
11755
  if (seen.has(value)) return;
@@ -11491,14 +11757,14 @@ function extractOutput(output) {
11491
11757
  if (Array.isArray(value)) {
11492
11758
  if (value.length > 0 && value.every((v) => typeof v === "string" && !isMediaRef(v))) {
11493
11759
  const joined = value.join("");
11494
- if (joined.length > 0) result.texts.push(path57 ? `${path57}: ${joined}` : joined);
11760
+ if (joined.length > 0) result.texts.push(path58 ? `${path58}: ${joined}` : joined);
11495
11761
  return;
11496
11762
  }
11497
- value.forEach((item, i) => visit(item, `${path57}[${i}]`));
11763
+ value.forEach((item, i) => visit(item, `${path58}[${i}]`));
11498
11764
  return;
11499
11765
  }
11500
11766
  for (const [key, val] of Object.entries(value)) {
11501
- visit(val, path57 ? `${path57}.${key}` : key);
11767
+ visit(val, path58 ? `${path58}.${key}` : key);
11502
11768
  }
11503
11769
  };
11504
11770
  visit(output, "");
@@ -11616,16 +11882,16 @@ var DOWNLOAD_TIMEOUT_MS = 3e5;
11616
11882
  function isDirTarget(output) {
11617
11883
  if (/[\\/]$/.test(output)) return true;
11618
11884
  try {
11619
- return fs44.statSync(output).isDirectory();
11885
+ return fs45.statSync(output).isDirectory();
11620
11886
  } catch {
11621
11887
  return false;
11622
11888
  }
11623
11889
  }
11624
11890
  function resolveDestination(predictionId, index, count, ext, opts) {
11625
- if (!opts.output) return path51.join(opts.outDir, outputFilename(predictionId, index, ext));
11626
- const output = path51.resolve(opts.output);
11627
- if (isDirTarget(opts.output)) return path51.join(output, outputFilename(predictionId, index, ext));
11628
- const givenExt = path51.extname(output);
11891
+ if (!opts.output) return path52.join(opts.outDir, outputFilename(predictionId, index, ext));
11892
+ const output = path52.resolve(opts.output);
11893
+ if (isDirTarget(opts.output)) return path52.join(output, outputFilename(predictionId, index, ext));
11894
+ const givenExt = path52.extname(output);
11629
11895
  if (count === 1) return givenExt ? output : `${output}${ext}`;
11630
11896
  const base = givenExt ? output.slice(0, -givenExt.length) : output;
11631
11897
  return `${base}-${String(index + 1).padStart(2, "0")}${ext}`;
@@ -11643,8 +11909,8 @@ async function saveOne(item, predictionId, index, count, opts) {
11643
11909
  extFromUrl4(item.url) ?? ".bin",
11644
11910
  opts
11645
11911
  );
11646
- fs44.mkdirSync(path51.dirname(dest), { recursive: true });
11647
- fs44.writeFileSync(dest, bytes);
11912
+ fs45.mkdirSync(path52.dirname(dest), { recursive: true });
11913
+ fs45.writeFileSync(dest, bytes);
11648
11914
  return dest;
11649
11915
  }
11650
11916
  const warned2 = opts.warnedHosts;
@@ -11658,7 +11924,7 @@ async function saveOne(item, predictionId, index, count, opts) {
11658
11924
  maxBytes: opts.maxBytes,
11659
11925
  fetch: opts.fetch,
11660
11926
  validateUrl: (url) => {
11661
- const { host, trusted } = checkOutputUrl(url);
11927
+ const { host, trusted } = checkOutputUrl2(url);
11662
11928
  if (!trusted && !warned2.has(host)) {
11663
11929
  warned2.add(host);
11664
11930
  opts.logger.warn(
@@ -11859,8 +12125,8 @@ function registerReplicateImageCommand(parent) {
11859
12125
 
11860
12126
  // src/providers/replicate/node-input-files.ts
11861
12127
  init_errors();
11862
- import fs45 from "fs";
11863
- import path52 from "path";
12128
+ import fs46 from "fs";
12129
+ import path53 from "path";
11864
12130
  var DATA_URI_MAX_BYTES = 256 * 1024;
11865
12131
  var UPLOAD_MAX_BYTES = 100 * 1024 * 1024;
11866
12132
  function formatMb4(bytes) {
@@ -11868,10 +12134,10 @@ function formatMb4(bytes) {
11868
12134
  }
11869
12135
  async function resolveFileInput(value, client, logger) {
11870
12136
  if (isMediaRef(value)) return value;
11871
- const filePath = path52.resolve(value);
12137
+ const filePath = path53.resolve(value);
11872
12138
  let stat;
11873
12139
  try {
11874
- stat = fs45.statSync(filePath);
12140
+ stat = fs46.statSync(filePath);
11875
12141
  } catch {
11876
12142
  throw new ValidationError(`Input file not found: ${value} (pass a local path or an https URL)`);
11877
12143
  }
@@ -11882,9 +12148,9 @@ async function resolveFileInput(value, client, logger) {
11882
12148
  `Input file ${value} is ${formatMb4(stat.size)}; Replicate accepts uploads under 100MB. Host it and pass an https URL instead.`
11883
12149
  );
11884
12150
  }
11885
- const filename = path52.basename(filePath);
12151
+ const filename = path53.basename(filePath);
11886
12152
  const contentType = mimeFromFilename(filename);
11887
- const bytes = fs45.readFileSync(filePath);
12153
+ const bytes = fs46.readFileSync(filePath);
11888
12154
  if (stat.size <= DATA_URI_MAX_BYTES) {
11889
12155
  logger.debug(`Inlining ${filename} (${stat.size} bytes) as a data URI`);
11890
12156
  return `data:${contentType};base64,${bytes.toString("base64")}`;
@@ -12011,8 +12277,8 @@ function registerReplicateCommands(program2) {
12011
12277
  }
12012
12278
 
12013
12279
  // src/commands/check.ts
12014
- import fs46 from "fs";
12015
- import os4 from "os";
12280
+ import fs47 from "fs";
12281
+ import os5 from "os";
12016
12282
 
12017
12283
  // src/commands/check-helpers/binary-check.ts
12018
12284
  import { execa as execa2 } from "execa";
@@ -12260,8 +12526,8 @@ function registerCheckCommand(program2) {
12260
12526
  logger.info(`Env files loaded: ${files.length > 0 ? files.join(", ") : "none"}`);
12261
12527
  }
12262
12528
  if (!legacyClaudeEnvEnabled()) {
12263
- const home = process.env.MULTIX_DISABLE_HOME_ENV ? void 0 : os4.homedir();
12264
- const legacy = legacyClaudeEnvFiles(process.cwd(), home).filter((f) => fs46.existsSync(f));
12529
+ const home = process.env.MULTIX_DISABLE_HOME_ENV ? void 0 : os5.homedir();
12530
+ const legacy = legacyClaudeEnvFiles(process.cwd(), home).filter((f) => fs47.existsSync(f));
12265
12531
  if (legacy.length > 0) {
12266
12532
  logger.info(
12267
12533
  `Found ${legacy.length} legacy .claude env file(s); set MULTIX_LEGACY_CLAUDE_ENV=1 to load them`
@@ -12378,8 +12644,8 @@ function registerCheckCommand(program2) {
12378
12644
  }
12379
12645
 
12380
12646
  // src/commands/doc/convert.ts
12381
- import fs47 from "fs";
12382
- import path53 from "path";
12647
+ import fs48 from "fs";
12648
+ import path54 from "path";
12383
12649
 
12384
12650
  // src/commands/doc/default-prompt.ts
12385
12651
  var DEFAULT_CONVERT_PROMPT = `Convert this document to clean, well-formatted Markdown.
@@ -12397,7 +12663,7 @@ Output only the markdown content without any preamble or explanation.`;
12397
12663
  // src/commands/doc/convert.ts
12398
12664
  function autoNameBase(input) {
12399
12665
  if (input === STDIN_ARG) return "stdin";
12400
- return path53.basename(input, path53.extname(input));
12666
+ return path54.basename(input, path54.extname(input));
12401
12667
  }
12402
12668
  async function convertDocuments(opts) {
12403
12669
  const { output, autoName = false, verbose, logger } = opts;
@@ -12414,13 +12680,13 @@ async function convertDocuments(opts) {
12414
12680
  try {
12415
12681
  let outputPath;
12416
12682
  if (output) {
12417
- outputPath = path53.resolve(output);
12683
+ outputPath = path54.resolve(output);
12418
12684
  } else if (autoName && opts.inputs.length === 1 && opts.inputs[0]) {
12419
- outputPath = path53.join(getOutputDir(), `${autoNameBase(opts.inputs[0])}-extraction.md`);
12685
+ outputPath = path54.join(getOutputDir(), `${autoNameBase(opts.inputs[0])}-extraction.md`);
12420
12686
  } else {
12421
- outputPath = path53.join(getOutputDir(), "document-extraction.md");
12687
+ outputPath = path54.join(getOutputDir(), "document-extraction.md");
12422
12688
  }
12423
- fs47.mkdirSync(path53.dirname(outputPath), { recursive: true });
12689
+ fs48.mkdirSync(path54.dirname(outputPath), { recursive: true });
12424
12690
  failed = await convertResolved(inputs, { outputPath, model, prompt, verbose, logger });
12425
12691
  } finally {
12426
12692
  cleanup();
@@ -12493,7 +12759,7 @@ async function convertResolved(inputs, ctx) {
12493
12759
  `);
12494
12760
  for (const r of results) {
12495
12761
  lines.push(`
12496
- ## ${path53.basename(r.file).replace(/[<>]/g, "\\$&")}
12762
+ ## ${path54.basename(r.file).replace(/[<>]/g, "\\$&")}
12497
12763
  `);
12498
12764
  if (r.status === "success" && r.markdown) {
12499
12765
  lines.push(`${r.markdown}
@@ -12506,7 +12772,7 @@ async function convertResolved(inputs, ctx) {
12506
12772
  }
12507
12773
  lines.push("\n---\n");
12508
12774
  }
12509
- fs47.writeFileSync(outputPath, lines.join("\n"), "utf8");
12775
+ fs48.writeFileSync(outputPath, lines.join("\n"), "utf8");
12510
12776
  const succeeded = results.filter((r) => r.status === "success").length;
12511
12777
  console.log(`
12512
12778
  ${"=".repeat(50)}`);
@@ -12560,12 +12826,12 @@ function registerDocCommands(program2) {
12560
12826
 
12561
12827
  // src/commands/media/index.ts
12562
12828
  init_errors();
12563
- import fs50 from "fs";
12564
- import path56 from "path";
12829
+ import fs51 from "fs";
12830
+ import path57 from "path";
12565
12831
 
12566
12832
  // src/commands/media/batch.ts
12567
- import fs48 from "fs";
12568
- import path54 from "path";
12833
+ import fs49 from "fs";
12834
+ import path55 from "path";
12569
12835
 
12570
12836
  // src/commands/media/detect-type.ts
12571
12837
  var VIDEO_EXTS2 = /* @__PURE__ */ new Set([".mp4", ".mov", ".avi", ".mkv", ".webm", ".flv", ".mpg", ".mpeg"]);
@@ -12761,10 +13027,10 @@ async function batchOptimize(opts) {
12761
13027
  verbose,
12762
13028
  logger
12763
13029
  } = opts;
12764
- fs48.mkdirSync(outputDir, { recursive: true });
12765
- const entries = fs48.readdirSync(inputDir);
13030
+ fs49.mkdirSync(outputDir, { recursive: true });
13031
+ const entries = fs49.readdirSync(inputDir);
12766
13032
  const mediaFiles = entries.filter((f) => {
12767
- const ext = path54.extname(f).toLowerCase();
13033
+ const ext = path55.extname(f).toLowerCase();
12768
13034
  return ALL_EXTS.has(ext);
12769
13035
  });
12770
13036
  if (mediaFiles.length === 0) {
@@ -12775,9 +13041,9 @@ async function batchOptimize(opts) {
12775
13041
  let succeeded = 0;
12776
13042
  const errors = [];
12777
13043
  for (const file of mediaFiles) {
12778
- const inputPath = path54.join(inputDir, file);
12779
- const outputPath = path54.join(outputDir, file);
12780
- const ext = path54.extname(file).toLowerCase();
13044
+ const inputPath = path55.join(inputDir, file);
13045
+ const outputPath = path55.join(outputDir, file);
13046
+ const ext = path55.extname(file).toLowerCase();
12781
13047
  const kind = extToKind(ext);
12782
13048
  logger?.debug(`Processing: ${file}`);
12783
13049
  try {
@@ -12814,8 +13080,8 @@ async function batchOptimize(opts) {
12814
13080
 
12815
13081
  // src/commands/media/split-video.ts
12816
13082
  init_errors();
12817
- import fs49 from "fs";
12818
- import path55 from "path";
13083
+ import fs50 from "fs";
13084
+ import path56 from "path";
12819
13085
  async function splitVideo(opts) {
12820
13086
  await assertFfmpeg();
12821
13087
  const { input, outputDir, chunkDuration = 3600, verbose, logger } = opts;
@@ -12826,10 +13092,10 @@ async function splitVideo(opts) {
12826
13092
  logger?.info("Video is short enough \u2014 no splitting needed");
12827
13093
  return [input];
12828
13094
  }
12829
- fs49.mkdirSync(outputDir, { recursive: true });
12830
- const ext = path55.extname(input) || ".mp4";
12831
- const baseName = path55.basename(input, ext);
12832
- const outputPattern = path55.join(outputDir, `${baseName}_chunk_%03d${ext}`);
13095
+ fs50.mkdirSync(outputDir, { recursive: true });
13096
+ const ext = path56.extname(input) || ".mp4";
13097
+ const baseName = path56.basename(input, ext);
13098
+ const outputPattern = path56.join(outputDir, `${baseName}_chunk_%03d${ext}`);
12833
13099
  logger?.debug(`Splitting into ~${numChunks} chunks of ${chunkDuration}s each`);
12834
13100
  const args = [
12835
13101
  "-i",
@@ -12848,8 +13114,8 @@ async function splitVideo(opts) {
12848
13114
  await runFfmpeg(args, { verbose, logger });
12849
13115
  const files = [];
12850
13116
  for (let i = 0; i < numChunks + 1; i++) {
12851
- const fname = path55.join(outputDir, `${baseName}_chunk_${String(i).padStart(3, "0")}${ext}`);
12852
- if (fs49.existsSync(fname)) files.push(fname);
13117
+ const fname = path56.join(outputDir, `${baseName}_chunk_${String(i).padStart(3, "0")}${ext}`);
13118
+ if (fs50.existsSync(fname)) files.push(fname);
12853
13119
  }
12854
13120
  logger?.success(`Created ${files.length} chunks in ${outputDir}`);
12855
13121
  return files;
@@ -12863,9 +13129,9 @@ function registerMediaCommands(program2) {
12863
13129
  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(
12864
13130
  async (opts) => {
12865
13131
  const logger = createLogger({ verbose: opts.verbose ?? false });
12866
- if (!fs50.existsSync(opts.input)) throw new ValidationError(`Input not found: ${opts.input}`);
12867
- fs50.mkdirSync(path56.dirname(path56.resolve(opts.output)), { recursive: true });
12868
- const ext = path56.extname(opts.input).toLowerCase();
13132
+ if (!fs51.existsSync(opts.input)) throw new ValidationError(`Input not found: ${opts.input}`);
13133
+ fs51.mkdirSync(path57.dirname(path57.resolve(opts.output)), { recursive: true });
13134
+ const ext = path57.extname(opts.input).toLowerCase();
12869
13135
  const kind = extToKind(ext);
12870
13136
  if (!kind) throw new ValidationError(`Unsupported file type: ${ext}`);
12871
13137
  const quality = Number.parseInt(opts.quality, 10);
@@ -12905,7 +13171,7 @@ function registerMediaCommands(program2) {
12905
13171
  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(
12906
13172
  async (opts) => {
12907
13173
  const logger = createLogger({ verbose: opts.verbose ?? false });
12908
- if (!fs50.existsSync(opts.input)) throw new ValidationError(`Input not found: ${opts.input}`);
13174
+ if (!fs51.existsSync(opts.input)) throw new ValidationError(`Input not found: ${opts.input}`);
12909
13175
  const chunks = await splitVideo({
12910
13176
  input: opts.input,
12911
13177
  outputDir: opts.outputDir,
@@ -12921,7 +13187,7 @@ Created ${chunks.length} chunk(s) in ${opts.outputDir}`);
12921
13187
  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(
12922
13188
  async (opts) => {
12923
13189
  const logger = createLogger({ verbose: opts.verbose ?? false });
12924
- if (!fs50.existsSync(opts.inputDir))
13190
+ if (!fs51.existsSync(opts.inputDir))
12925
13191
  throw new ValidationError(`Input dir not found: ${opts.inputDir}`);
12926
13192
  const result = await batchOptimize({
12927
13193
  inputDir: opts.inputDir,