@bonginkan/maria 4.4.0 → 4.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -6
- package/dist/READY.manifest.json +1 -1
- package/dist/bin/maria.cjs +399 -262
- package/dist/bin/maria.cjs.map +1 -1
- package/dist/cli.cjs +400 -263
- package/dist/cli.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/server/express-server.cjs +140 -4
- package/dist/server/express-server.js +140 -4
- package/dist/server-express.cjs +140 -4
- package/dist/server-express.cjs.map +1 -1
- package/package.json +2 -2
- package/src/slash-commands/READY.manifest.json +1 -1
|
@@ -9506,7 +9506,7 @@ app.get("/api/status", (req, res) => {
|
|
|
9506
9506
|
app.get("/", (req, res) => {
|
|
9507
9507
|
res.json({
|
|
9508
9508
|
name: "MARIA CODE API",
|
|
9509
|
-
version: "4.4.
|
|
9509
|
+
version: "4.4.1",
|
|
9510
9510
|
status: "running",
|
|
9511
9511
|
environment: process.env.NODE_ENV || "development",
|
|
9512
9512
|
endpoints: {
|
|
@@ -10043,12 +10043,18 @@ app.post("/api/v1/video", rateLimitMiddleware, async (req, res) => {
|
|
|
10043
10043
|
}
|
|
10044
10044
|
const requestedProvider = typeof reqProvider === "string" ? reqProvider.toLowerCase() : void 0;
|
|
10045
10045
|
const requestedModel = typeof model === "string" ? String(model).trim().toLowerCase() : void 0;
|
|
10046
|
+
const providerFromModel2 = (() => {
|
|
10047
|
+
if (!requestedModel) return void 0;
|
|
10048
|
+
if (requestedModel.startsWith("sora")) return "openai";
|
|
10049
|
+
if (requestedModel.startsWith("veo") || requestedModel.startsWith("gemini")) return "google";
|
|
10050
|
+
return void 0;
|
|
10051
|
+
})();
|
|
10046
10052
|
const openaiKey = process.env.OPENAI_API_KEY;
|
|
10047
|
-
const
|
|
10048
|
-
if (
|
|
10053
|
+
const goOpenAI = providerFromModel2 === "openai" || requestedProvider === "openai" || !providerFromModel2 && !requestedProvider && (isProOrAbove && !!openaiKey);
|
|
10054
|
+
if (goOpenAI && openaiKey) {
|
|
10049
10055
|
const OpenAI2 = (await import('openai')).default;
|
|
10050
10056
|
const client = new OpenAI2({ apiKey: openaiKey });
|
|
10051
|
-
const soraModel = requestedModel && requestedModel.
|
|
10057
|
+
const soraModel = requestedModel && requestedModel.startsWith("sora") ? requestedModel : "sora-2";
|
|
10052
10058
|
const secondsStr = (() => {
|
|
10053
10059
|
const d = Number(duration) || 8;
|
|
10054
10060
|
if (d <= 4) return "4";
|
|
@@ -10740,6 +10746,136 @@ async function getFirestoreSafe() {
|
|
|
10740
10746
|
return null;
|
|
10741
10747
|
}
|
|
10742
10748
|
}
|
|
10749
|
+
app.post("/api/v1/snapshots", rateLimitMiddleware, async (req, res) => {
|
|
10750
|
+
try {
|
|
10751
|
+
const auth = req.headers.authorization;
|
|
10752
|
+
if (!auth || !auth.startsWith("Bearer ")) return res.status(401).json({ error: "unauthorized" });
|
|
10753
|
+
const idToken = auth.substring("Bearer ".length).trim();
|
|
10754
|
+
const decoded = await decodeFirebaseToken(idToken).catch(() => null);
|
|
10755
|
+
if (!decoded) return res.status(401).json({ error: "unauthorized" });
|
|
10756
|
+
const uid = decoded?.uid || decoded?.sub;
|
|
10757
|
+
const { projectId, taskId, summary, decisions, artifacts, refs, resumePrompt } = req.body || {};
|
|
10758
|
+
const pid = typeof projectId === "string" && projectId.trim() ? String(projectId).trim() : "default";
|
|
10759
|
+
const tid = typeof taskId === "string" && taskId.trim() ? String(taskId).trim() : "";
|
|
10760
|
+
const sum = typeof summary === "string" && summary.trim() ? String(summary).trim() : "";
|
|
10761
|
+
try {
|
|
10762
|
+
console.log("[Snapshots][POST] incoming", { uid: String(uid).slice(0, 8) + "\u2026", projectId: pid, taskId: tid, hasSummary: !!sum });
|
|
10763
|
+
} catch {
|
|
10764
|
+
}
|
|
10765
|
+
if (!tid || !sum) return res.status(400).json({ error: "bad_request", message: "taskId and summary are required" });
|
|
10766
|
+
const db = await getFirestoreSafe();
|
|
10767
|
+
if (!db) return res.status(503).json({ error: "unavailable", message: "database is not configured" });
|
|
10768
|
+
const tsId = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").split(".")[0] + "Z";
|
|
10769
|
+
const userRef = db.collection("users").doc(uid);
|
|
10770
|
+
const projRef = userRef.collection("projects").doc(pid);
|
|
10771
|
+
const taskRef = projRef.collection("tasks").doc(tid);
|
|
10772
|
+
const ref = taskRef.collection("snapshots").doc(tsId);
|
|
10773
|
+
const nowISO = (/* @__PURE__ */ new Date()).toISOString();
|
|
10774
|
+
try {
|
|
10775
|
+
await Promise.all([
|
|
10776
|
+
userRef.set({ updatedAt: nowISO }, { merge: true }),
|
|
10777
|
+
projRef.set({ projectId: pid, updatedAt: nowISO }, { merge: true }),
|
|
10778
|
+
taskRef.set({ taskId: tid, updatedAt: nowISO }, { merge: true })
|
|
10779
|
+
]);
|
|
10780
|
+
} catch (e2) {
|
|
10781
|
+
try {
|
|
10782
|
+
console.warn("[Snapshots][POST] parent set warn:", e2?.message || String(e2));
|
|
10783
|
+
} catch {
|
|
10784
|
+
}
|
|
10785
|
+
}
|
|
10786
|
+
await ref.set({
|
|
10787
|
+
snapshotVersion: 1,
|
|
10788
|
+
uid,
|
|
10789
|
+
projectId: pid,
|
|
10790
|
+
taskId: tid,
|
|
10791
|
+
timestamp: nowISO,
|
|
10792
|
+
summary: sum,
|
|
10793
|
+
decisions: Array.isArray(decisions) ? decisions.slice(0, 50).map(String) : [],
|
|
10794
|
+
artifacts: Array.isArray(artifacts) ? artifacts.slice(0, 200).map(String) : [],
|
|
10795
|
+
links: Array.isArray(refs) ? refs.slice(0, 200).map((r2) => ({ type: "fs", ref: String(r2) })) : [],
|
|
10796
|
+
resumePrompt: typeof resumePrompt === "string" ? String(resumePrompt) : ""
|
|
10797
|
+
});
|
|
10798
|
+
try {
|
|
10799
|
+
console.log("[Snapshots][POST] saved", { path: `users/${uid}/projects/${pid}/tasks/${tid}/snapshots/${tsId}` });
|
|
10800
|
+
} catch {
|
|
10801
|
+
}
|
|
10802
|
+
return res.json({ success: true });
|
|
10803
|
+
} catch (e2) {
|
|
10804
|
+
try {
|
|
10805
|
+
console.error("[Snapshots][POST] error:", e2?.message || String(e2));
|
|
10806
|
+
} catch {
|
|
10807
|
+
}
|
|
10808
|
+
return res.status(500).json({ error: "internal_error" });
|
|
10809
|
+
}
|
|
10810
|
+
});
|
|
10811
|
+
app.get("/api/v1/get-snapshots", rateLimitMiddleware, async (req, res) => {
|
|
10812
|
+
try {
|
|
10813
|
+
const auth = req.headers.authorization;
|
|
10814
|
+
if (!auth || !auth.startsWith("Bearer ")) return res.status(401).json({ error: "unauthorized" });
|
|
10815
|
+
const idToken = auth.substring("Bearer ".length).trim();
|
|
10816
|
+
const decoded = await decodeFirebaseToken(idToken).catch(() => null);
|
|
10817
|
+
if (!decoded) return res.status(401).json({ error: "unauthorized" });
|
|
10818
|
+
const uid = decoded?.uid || decoded?.sub;
|
|
10819
|
+
const projectId = typeof req.query.projectId === "string" && req.query.projectId.trim() ? String(req.query.projectId).trim() : "default";
|
|
10820
|
+
const taskId = typeof req.query.taskId === "string" && req.query.taskId.trim() ? String(req.query.taskId).trim() : void 0;
|
|
10821
|
+
const dateISO = typeof req.query.date === "string" && req.query.date.trim() ? String(req.query.date).trim() : void 0;
|
|
10822
|
+
const limit = Number.isFinite(Number(req.query.limit)) ? Math.max(1, Math.min(50, Number(req.query.limit))) : 5;
|
|
10823
|
+
const db = await getFirestoreSafe();
|
|
10824
|
+
if (!db) return res.status(503).json({ error: "unavailable", message: "database is not configured" });
|
|
10825
|
+
let docs = [];
|
|
10826
|
+
if (taskId) {
|
|
10827
|
+
const q = db.collection("users").doc(uid).collection("projects").doc(projectId).collection("tasks").doc(taskId).collection("snapshots").orderBy("timestamp", "desc").limit(limit);
|
|
10828
|
+
const ss = await q.get();
|
|
10829
|
+
docs = ss.docs.map((d) => d.data());
|
|
10830
|
+
} else if (dateISO) {
|
|
10831
|
+
const start = new Date(dateISO).toISOString();
|
|
10832
|
+
const end = new Date(new Date(dateISO).getTime() + 24 * 60 * 60 * 1e3).toISOString();
|
|
10833
|
+
try {
|
|
10834
|
+
const ss = await db.collectionGroup("snapshots").where("uid", "==", uid).where("projectId", "==", projectId).where("timestamp", ">=", start).where("timestamp", "<", end).orderBy("timestamp", "desc").limit(limit).get();
|
|
10835
|
+
docs = ss.docs.map((d) => d.data());
|
|
10836
|
+
} catch (e2) {
|
|
10837
|
+
const projRef = db.collection("users").doc(uid).collection("projects").doc(projectId);
|
|
10838
|
+
const tasksSnap = await projRef.collection("tasks").get();
|
|
10839
|
+
const collected = [];
|
|
10840
|
+
for (const t2 of tasksSnap.docs) {
|
|
10841
|
+
try {
|
|
10842
|
+
const snaps = await t2.ref.collection("snapshots").orderBy("timestamp", "desc").limit(50).get();
|
|
10843
|
+
for (const s2 of snaps.docs) {
|
|
10844
|
+
const data = s2.data();
|
|
10845
|
+
const ts = String(data?.timestamp || "");
|
|
10846
|
+
if (ts >= start && ts < end) collected.push(data);
|
|
10847
|
+
}
|
|
10848
|
+
} catch {
|
|
10849
|
+
}
|
|
10850
|
+
}
|
|
10851
|
+
collected.sort((a, b) => String(b?.timestamp || "").localeCompare(String(a?.timestamp || "")));
|
|
10852
|
+
docs = collected.slice(0, limit);
|
|
10853
|
+
}
|
|
10854
|
+
} else {
|
|
10855
|
+
try {
|
|
10856
|
+
const ss = await db.collectionGroup("snapshots").where("uid", "==", uid).where("projectId", "==", projectId).orderBy("timestamp", "desc").limit(limit).get();
|
|
10857
|
+
docs = ss.docs.map((d) => d.data());
|
|
10858
|
+
} catch (e2) {
|
|
10859
|
+
const projRef = db.collection("users").doc(uid).collection("projects").doc(projectId);
|
|
10860
|
+
const tasksSnap = await projRef.collection("tasks").get();
|
|
10861
|
+
const perTaskTop = [];
|
|
10862
|
+
for (const t2 of tasksSnap.docs) {
|
|
10863
|
+
try {
|
|
10864
|
+
const snaps = await t2.ref.collection("snapshots").orderBy("timestamp", "desc").limit(Math.max(1, Math.ceil(limit / Math.max(1, tasksSnap.size)))).get();
|
|
10865
|
+
for (const s2 of snaps.docs) perTaskTop.push(s2.data());
|
|
10866
|
+
} catch {
|
|
10867
|
+
}
|
|
10868
|
+
}
|
|
10869
|
+
perTaskTop.sort((a, b) => String(b?.timestamp || "").localeCompare(String(a?.timestamp || "")));
|
|
10870
|
+
docs = perTaskTop.slice(0, limit);
|
|
10871
|
+
}
|
|
10872
|
+
}
|
|
10873
|
+
return res.json({ success: true, data: { snapshots: docs } });
|
|
10874
|
+
} catch (e2) {
|
|
10875
|
+
console.error("[Snapshots] GET error", e2);
|
|
10876
|
+
return res.status(500).json({ error: "internal_error" });
|
|
10877
|
+
}
|
|
10878
|
+
});
|
|
10743
10879
|
app.get("/api/v1/usage", rateLimitMiddleware, async (req, res) => {
|
|
10744
10880
|
try {
|
|
10745
10881
|
const auth = req.headers.authorization;
|
|
@@ -9506,7 +9506,7 @@ app.get("/api/status", (req, res) => {
|
|
|
9506
9506
|
app.get("/", (req, res) => {
|
|
9507
9507
|
res.json({
|
|
9508
9508
|
name: "MARIA CODE API",
|
|
9509
|
-
version: "4.4.
|
|
9509
|
+
version: "4.4.1",
|
|
9510
9510
|
status: "running",
|
|
9511
9511
|
environment: process.env.NODE_ENV || "development",
|
|
9512
9512
|
endpoints: {
|
|
@@ -10043,12 +10043,18 @@ app.post("/api/v1/video", rateLimitMiddleware, async (req, res) => {
|
|
|
10043
10043
|
}
|
|
10044
10044
|
const requestedProvider = typeof reqProvider === "string" ? reqProvider.toLowerCase() : void 0;
|
|
10045
10045
|
const requestedModel = typeof model === "string" ? String(model).trim().toLowerCase() : void 0;
|
|
10046
|
+
const providerFromModel2 = (() => {
|
|
10047
|
+
if (!requestedModel) return void 0;
|
|
10048
|
+
if (requestedModel.startsWith("sora")) return "openai";
|
|
10049
|
+
if (requestedModel.startsWith("veo") || requestedModel.startsWith("gemini")) return "google";
|
|
10050
|
+
return void 0;
|
|
10051
|
+
})();
|
|
10046
10052
|
const openaiKey = process.env.OPENAI_API_KEY;
|
|
10047
|
-
const
|
|
10048
|
-
if (
|
|
10053
|
+
const goOpenAI = providerFromModel2 === "openai" || requestedProvider === "openai" || !providerFromModel2 && !requestedProvider && (isProOrAbove && !!openaiKey);
|
|
10054
|
+
if (goOpenAI && openaiKey) {
|
|
10049
10055
|
const OpenAI2 = (await import('openai')).default;
|
|
10050
10056
|
const client = new OpenAI2({ apiKey: openaiKey });
|
|
10051
|
-
const soraModel = requestedModel && requestedModel.
|
|
10057
|
+
const soraModel = requestedModel && requestedModel.startsWith("sora") ? requestedModel : "sora-2";
|
|
10052
10058
|
const secondsStr = (() => {
|
|
10053
10059
|
const d = Number(duration) || 8;
|
|
10054
10060
|
if (d <= 4) return "4";
|
|
@@ -10740,6 +10746,136 @@ async function getFirestoreSafe() {
|
|
|
10740
10746
|
return null;
|
|
10741
10747
|
}
|
|
10742
10748
|
}
|
|
10749
|
+
app.post("/api/v1/snapshots", rateLimitMiddleware, async (req, res) => {
|
|
10750
|
+
try {
|
|
10751
|
+
const auth = req.headers.authorization;
|
|
10752
|
+
if (!auth || !auth.startsWith("Bearer ")) return res.status(401).json({ error: "unauthorized" });
|
|
10753
|
+
const idToken = auth.substring("Bearer ".length).trim();
|
|
10754
|
+
const decoded = await decodeFirebaseToken(idToken).catch(() => null);
|
|
10755
|
+
if (!decoded) return res.status(401).json({ error: "unauthorized" });
|
|
10756
|
+
const uid = decoded?.uid || decoded?.sub;
|
|
10757
|
+
const { projectId, taskId, summary, decisions, artifacts, refs, resumePrompt } = req.body || {};
|
|
10758
|
+
const pid = typeof projectId === "string" && projectId.trim() ? String(projectId).trim() : "default";
|
|
10759
|
+
const tid = typeof taskId === "string" && taskId.trim() ? String(taskId).trim() : "";
|
|
10760
|
+
const sum = typeof summary === "string" && summary.trim() ? String(summary).trim() : "";
|
|
10761
|
+
try {
|
|
10762
|
+
console.log("[Snapshots][POST] incoming", { uid: String(uid).slice(0, 8) + "\u2026", projectId: pid, taskId: tid, hasSummary: !!sum });
|
|
10763
|
+
} catch {
|
|
10764
|
+
}
|
|
10765
|
+
if (!tid || !sum) return res.status(400).json({ error: "bad_request", message: "taskId and summary are required" });
|
|
10766
|
+
const db = await getFirestoreSafe();
|
|
10767
|
+
if (!db) return res.status(503).json({ error: "unavailable", message: "database is not configured" });
|
|
10768
|
+
const tsId = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").split(".")[0] + "Z";
|
|
10769
|
+
const userRef = db.collection("users").doc(uid);
|
|
10770
|
+
const projRef = userRef.collection("projects").doc(pid);
|
|
10771
|
+
const taskRef = projRef.collection("tasks").doc(tid);
|
|
10772
|
+
const ref = taskRef.collection("snapshots").doc(tsId);
|
|
10773
|
+
const nowISO = (/* @__PURE__ */ new Date()).toISOString();
|
|
10774
|
+
try {
|
|
10775
|
+
await Promise.all([
|
|
10776
|
+
userRef.set({ updatedAt: nowISO }, { merge: true }),
|
|
10777
|
+
projRef.set({ projectId: pid, updatedAt: nowISO }, { merge: true }),
|
|
10778
|
+
taskRef.set({ taskId: tid, updatedAt: nowISO }, { merge: true })
|
|
10779
|
+
]);
|
|
10780
|
+
} catch (e2) {
|
|
10781
|
+
try {
|
|
10782
|
+
console.warn("[Snapshots][POST] parent set warn:", e2?.message || String(e2));
|
|
10783
|
+
} catch {
|
|
10784
|
+
}
|
|
10785
|
+
}
|
|
10786
|
+
await ref.set({
|
|
10787
|
+
snapshotVersion: 1,
|
|
10788
|
+
uid,
|
|
10789
|
+
projectId: pid,
|
|
10790
|
+
taskId: tid,
|
|
10791
|
+
timestamp: nowISO,
|
|
10792
|
+
summary: sum,
|
|
10793
|
+
decisions: Array.isArray(decisions) ? decisions.slice(0, 50).map(String) : [],
|
|
10794
|
+
artifacts: Array.isArray(artifacts) ? artifacts.slice(0, 200).map(String) : [],
|
|
10795
|
+
links: Array.isArray(refs) ? refs.slice(0, 200).map((r2) => ({ type: "fs", ref: String(r2) })) : [],
|
|
10796
|
+
resumePrompt: typeof resumePrompt === "string" ? String(resumePrompt) : ""
|
|
10797
|
+
});
|
|
10798
|
+
try {
|
|
10799
|
+
console.log("[Snapshots][POST] saved", { path: `users/${uid}/projects/${pid}/tasks/${tid}/snapshots/${tsId}` });
|
|
10800
|
+
} catch {
|
|
10801
|
+
}
|
|
10802
|
+
return res.json({ success: true });
|
|
10803
|
+
} catch (e2) {
|
|
10804
|
+
try {
|
|
10805
|
+
console.error("[Snapshots][POST] error:", e2?.message || String(e2));
|
|
10806
|
+
} catch {
|
|
10807
|
+
}
|
|
10808
|
+
return res.status(500).json({ error: "internal_error" });
|
|
10809
|
+
}
|
|
10810
|
+
});
|
|
10811
|
+
app.get("/api/v1/get-snapshots", rateLimitMiddleware, async (req, res) => {
|
|
10812
|
+
try {
|
|
10813
|
+
const auth = req.headers.authorization;
|
|
10814
|
+
if (!auth || !auth.startsWith("Bearer ")) return res.status(401).json({ error: "unauthorized" });
|
|
10815
|
+
const idToken = auth.substring("Bearer ".length).trim();
|
|
10816
|
+
const decoded = await decodeFirebaseToken(idToken).catch(() => null);
|
|
10817
|
+
if (!decoded) return res.status(401).json({ error: "unauthorized" });
|
|
10818
|
+
const uid = decoded?.uid || decoded?.sub;
|
|
10819
|
+
const projectId = typeof req.query.projectId === "string" && req.query.projectId.trim() ? String(req.query.projectId).trim() : "default";
|
|
10820
|
+
const taskId = typeof req.query.taskId === "string" && req.query.taskId.trim() ? String(req.query.taskId).trim() : void 0;
|
|
10821
|
+
const dateISO = typeof req.query.date === "string" && req.query.date.trim() ? String(req.query.date).trim() : void 0;
|
|
10822
|
+
const limit = Number.isFinite(Number(req.query.limit)) ? Math.max(1, Math.min(50, Number(req.query.limit))) : 5;
|
|
10823
|
+
const db = await getFirestoreSafe();
|
|
10824
|
+
if (!db) return res.status(503).json({ error: "unavailable", message: "database is not configured" });
|
|
10825
|
+
let docs = [];
|
|
10826
|
+
if (taskId) {
|
|
10827
|
+
const q = db.collection("users").doc(uid).collection("projects").doc(projectId).collection("tasks").doc(taskId).collection("snapshots").orderBy("timestamp", "desc").limit(limit);
|
|
10828
|
+
const ss = await q.get();
|
|
10829
|
+
docs = ss.docs.map((d) => d.data());
|
|
10830
|
+
} else if (dateISO) {
|
|
10831
|
+
const start = new Date(dateISO).toISOString();
|
|
10832
|
+
const end = new Date(new Date(dateISO).getTime() + 24 * 60 * 60 * 1e3).toISOString();
|
|
10833
|
+
try {
|
|
10834
|
+
const ss = await db.collectionGroup("snapshots").where("uid", "==", uid).where("projectId", "==", projectId).where("timestamp", ">=", start).where("timestamp", "<", end).orderBy("timestamp", "desc").limit(limit).get();
|
|
10835
|
+
docs = ss.docs.map((d) => d.data());
|
|
10836
|
+
} catch (e2) {
|
|
10837
|
+
const projRef = db.collection("users").doc(uid).collection("projects").doc(projectId);
|
|
10838
|
+
const tasksSnap = await projRef.collection("tasks").get();
|
|
10839
|
+
const collected = [];
|
|
10840
|
+
for (const t2 of tasksSnap.docs) {
|
|
10841
|
+
try {
|
|
10842
|
+
const snaps = await t2.ref.collection("snapshots").orderBy("timestamp", "desc").limit(50).get();
|
|
10843
|
+
for (const s2 of snaps.docs) {
|
|
10844
|
+
const data = s2.data();
|
|
10845
|
+
const ts = String(data?.timestamp || "");
|
|
10846
|
+
if (ts >= start && ts < end) collected.push(data);
|
|
10847
|
+
}
|
|
10848
|
+
} catch {
|
|
10849
|
+
}
|
|
10850
|
+
}
|
|
10851
|
+
collected.sort((a, b) => String(b?.timestamp || "").localeCompare(String(a?.timestamp || "")));
|
|
10852
|
+
docs = collected.slice(0, limit);
|
|
10853
|
+
}
|
|
10854
|
+
} else {
|
|
10855
|
+
try {
|
|
10856
|
+
const ss = await db.collectionGroup("snapshots").where("uid", "==", uid).where("projectId", "==", projectId).orderBy("timestamp", "desc").limit(limit).get();
|
|
10857
|
+
docs = ss.docs.map((d) => d.data());
|
|
10858
|
+
} catch (e2) {
|
|
10859
|
+
const projRef = db.collection("users").doc(uid).collection("projects").doc(projectId);
|
|
10860
|
+
const tasksSnap = await projRef.collection("tasks").get();
|
|
10861
|
+
const perTaskTop = [];
|
|
10862
|
+
for (const t2 of tasksSnap.docs) {
|
|
10863
|
+
try {
|
|
10864
|
+
const snaps = await t2.ref.collection("snapshots").orderBy("timestamp", "desc").limit(Math.max(1, Math.ceil(limit / Math.max(1, tasksSnap.size)))).get();
|
|
10865
|
+
for (const s2 of snaps.docs) perTaskTop.push(s2.data());
|
|
10866
|
+
} catch {
|
|
10867
|
+
}
|
|
10868
|
+
}
|
|
10869
|
+
perTaskTop.sort((a, b) => String(b?.timestamp || "").localeCompare(String(a?.timestamp || "")));
|
|
10870
|
+
docs = perTaskTop.slice(0, limit);
|
|
10871
|
+
}
|
|
10872
|
+
}
|
|
10873
|
+
return res.json({ success: true, data: { snapshots: docs } });
|
|
10874
|
+
} catch (e2) {
|
|
10875
|
+
console.error("[Snapshots] GET error", e2);
|
|
10876
|
+
return res.status(500).json({ error: "internal_error" });
|
|
10877
|
+
}
|
|
10878
|
+
});
|
|
10743
10879
|
app.get("/api/v1/usage", rateLimitMiddleware, async (req, res) => {
|
|
10744
10880
|
try {
|
|
10745
10881
|
const auth = req.headers.authorization;
|
package/dist/server-express.cjs
CHANGED
|
@@ -9506,7 +9506,7 @@ app.get("/api/status", (req, res) => {
|
|
|
9506
9506
|
app.get("/", (req, res) => {
|
|
9507
9507
|
res.json({
|
|
9508
9508
|
name: "MARIA CODE API",
|
|
9509
|
-
version: "4.4.
|
|
9509
|
+
version: "4.4.1",
|
|
9510
9510
|
status: "running",
|
|
9511
9511
|
environment: process.env.NODE_ENV || "development",
|
|
9512
9512
|
endpoints: {
|
|
@@ -10043,12 +10043,18 @@ app.post("/api/v1/video", rateLimitMiddleware, async (req, res) => {
|
|
|
10043
10043
|
}
|
|
10044
10044
|
const requestedProvider = typeof reqProvider === "string" ? reqProvider.toLowerCase() : void 0;
|
|
10045
10045
|
const requestedModel = typeof model === "string" ? String(model).trim().toLowerCase() : void 0;
|
|
10046
|
+
const providerFromModel2 = (() => {
|
|
10047
|
+
if (!requestedModel) return void 0;
|
|
10048
|
+
if (requestedModel.startsWith("sora")) return "openai";
|
|
10049
|
+
if (requestedModel.startsWith("veo") || requestedModel.startsWith("gemini")) return "google";
|
|
10050
|
+
return void 0;
|
|
10051
|
+
})();
|
|
10046
10052
|
const openaiKey = process.env.OPENAI_API_KEY;
|
|
10047
|
-
const
|
|
10048
|
-
if (
|
|
10053
|
+
const goOpenAI = providerFromModel2 === "openai" || requestedProvider === "openai" || !providerFromModel2 && !requestedProvider && (isProOrAbove && !!openaiKey);
|
|
10054
|
+
if (goOpenAI && openaiKey) {
|
|
10049
10055
|
const OpenAI2 = (await import('openai')).default;
|
|
10050
10056
|
const client = new OpenAI2({ apiKey: openaiKey });
|
|
10051
|
-
const soraModel = requestedModel && requestedModel.
|
|
10057
|
+
const soraModel = requestedModel && requestedModel.startsWith("sora") ? requestedModel : "sora-2";
|
|
10052
10058
|
const secondsStr = (() => {
|
|
10053
10059
|
const d = Number(duration) || 8;
|
|
10054
10060
|
if (d <= 4) return "4";
|
|
@@ -10740,6 +10746,136 @@ async function getFirestoreSafe() {
|
|
|
10740
10746
|
return null;
|
|
10741
10747
|
}
|
|
10742
10748
|
}
|
|
10749
|
+
app.post("/api/v1/snapshots", rateLimitMiddleware, async (req, res) => {
|
|
10750
|
+
try {
|
|
10751
|
+
const auth = req.headers.authorization;
|
|
10752
|
+
if (!auth || !auth.startsWith("Bearer ")) return res.status(401).json({ error: "unauthorized" });
|
|
10753
|
+
const idToken = auth.substring("Bearer ".length).trim();
|
|
10754
|
+
const decoded = await decodeFirebaseToken(idToken).catch(() => null);
|
|
10755
|
+
if (!decoded) return res.status(401).json({ error: "unauthorized" });
|
|
10756
|
+
const uid = decoded?.uid || decoded?.sub;
|
|
10757
|
+
const { projectId, taskId, summary, decisions, artifacts, refs, resumePrompt } = req.body || {};
|
|
10758
|
+
const pid = typeof projectId === "string" && projectId.trim() ? String(projectId).trim() : "default";
|
|
10759
|
+
const tid = typeof taskId === "string" && taskId.trim() ? String(taskId).trim() : "";
|
|
10760
|
+
const sum = typeof summary === "string" && summary.trim() ? String(summary).trim() : "";
|
|
10761
|
+
try {
|
|
10762
|
+
console.log("[Snapshots][POST] incoming", { uid: String(uid).slice(0, 8) + "\u2026", projectId: pid, taskId: tid, hasSummary: !!sum });
|
|
10763
|
+
} catch {
|
|
10764
|
+
}
|
|
10765
|
+
if (!tid || !sum) return res.status(400).json({ error: "bad_request", message: "taskId and summary are required" });
|
|
10766
|
+
const db = await getFirestoreSafe();
|
|
10767
|
+
if (!db) return res.status(503).json({ error: "unavailable", message: "database is not configured" });
|
|
10768
|
+
const tsId = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").split(".")[0] + "Z";
|
|
10769
|
+
const userRef = db.collection("users").doc(uid);
|
|
10770
|
+
const projRef = userRef.collection("projects").doc(pid);
|
|
10771
|
+
const taskRef = projRef.collection("tasks").doc(tid);
|
|
10772
|
+
const ref = taskRef.collection("snapshots").doc(tsId);
|
|
10773
|
+
const nowISO = (/* @__PURE__ */ new Date()).toISOString();
|
|
10774
|
+
try {
|
|
10775
|
+
await Promise.all([
|
|
10776
|
+
userRef.set({ updatedAt: nowISO }, { merge: true }),
|
|
10777
|
+
projRef.set({ projectId: pid, updatedAt: nowISO }, { merge: true }),
|
|
10778
|
+
taskRef.set({ taskId: tid, updatedAt: nowISO }, { merge: true })
|
|
10779
|
+
]);
|
|
10780
|
+
} catch (e2) {
|
|
10781
|
+
try {
|
|
10782
|
+
console.warn("[Snapshots][POST] parent set warn:", e2?.message || String(e2));
|
|
10783
|
+
} catch {
|
|
10784
|
+
}
|
|
10785
|
+
}
|
|
10786
|
+
await ref.set({
|
|
10787
|
+
snapshotVersion: 1,
|
|
10788
|
+
uid,
|
|
10789
|
+
projectId: pid,
|
|
10790
|
+
taskId: tid,
|
|
10791
|
+
timestamp: nowISO,
|
|
10792
|
+
summary: sum,
|
|
10793
|
+
decisions: Array.isArray(decisions) ? decisions.slice(0, 50).map(String) : [],
|
|
10794
|
+
artifacts: Array.isArray(artifacts) ? artifacts.slice(0, 200).map(String) : [],
|
|
10795
|
+
links: Array.isArray(refs) ? refs.slice(0, 200).map((r2) => ({ type: "fs", ref: String(r2) })) : [],
|
|
10796
|
+
resumePrompt: typeof resumePrompt === "string" ? String(resumePrompt) : ""
|
|
10797
|
+
});
|
|
10798
|
+
try {
|
|
10799
|
+
console.log("[Snapshots][POST] saved", { path: `users/${uid}/projects/${pid}/tasks/${tid}/snapshots/${tsId}` });
|
|
10800
|
+
} catch {
|
|
10801
|
+
}
|
|
10802
|
+
return res.json({ success: true });
|
|
10803
|
+
} catch (e2) {
|
|
10804
|
+
try {
|
|
10805
|
+
console.error("[Snapshots][POST] error:", e2?.message || String(e2));
|
|
10806
|
+
} catch {
|
|
10807
|
+
}
|
|
10808
|
+
return res.status(500).json({ error: "internal_error" });
|
|
10809
|
+
}
|
|
10810
|
+
});
|
|
10811
|
+
app.get("/api/v1/get-snapshots", rateLimitMiddleware, async (req, res) => {
|
|
10812
|
+
try {
|
|
10813
|
+
const auth = req.headers.authorization;
|
|
10814
|
+
if (!auth || !auth.startsWith("Bearer ")) return res.status(401).json({ error: "unauthorized" });
|
|
10815
|
+
const idToken = auth.substring("Bearer ".length).trim();
|
|
10816
|
+
const decoded = await decodeFirebaseToken(idToken).catch(() => null);
|
|
10817
|
+
if (!decoded) return res.status(401).json({ error: "unauthorized" });
|
|
10818
|
+
const uid = decoded?.uid || decoded?.sub;
|
|
10819
|
+
const projectId = typeof req.query.projectId === "string" && req.query.projectId.trim() ? String(req.query.projectId).trim() : "default";
|
|
10820
|
+
const taskId = typeof req.query.taskId === "string" && req.query.taskId.trim() ? String(req.query.taskId).trim() : void 0;
|
|
10821
|
+
const dateISO = typeof req.query.date === "string" && req.query.date.trim() ? String(req.query.date).trim() : void 0;
|
|
10822
|
+
const limit = Number.isFinite(Number(req.query.limit)) ? Math.max(1, Math.min(50, Number(req.query.limit))) : 5;
|
|
10823
|
+
const db = await getFirestoreSafe();
|
|
10824
|
+
if (!db) return res.status(503).json({ error: "unavailable", message: "database is not configured" });
|
|
10825
|
+
let docs = [];
|
|
10826
|
+
if (taskId) {
|
|
10827
|
+
const q = db.collection("users").doc(uid).collection("projects").doc(projectId).collection("tasks").doc(taskId).collection("snapshots").orderBy("timestamp", "desc").limit(limit);
|
|
10828
|
+
const ss = await q.get();
|
|
10829
|
+
docs = ss.docs.map((d) => d.data());
|
|
10830
|
+
} else if (dateISO) {
|
|
10831
|
+
const start = new Date(dateISO).toISOString();
|
|
10832
|
+
const end = new Date(new Date(dateISO).getTime() + 24 * 60 * 60 * 1e3).toISOString();
|
|
10833
|
+
try {
|
|
10834
|
+
const ss = await db.collectionGroup("snapshots").where("uid", "==", uid).where("projectId", "==", projectId).where("timestamp", ">=", start).where("timestamp", "<", end).orderBy("timestamp", "desc").limit(limit).get();
|
|
10835
|
+
docs = ss.docs.map((d) => d.data());
|
|
10836
|
+
} catch (e2) {
|
|
10837
|
+
const projRef = db.collection("users").doc(uid).collection("projects").doc(projectId);
|
|
10838
|
+
const tasksSnap = await projRef.collection("tasks").get();
|
|
10839
|
+
const collected = [];
|
|
10840
|
+
for (const t2 of tasksSnap.docs) {
|
|
10841
|
+
try {
|
|
10842
|
+
const snaps = await t2.ref.collection("snapshots").orderBy("timestamp", "desc").limit(50).get();
|
|
10843
|
+
for (const s2 of snaps.docs) {
|
|
10844
|
+
const data = s2.data();
|
|
10845
|
+
const ts = String(data?.timestamp || "");
|
|
10846
|
+
if (ts >= start && ts < end) collected.push(data);
|
|
10847
|
+
}
|
|
10848
|
+
} catch {
|
|
10849
|
+
}
|
|
10850
|
+
}
|
|
10851
|
+
collected.sort((a, b) => String(b?.timestamp || "").localeCompare(String(a?.timestamp || "")));
|
|
10852
|
+
docs = collected.slice(0, limit);
|
|
10853
|
+
}
|
|
10854
|
+
} else {
|
|
10855
|
+
try {
|
|
10856
|
+
const ss = await db.collectionGroup("snapshots").where("uid", "==", uid).where("projectId", "==", projectId).orderBy("timestamp", "desc").limit(limit).get();
|
|
10857
|
+
docs = ss.docs.map((d) => d.data());
|
|
10858
|
+
} catch (e2) {
|
|
10859
|
+
const projRef = db.collection("users").doc(uid).collection("projects").doc(projectId);
|
|
10860
|
+
const tasksSnap = await projRef.collection("tasks").get();
|
|
10861
|
+
const perTaskTop = [];
|
|
10862
|
+
for (const t2 of tasksSnap.docs) {
|
|
10863
|
+
try {
|
|
10864
|
+
const snaps = await t2.ref.collection("snapshots").orderBy("timestamp", "desc").limit(Math.max(1, Math.ceil(limit / Math.max(1, tasksSnap.size)))).get();
|
|
10865
|
+
for (const s2 of snaps.docs) perTaskTop.push(s2.data());
|
|
10866
|
+
} catch {
|
|
10867
|
+
}
|
|
10868
|
+
}
|
|
10869
|
+
perTaskTop.sort((a, b) => String(b?.timestamp || "").localeCompare(String(a?.timestamp || "")));
|
|
10870
|
+
docs = perTaskTop.slice(0, limit);
|
|
10871
|
+
}
|
|
10872
|
+
}
|
|
10873
|
+
return res.json({ success: true, data: { snapshots: docs } });
|
|
10874
|
+
} catch (e2) {
|
|
10875
|
+
console.error("[Snapshots] GET error", e2);
|
|
10876
|
+
return res.status(500).json({ error: "internal_error" });
|
|
10877
|
+
}
|
|
10878
|
+
});
|
|
10743
10879
|
app.get("/api/v1/usage", rateLimitMiddleware, async (req, res) => {
|
|
10744
10880
|
try {
|
|
10745
10881
|
const auth = req.headers.authorization;
|