@bonginkan/maria 4.3.16 → 4.3.17

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.
@@ -6531,12 +6531,12 @@ __export(src_exports, {
6531
6531
  Response: () => Response,
6532
6532
  blobFrom: () => blobFrom,
6533
6533
  blobFromSync: () => blobFromSync,
6534
- default: () => fetch,
6534
+ default: () => fetch2,
6535
6535
  fileFrom: () => fileFrom,
6536
6536
  fileFromSync: () => fileFromSync,
6537
6537
  isRedirect: () => isRedirect
6538
6538
  });
6539
- async function fetch(url, options_) {
6539
+ async function fetch2(url, options_) {
6540
6540
  return new Promise((resolve2, reject) => {
6541
6541
  const request = new Request(url, options_);
6542
6542
  const { parsedURL, options } = getNodeRequestOptions(request);
@@ -6668,7 +6668,7 @@ async function fetch(url, options_) {
6668
6668
  if (responseReferrerPolicy) {
6669
6669
  requestOptions.referrerPolicy = responseReferrerPolicy;
6670
6670
  }
6671
- resolve2(fetch(new Request(locationURL, requestOptions)));
6671
+ resolve2(fetch2(new Request(locationURL, requestOptions)));
6672
6672
  finalize();
6673
6673
  return;
6674
6674
  }
@@ -6991,6 +6991,15 @@ async function hasCaseInsensitiveCollision(dirFull, targetFile) {
6991
6991
  async function atomicRename(stage, dest) {
6992
6992
  try {
6993
6993
  await fsp__namespace.rename(stage, dest);
6994
+ try {
6995
+ const fd = await fsp__namespace.open(dest, "r");
6996
+ try {
6997
+ await fd.sync();
6998
+ } finally {
6999
+ await fd.close();
7000
+ }
7001
+ } catch {
7002
+ }
6994
7003
  } catch (e2) {
6995
7004
  if (e2 && e2.code === "EXDEV") {
6996
7005
  await fsp__namespace.copyFile(stage, dest);
@@ -7045,7 +7054,8 @@ async function saveArtifacts(ctx, items, manifest) {
7045
7054
  }
7046
7055
  await fsp__namespace.writeFile(stg, it.bytes);
7047
7056
  await atomicRename(stg, dest.full);
7048
- saved.push(dest.rel);
7057
+ const relPosix = dest.rel.replace(/\\/g, "/");
7058
+ saved.push(relPosix);
7049
7059
  }
7050
7060
  if (!ctx.skipManifest) {
7051
7061
  const manifestObj = {
@@ -7064,7 +7074,7 @@ async function saveArtifacts(ctx, items, manifest) {
7064
7074
  }
7065
7075
  await atomicRename(manifestStage, manifestFull);
7066
7076
  await fsp__namespace.rm(stage, { recursive: true, force: true });
7067
- return { files: saved, manifestPath: manifestPathRel };
7077
+ return { files: saved, manifestPath: manifestPathRel.replace(/\\/g, "/") };
7068
7078
  } else {
7069
7079
  await fsp__namespace.rm(stage, { recursive: true, force: true });
7070
7080
  return { files: saved, manifestPath: "" };
@@ -7111,6 +7121,13 @@ var GeminiMediaProvider = class {
7111
7121
  `GeminiMediaProvider.generateImage request failed: model=${modelName}; prompt="${promptPreview}"; error=${errMsg}`
7112
7122
  );
7113
7123
  }
7124
+ const feedback = resp?.response?.promptFeedback;
7125
+ const blockReason = feedback?.blockReason || feedback?.block_reason;
7126
+ if (blockReason) {
7127
+ const modelName2 = this.primaryModel;
7128
+ const reason = String(blockReason);
7129
+ throw new Error(`GeminiMediaProvider.policy_violation: model=${modelName2}; reason=${reason}`);
7130
+ }
7114
7131
  const parts = resp?.response?.candidates?.[0]?.content?.parts || [];
7115
7132
  for (const p of parts) {
7116
7133
  const data = p?.inlineData?.data || p?.inline_data?.data;
@@ -7801,12 +7818,12 @@ var UnifiedBaseProvider = class {
7801
7818
  }
7802
7819
  // Helper method for HTTP requests
7803
7820
  async makeRequest(url, options) {
7804
- const fetch2 = (await Promise.resolve().then(() => (init_src(), src_exports))).default;
7821
+ const fetch3 = (await Promise.resolve().then(() => (init_src(), src_exports))).default;
7805
7822
  const timeoutMs = options.timeout || 3e4;
7806
7823
  const controller = new AbortController();
7807
7824
  const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
7808
7825
  try {
7809
- const response2 = await fetch2(url, {
7826
+ const response2 = await fetch3(url, {
7810
7827
  method: "POST",
7811
7828
  headers: {
7812
7829
  "Content-Type": "application/json",
@@ -7830,12 +7847,12 @@ var UnifiedBaseProvider = class {
7830
7847
  }
7831
7848
  // Helper method for streaming requests
7832
7849
  async makeStreamRequest(url, options) {
7833
- const fetch2 = (await Promise.resolve().then(() => (init_src(), src_exports))).default;
7850
+ const fetch3 = (await Promise.resolve().then(() => (init_src(), src_exports))).default;
7834
7851
  const timeoutMs = options.timeout || 3e4;
7835
7852
  const controller = new AbortController();
7836
7853
  const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
7837
7854
  try {
7838
- const response2 = await fetch2(url, {
7855
+ const response2 = await fetch3(url, {
7839
7856
  method: "POST",
7840
7857
  headers: {
7841
7858
  "Content-Type": "application/json",
@@ -8548,7 +8565,7 @@ app.use(express__default.default.json({ limit: "10mb" }));
8548
8565
  app.use(express__default.default.urlencoded({ extended: true }));
8549
8566
  try {
8550
8567
  const artifactsDir = path__namespace.default.resolve(process.cwd(), "artifacts");
8551
- app.use("/artifacts", express__default.default.static(artifactsDir, { fallthrough: true, extensions: ["json", "png", "jpg", "webp", "mp4"] }));
8568
+ app.use("/artifacts", express__default.default.static(artifactsDir, { fallthrough: true, extensions: ["json", "png", "jpg", "webp", "mp4", "webm", "mov"] }));
8552
8569
  } catch {
8553
8570
  }
8554
8571
  app.use((req, res, next) => {
@@ -8572,7 +8589,7 @@ app.get("/api/status", (req, res) => {
8572
8589
  app.get("/", (req, res) => {
8573
8590
  res.json({
8574
8591
  name: "MARIA CODE API",
8575
- version: "4.3.9",
8592
+ version: "4.3.17",
8576
8593
  status: "running",
8577
8594
  environment: process.env.NODE_ENV || "development",
8578
8595
  endpoints: {
@@ -8587,6 +8604,30 @@ app.get("/", (req, res) => {
8587
8604
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
8588
8605
  });
8589
8606
  });
8607
+ function classifyMediaError(err) {
8608
+ const raw = err;
8609
+ const msg = String(raw?.message || raw || "unknown error");
8610
+ const lower2 = msg.toLowerCase();
8611
+ if (lower2.includes("missing api key") || lower2.includes("api key") && lower2.includes("missing")) {
8612
+ return { status: 503, code: "provider_unavailable", message: "Provider API key is not configured", hint: "Set GOOGLE_API_KEY or GEMINI_API_KEY on the server" };
8613
+ }
8614
+ if (lower2.includes("invalid api key") || lower2.includes("permission denied") || lower2.includes("unauthorized")) {
8615
+ return { status: 502, code: "provider_auth_failed", message: "Provider authentication failed", hint: "Verify your Google AI Studio API key" };
8616
+ }
8617
+ if (lower2.includes("blockreason") || lower2.includes("safety") || lower2.includes("blocked") || lower2.includes("policy")) {
8618
+ return { status: 422, code: "policy_violation", message: "Request was blocked by provider policy", hint: "Modify the prompt to comply with safety policies" };
8619
+ }
8620
+ if (lower2.includes("no inline image returned") || lower2.includes("no video returned") || lower2.includes("refus")) {
8621
+ return { status: 422, code: "content_refused", message: "Model refused or returned no content", hint: "Try rephrasing the prompt or a different model" };
8622
+ }
8623
+ if (lower2.includes("timeout")) {
8624
+ return { status: 504, code: "timeout", message: "Generation timed out", hint: "Please retry later" };
8625
+ }
8626
+ if (lower2.includes("rate limit") || lower2.includes("429")) {
8627
+ return { status: 429, code: "rate_limited", message: "Rate limit exceeded", hint: "Slow down requests or try again shortly" };
8628
+ }
8629
+ return { status: 500, code: "internal_error", message: "Failed to generate media" };
8630
+ }
8590
8631
  async function decodeFirebaseToken(token) {
8591
8632
  try {
8592
8633
  const admin = await import('firebase-admin').catch(() => null);
@@ -8716,9 +8757,10 @@ app.post("/api/v1/image", rateLimitMiddleware, async (req, res) => {
8716
8757
  try {
8717
8758
  await loadProviderKeys();
8718
8759
  const auth = req.headers.authorization;
8719
- if (!auth || !auth.startsWith("Bearer ")) return res.status(401).json({ error: "unauthorized" });
8760
+ if (!auth || !auth.startsWith("Bearer ")) return res.status(401).json({ error: "unauthorized", message: "Login required", hint: "Sign in and retry" });
8720
8761
  const idToken = auth.substring("Bearer ".length).trim();
8721
8762
  const decoded = await decodeFirebaseToken(idToken).catch(() => null);
8763
+ if (!decoded) return res.status(401).json({ error: "unauthorized", message: "Invalid login session", hint: "Re-login to continue" });
8722
8764
  const uid = decoded?.uid || decoded?.sub || "current";
8723
8765
  const { prompt, model, size = "1024x1024", format = "png", count = 1, seed } = req.body || {};
8724
8766
  if (!prompt) return res.status(400).json({ error: "bad_request", message: "prompt required" });
@@ -8760,17 +8802,15 @@ app.post("/api/v1/image", rateLimitMiddleware, async (req, res) => {
8760
8802
  return res.json({ success: true, data: { url: saved.manifestPath, files: saved.files, filesInline, jobId: manifest.trace } });
8761
8803
  } catch (error) {
8762
8804
  console.error("[Image API] Error:", error);
8763
- return res.status(500).json({
8764
- error: "internal_error",
8765
- message: "Failed to generate image"
8766
- });
8805
+ const mapped = classifyMediaError(error);
8806
+ return res.status(mapped.status).json({ error: mapped.code, message: mapped.message, hint: mapped.hint });
8767
8807
  }
8768
8808
  });
8769
8809
  app.post("/api/v1/video", rateLimitMiddleware, async (req, res) => {
8770
8810
  try {
8771
8811
  await loadProviderKeys();
8772
8812
  const auth = req.headers.authorization;
8773
- if (!auth || !auth.startsWith("Bearer ")) return res.status(401).json({ error: "unauthorized" });
8813
+ if (!auth || !auth.startsWith("Bearer ")) return res.status(401).json({ error: "unauthorized", message: "Login required", hint: "Sign in and retry" });
8774
8814
  const { prompt, duration = 8, fps = 24, res: resStr = "1280x720", format = "mp4", model, seed } = req.body || {};
8775
8815
  if (!prompt) return res.status(400).json({ error: "bad_request", message: "prompt required" });
8776
8816
  const m2 = /^(\d{2,4})x(\d{2,4})$/.exec(String(resStr));
@@ -8778,7 +8818,7 @@ app.post("/api/v1/video", rateLimitMiddleware, async (req, res) => {
8778
8818
  const w = +m2[1], h2 = +m2[2];
8779
8819
  const { GoogleGenAI } = __require("@google/genai");
8780
8820
  const apiKey = process.env.GOOGLE_API_KEY || process.env.GEMINI_API_KEY;
8781
- if (!apiKey) return res.status(500).json({ error: "internal_error", message: "No Google API key configured" });
8821
+ if (!apiKey) return res.status(503).json({ error: "provider_unavailable", message: "Provider API key is not configured", hint: "Set GOOGLE_API_KEY or GEMINI_API_KEY on the server" });
8782
8822
  const ai = new GoogleGenAI({ apiKey });
8783
8823
  const veoModel = model && String(model).trim() || process.env.MARIA_VIDEO_MODEL || "veo-3.0-generate-001";
8784
8824
  const aspectRatio = w >= h2 ? "16:9" : "9:16";
@@ -8793,14 +8833,15 @@ app.post("/api/v1/video", rateLimitMiddleware, async (req, res) => {
8793
8833
  // Pass duration/fps to provider to avoid default ~1s clips
8794
8834
  config: { aspectRatio, durationSeconds: effectiveDuration, frameRate: effectiveFps }
8795
8835
  });
8796
- const deadline = Date.now() + 6e5;
8836
+ const deadline = Date.now() + 72e4;
8797
8837
  while (!operation?.done) {
8798
8838
  if (Date.now() > deadline) {
8799
8839
  return res.status(504).json({ error: "timeout", message: "video generation timed out" });
8800
8840
  }
8801
- await new Promise((r2) => setTimeout(r2, 2e3));
8841
+ await new Promise((r2) => setTimeout(r2, 1e4));
8802
8842
  operation = await ai.operations.getVideosOperation({ operation });
8803
8843
  }
8844
+ await new Promise((r2) => setTimeout(r2, 2500));
8804
8845
  const videoRef = operation?.response?.generatedVideos?.[0]?.video;
8805
8846
  const videoMime = videoRef && (videoRef.mimeType || videoRef.mime_type) ? String(videoRef.mimeType || videoRef.mime_type) : void 0;
8806
8847
  if (!videoRef) {
@@ -8820,36 +8861,143 @@ app.post("/api/v1/video", rateLimitMiddleware, async (req, res) => {
8820
8861
  const looksHtml = headStr.includes("<!doctype") || headStr.includes("<html") || headStr.includes("<?xml");
8821
8862
  return (hasFtyp2 || isWebm2) && !looksHtml;
8822
8863
  };
8823
- const ensureFileMaterialized = async (p, waitMs = 1e4) => {
8864
+ const readUint32BE = (buf, off) => (buf[off] << 24 | buf[off + 1] << 16 | buf[off + 2] << 8 | buf[off + 3]) >>> 0;
8865
+ const readUint64BE = (buf, off) => {
8866
+ const hi = readUint32BE(buf, off);
8867
+ const lo = readUint32BE(buf, off + 4);
8868
+ return hi * 2 ** 32 + lo;
8869
+ };
8870
+ const tryParseMp4DurationSec = (buf) => {
8871
+ try {
8872
+ for (let i2 = 0; i2 + 8 < buf.length; ) {
8873
+ const size = readUint32BE(buf, i2);
8874
+ const type = buf.subarray(i2 + 4, i2 + 8).toString("latin1");
8875
+ if (!size || size < 8) break;
8876
+ if (type === "moov") {
8877
+ const end = Math.min(buf.length, i2 + size);
8878
+ let j = i2 + 8;
8879
+ while (j + 8 < end) {
8880
+ const sz = readUint32BE(buf, j);
8881
+ const tp = buf.subarray(j + 4, j + 8).toString("latin1");
8882
+ if (!sz || sz < 8) break;
8883
+ if (tp === "mvhd") {
8884
+ const ver = buf[j + 8];
8885
+ if (ver === 1) {
8886
+ const timescale = readUint32BE(buf, j + 28);
8887
+ const duration2 = readUint64BE(buf, j + 32);
8888
+ if (timescale > 0) return duration2 / timescale;
8889
+ } else {
8890
+ const timescale = readUint32BE(buf, j + 20);
8891
+ const duration2 = readUint32BE(buf, j + 24);
8892
+ if (timescale > 0) return duration2 / timescale;
8893
+ }
8894
+ break;
8895
+ }
8896
+ j += sz;
8897
+ }
8898
+ break;
8899
+ }
8900
+ i2 += size;
8901
+ }
8902
+ } catch {
8903
+ }
8904
+ return null;
8905
+ };
8906
+ const hasAtom = (buf, atom) => {
8907
+ const tgt = Buffer.from(atom, "latin1");
8908
+ return buf.indexOf(tgt) !== -1;
8909
+ };
8910
+ const validateVideoBytes = (buf) => {
8911
+ if (!buf || buf.length < 1024) return { kind: "unknown", ok: false, durationSec: null, reason: "too_small" };
8912
+ const header2 = buf.subarray(0, 256);
8913
+ const headerStr2 = header2.toString("latin1");
8914
+ const hasFtyp2 = headerStr2.indexOf("ftyp") >= 0 || header2.includes(Buffer.from("ftyp", "ascii"));
8915
+ const isWebm2 = header2[0] === 26 && header2[1] === 69 && header2[2] === 223 && header2[3] === 163;
8916
+ if (isWebm2) {
8917
+ return { kind: "webm", ok: buf.length > 2e5, durationSec: null };
8918
+ }
8919
+ if (hasFtyp2) {
8920
+ const majorBrand = header2.subarray(8, 12).toString("latin1").toLowerCase();
8921
+ const kind = majorBrand.startsWith("qt") ? "mov" : "mp4";
8922
+ const hasMoov = hasAtom(buf, "moov");
8923
+ const dur = tryParseMp4DurationSec(buf);
8924
+ const nearTarget = typeof dur === "number" ? dur + 0.75 >= effectiveDuration : false;
8925
+ const ok = hasMoov && nearTarget;
8926
+ return { kind, ok, durationSec: dur, reason: ok ? void 0 : !hasMoov ? "missing_moov" : "short_duration" };
8927
+ }
8928
+ return { kind: "unknown", ok: false, durationSec: null, reason: "unknown_header" };
8929
+ };
8930
+ const ensureFileMaterialized = async (p, waitMs = 45e3) => {
8824
8931
  const deadline2 = Date.now() + waitMs;
8932
+ let lastSize = -1;
8933
+ let stableCount = 0;
8825
8934
  while (true) {
8826
8935
  try {
8827
8936
  const st = await fsp__namespace.default.stat(p);
8828
- if (st && st.size > 0) return;
8937
+ if (st && st.size > 0) {
8938
+ if (st.size === lastSize) {
8939
+ stableCount++;
8940
+ if (stableCount >= 5) return;
8941
+ } else {
8942
+ stableCount = 0;
8943
+ lastSize = st.size;
8944
+ }
8945
+ }
8829
8946
  } catch {
8830
8947
  }
8831
- if (Date.now() > deadline2) throw new Error(`video file not found after download: ${p}`);
8948
+ if (Date.now() > deadline2) throw new Error(`video file not fully materialized: ${p}`);
8832
8949
  await new Promise((r2) => setTimeout(r2, 200));
8833
8950
  }
8834
8951
  };
8835
8952
  const trySdkDownloadOnce = async () => {
8836
8953
  try {
8837
8954
  await ai.files.download({ file: videoRef, downloadPath: tmpOut });
8838
- await ensureFileMaterialized(tmpOut, 15e3);
8955
+ await ensureFileMaterialized(tmpOut, 45e3);
8839
8956
  const bytes = await fsp__namespace.default.readFile(tmpOut);
8840
8957
  if (await isLikelyValidVideo(bytes)) return bytes;
8841
8958
  } catch {
8842
8959
  }
8843
8960
  return null;
8844
8961
  };
8962
+ const tryManualDownload = async () => {
8963
+ try {
8964
+ const fileObj = videoRef;
8965
+ const direct = fileObj?.uri || fileObj?.url || fileObj?.downloadUri || fileObj?.downloadUrl || "";
8966
+ if (!direct || typeof direct !== "string") return null;
8967
+ const res2 = await fetch(direct, {
8968
+ method: "GET",
8969
+ headers: dlApiKey ? { "x-goog-api-key": dlApiKey } : void 0,
8970
+ redirect: "follow"
8971
+ });
8972
+ if (!res2.ok) return null;
8973
+ const lenHeader = res2.headers?.get?.("content-length");
8974
+ const expectedLen = lenHeader ? Number(lenHeader) : void 0;
8975
+ const ab = await res2.arrayBuffer();
8976
+ const buf = Buffer.from(ab);
8977
+ if (typeof expectedLen === "number" && Number.isFinite(expectedLen) && expectedLen > 0 && buf.length !== expectedLen) {
8978
+ return null;
8979
+ }
8980
+ await fsp__namespace.default.writeFile(tmpOut, buf);
8981
+ if (await isLikelyValidVideo(buf)) return buf;
8982
+ } catch {
8983
+ }
8984
+ return null;
8985
+ };
8845
8986
  let videoBytes = null;
8846
- for (let attempt = 1; attempt <= 3 && !videoBytes; attempt++) {
8847
- videoBytes = await trySdkDownloadOnce();
8848
- if (!videoBytes) await new Promise((r2) => setTimeout(r2, 600 * attempt));
8849
- }
8850
- if (!videoBytes) {
8851
- throw new Error("failed to download valid video bytes");
8987
+ const maxAttempts = 10;
8988
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
8989
+ let bytes = await trySdkDownloadOnce();
8990
+ if (!bytes) bytes = await tryManualDownload();
8991
+ if (bytes && await isLikelyValidVideo(bytes)) {
8992
+ const v = validateVideoBytes(bytes);
8993
+ if (v.ok) {
8994
+ videoBytes = bytes;
8995
+ break;
8996
+ }
8997
+ }
8998
+ await new Promise((r2) => setTimeout(r2, Math.min(8e3, 1500 * attempt)));
8852
8999
  }
9000
+ if (!videoBytes) throw new Error("failed to obtain fully materialized video");
8853
9001
  const header = videoBytes.subarray(0, 256);
8854
9002
  const headerStr = header.toString("latin1");
8855
9003
  let outExt = ".mp4";
@@ -8884,7 +9032,8 @@ app.post("/api/v1/video", rateLimitMiddleware, async (req, res) => {
8884
9032
  metrics: { durationMs: Date.now() - startedMs, retries: 0, fallbacks: 0 },
8885
9033
  trace: traceId
8886
9034
  };
8887
- const saved = await saveArtifacts({ root: process.cwd(), kind: "video", baseDir: "artifacts/media/videos", flat: false, skipManifest: true }, [{ bytes: videoBytes, ext: outExt }], manifest);
9035
+ const baseDir = path__namespace.default.join("artifacts", "media", "videos", traceId);
9036
+ const saved = await saveArtifacts({ root: process.cwd(), kind: "video", baseDir, flat: true }, [{ bytes: videoBytes, ext: outExt }], manifest);
8888
9037
  const idemKey = req.headers["idempotency-key"] || void 0;
8889
9038
  const idToken = auth.substring("Bearer ".length).trim();
8890
9039
  const decoded = await decodeFirebaseToken(idToken).catch(() => null);
@@ -8899,13 +9048,11 @@ app.post("/api/v1/video", rateLimitMiddleware, async (req, res) => {
8899
9048
  updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
8900
9049
  uid
8901
9050
  });
8902
- return res.json({ success: true, data: { url: saved.files[0] ? `/${saved.files[0]}` : void 0, files: saved.files, jobId: manifest.trace, applied: { durationSeconds: effectiveDuration, frameRate: effectiveFps } } });
9051
+ return res.json({ success: true, data: { url: saved.manifestPath || (saved.files[0] ? `/${saved.files[0]}` : void 0), files: saved.files, jobId: manifest.trace, applied: { durationSeconds: effectiveDuration, frameRate: effectiveFps } } });
8903
9052
  } catch (error) {
8904
9053
  console.error("[Video API] Error:", error);
8905
- return res.status(500).json({
8906
- error: "internal_error",
8907
- message: "Failed to generate video"
8908
- });
9054
+ const mapped = classifyMediaError(error);
9055
+ return res.status(mapped.status).json({ error: mapped.code, message: mapped.message, hint: mapped.hint });
8909
9056
  }
8910
9057
  });
8911
9058
  app.get("/api/v1/jobs/:id", async (req, res) => {