fix(db): add missing apiKeys columns + restore ApiExplorerModal export

- schema.js: add createdAt/allowedModels to apiKeys, bump SCHEMA_VERSION 3->4
- shared/components/index.js: restore ApiExplorerModal barrel export (was replaced by TagInput)
- combos: tag support, repo + api + page updates
This commit is contained in:
2026-08-27 13:58:23 +07:00
parent d0f202a75d
commit 5c6048759c
82 changed files with 52711 additions and 32 deletions

1407
bun.lock Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,7 @@ import { DndContext, closestCenter, KeyboardSensor, PointerSensor, useSensor, us
import { arrayMove, SortableContext, sortableKeyboardCoordinates, useSortable, verticalListSortingStrategy } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import { restrictToVerticalAxis, restrictToParentElement } from "@dnd-kit/modifiers";
import { Card, Button, Modal, Input, CardSkeleton, ModelSelectModal, ModelSelectSidePanel, ConfirmModal, CapacityBadges, Select, Toggle } from "@/shared/components";
import { Card, Button, Modal, Input, CardSkeleton, ModelSelectModal, ModelSelectSidePanel, ConfirmModal, CapacityBadges, Select, Toggle, TagInput } from "@/shared/components";
import { useCopyToClipboard } from "@/shared/hooks/useCopyToClipboard";
import { useModelCaps } from "@/shared/hooks/useModelCaps";
import { isOpenAICompatibleProvider, isAnthropicCompatibleProvider } from "@/shared/constants/providers";
@@ -56,6 +56,9 @@ export default function CombosPage() {
const { getCaps } = useModelCaps();
const [confirmState, setConfirmState] = useState(null);
const { copied, copy } = useCopyToClipboard();
// Tag filter is additive (OR). Empty set = show all. "Untagged" is a
// synthetic filter that matches combos whose tags array is empty.
const [activeTagFilters, setActiveTagFilters] = useState(() => new Set());
useEffect(() => {
fetchData();
@@ -190,15 +193,56 @@ export default function CombosPage() {
console.log("Error updating combo strategy:", error);
}
};
// Build the union of all tags + a synthetic "Untagged" entry when at least
// one combo has no tags. Stable order: alphabetical, with Untagged last.
const availableTags = (() => {
const set = new Set();
let hasUntagged = false;
for (const c of combos) {
if (Array.isArray(c.tags) && c.tags.length > 0) {
for (const t of c.tags) set.add(t);
} else {
hasUntagged = true;
}
}
const out = [...set].sort();
if (hasUntagged) out.push("__untagged__");
return out;
})();
if (loading) {
return (
<div className="flex flex-col gap-6">
<CardSkeleton />
<CardSkeleton />
</div>
);
}
const toggleTagFilter = (tag) => {
setActiveTagFilters((prev) => {
const next = new Set(prev);
if (next.has(tag)) next.delete(tag);
else next.add(tag);
return next;
});
};
// Apply OR-of-active-tags filter. Empty set = pass through unchanged.
const visibleCombos = activeTagFilters.size === 0
? combos
: combos.filter((c) => {
const tags = Array.isArray(c.tags) && c.tags.length > 0 ? c.tags : ["__untagged__"];
return tags.some((t) => activeTagFilters.has(t));
});
// Group by tag. A combo with [a, b] appears in both groups. Combos with no
// tags land in a synthetic "__untagged__" bucket. Order within each group
// follows the input (combos are already in `createdAt ASC` from the repo).
const groupedCombos = (() => {
const groups = new Map();
for (const c of visibleCombos) {
const tags = Array.isArray(c.tags) && c.tags.length > 0 ? c.tags : ["__untagged__"];
for (const t of tags) {
if (activeTagFilters.size > 0 && !activeTagFilters.has(t)) continue;
const arr = groups.get(t) || [];
arr.push(c);
groups.set(t, arr);
}
}
return groups;
})();
return (
<div className="flex min-w-0 flex-col gap-6 px-1 sm:px-0">
@@ -219,7 +263,39 @@ export default function CombosPage() {
</Button>
</div>
{/* Combos List */}
{/* Tag filter bar — chips toggle inclusion. OR semantics. */}
{availableTags.length > 0 && (
<div className="flex flex-wrap items-center gap-1.5">
<span className="text-[11px] uppercase tracking-wide text-text-muted mr-1">Filter</span>
{availableTags.map((t) => {
const isUntagged = t === "__untagged__";
const active = activeTagFilters.has(t);
return (
<button
key={t}
type="button"
onClick={() => toggleTagFilter(t)}
className={`inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-[11px] font-medium transition-colors ${active ? "border-primary bg-primary/10 text-primary" : "border-black/10 dark:border-white/10 text-text-muted hover:text-text-main hover:border-primary/40"}`}
title={active ? `Remove ${t} filter` : `Filter by ${t}`}
>
<span className="material-symbols-outlined text-[12px]">{isUntagged ? "label_off" : "sell"}</span>
{isUntagged ? "Untagged" : t}
</button>
);
})}
{activeTagFilters.size > 0 && (
<button
type="button"
onClick={() => setActiveTagFilters(new Set())}
className="text-[11px] text-text-muted hover:text-primary ml-1"
>
Clear
</button>
)}
</div>
)}
{/* Combos List — grouped by active tag filter, or flat if no filter */}
{combos.length === 0 ? (
<Card>
<div className="text-center py-12">
@@ -233,6 +309,37 @@ export default function CombosPage() {
</Button>
</div>
</Card>
) : activeTagFilters.size > 0 ? (
<div className="flex flex-col gap-6">
{[...groupedCombos.entries()].map(([tag, list]) => (
<section key={tag} className="flex flex-col gap-3">
<div className="flex items-center gap-2 px-1">
<span className="material-symbols-outlined text-[16px] text-primary">sell</span>
<h3 className="text-sm font-semibold">
{tag === "__untagged__" ? "Untagged" : tag}
</h3>
<span className="text-[11px] text-text-muted">({list.length})</span>
</div>
<div className="flex flex-col gap-4">
{list.map((combo) => (
<ComboCard
key={combo.id}
combo={combo}
getCaps={getCaps}
activeProviders={activeProviders}
copied={copied}
onCopy={copy}
onEdit={() => setEditingCombo(combo)}
onDelete={() => handleDelete(combo.id)}
strategy={comboStrategies[combo.name] || {}}
globalStrategy={globalComboStrategy}
onSetStrategy={(patch) => handleSetComboStrategy(combo.name, patch)}
/>
))}
</div>
</section>
))}
</div>
) : (
<div className="flex flex-col gap-4">
{combos.map((combo) => (
@@ -252,8 +359,6 @@ export default function CombosPage() {
))}
</div>
)}
{/* Capacity Adapter */}
<CapacityAdapterSection
capacityAdapter={capacityAdapter}
onChange={handleSetCapacityAdapter}
@@ -334,6 +439,17 @@ function ComboCard({ combo, getCaps, activeProviders = [], copied, onCopy, onEdi
<span className="text-[10px] text-text-muted">+{combo.models.length - 3} more</span>
)}
</div>
{/* Tags — read-only chips. Edit/manage via the form modal. */}
{Array.isArray(combo.tags) && combo.tags.length > 0 && (
<div className="mt-1.5 flex min-w-0 flex-wrap items-center gap-1">
{combo.tags.map((t) => (
<span key={t} className="inline-flex items-center gap-0.5 rounded bg-primary/10 px-1.5 py-0.5 text-[10px] font-medium text-primary">
<span className="material-symbols-outlined text-[11px]">sell</span>
{t}
</span>
))}
</div>
)}
{/* Fusion: judge picker (Auto = first model) */}
{isFusion && (
<div className="mt-2 flex min-w-0 flex-wrap items-center gap-1.5">
@@ -664,6 +780,7 @@ function ComboFormModal({ isOpen, combo, onClose, onSave, activeProviders, kindF
// Initialize state with combo values - key prop on parent handles reset on remount
const [name, setName] = useState(combo?.name || "");
const [models, setModels] = useState(combo?.models || []);
const [tags, setTags] = useState(combo?.tags || []);
const [showModelSelect, setShowModelSelect] = useState(false);
const [saving, setSaving] = useState(false);
const [nameError, setNameError] = useState("");
@@ -754,7 +871,7 @@ function ComboFormModal({ isOpen, combo, onClose, onSave, activeProviders, kindF
const handleSave = async () => {
if (!validateName(name)) return;
setSaving(true);
await onSave({ name: name.trim(), models });
await onSave({ name: name.trim(), models, tags });
setSaving(false);
};
@@ -838,6 +955,17 @@ function ComboFormModal({ isOpen, combo, onClose, onSave, activeProviders, kindF
</button>
</div>
{/* Tags — optional. Used to group combos in the dashboard list. */}
<div>
<label className="text-sm font-medium mb-1.5 block">
Tags <span className="text-text-muted font-normal">— optional, used to group combos</span>
</label>
<TagInput value={tags} onChange={setTags} placeholder="coding, cheap, vision…" />
<p className="text-[10px] text-text-muted mt-0.5">
Press Enter or comma to add. Lowercased + deduped automatically.
</p>
</div>
{/* Actions */}
<div className="flex flex-col gap-2 pt-1 sm:flex-row">
<Button onClick={onClose} variant="ghost" fullWidth size="sm">

View File

@@ -1784,7 +1784,7 @@ export default function ProviderDetailPage() {
size="sm"
variant="secondary"
icon={allSelectedDisabled ? "toggle_on" : "toggle_off"}
onClick={() => handleBulkToggleActive(!allSelectedDisabled)}
onClick={() => handleBulkToggleActive(allSelectedDisabled)}
>
{allSelectedDisabled ? "Enable" : "Disable"} Selected ({selectedConnectionIds.length})
</Button>

View File

@@ -1,3 +1,20 @@
// Normalize user-supplied tags: lowercase, trim, strip empties/dupes, cap length+count.
// Kept in sync with the create route — same rules, same shape.
function sanitizeTags(tags) {
if (!Array.isArray(tags)) return [];
const out = [];
const seen = new Set();
for (const t of tags) {
if (typeof t !== "string") continue;
const v = t.trim().toLowerCase();
if (!v || v.length > 32 || seen.has(v)) continue;
seen.add(v);
out.push(v);
if (out.length >= 12) break;
}
return out;
}
import { NextResponse } from "next/server";
import { getComboById, updateCombo, deleteCombo, getComboByName } from "@/lib/localDb";
import { resetComboRotation } from "open-sse/services/combo.js";
@@ -27,24 +44,29 @@ export async function PUT(request, { params }) {
try {
const { id } = await params;
const body = await request.json();
// Validate name format if provided
if (body.name) {
if (!VALID_NAME_REGEX.test(body.name)) {
return NextResponse.json({ error: "Name can only contain letters, numbers, -, _ and ." }, { status: 400 });
}
// Check if name already exists (exclude current combo)
const existing = await getComboByName(body.name);
if (existing && existing.id !== id) {
return NextResponse.json({ error: "Combo name already exists" }, { status: 400 });
}
}
// `tags` is optional in PUT. When present (even as []), sanitize and store.
if (body.tags !== undefined) {
body.tags = sanitizeTags(body.tags);
}
// Capture previous name to invalidate rotation state on rename
const prev = await getComboById(id);
const combo = await updateCombo(id, body);
if (!combo) {
return NextResponse.json({ error: "Combo not found" }, { status: 404 });
}

View File

@@ -1,3 +1,21 @@
// Normalize user-supplied tags: lowercase, trim, strip empties/dupes, cap length+count.
// Anything non-string is dropped. Tags are pure metadata, so this is a
// generous regex on purpose (spaces, unicode) — only length + count are bounded.
function sanitizeTags(tags) {
if (!Array.isArray(tags)) return [];
const out = [];
const seen = new Set();
for (const t of tags) {
if (typeof t !== "string") continue;
const v = t.trim().toLowerCase();
if (!v || v.length > 32 || seen.has(v)) continue;
seen.add(v);
out.push(v);
if (out.length >= 12) break;
}
return out;
}
import { NextResponse } from "next/server";
import { getCombos, createCombo, getComboByName } from "@/lib/localDb";
@@ -21,7 +39,7 @@ export async function GET() {
export async function POST(request) {
try {
const body = await request.json();
const { name, models, kind } = body;
const { name, models, kind, tags } = body;
if (!name) {
return NextResponse.json({ error: "Name is required" }, { status: 400 });
@@ -38,7 +56,8 @@ export async function POST(request) {
return NextResponse.json({ error: "Combo name already exists" }, { status: 400 });
}
const combo = await createCombo({ name, models: models || [], kind: kind || null });
const sanitizedTags = sanitizeTags(tags);
const combo = await createCombo({ name, models: models || [], kind: kind || null, tags: sanitizedTags });
return NextResponse.json(combo, { status: 201 });
} catch (error) {

View File

@@ -79,7 +79,7 @@ export async function exportDb() {
providerNodes: db.all(`SELECT * FROM providerNodes`).map((r) => ({ ...parseJson(r.data, {}), id: r.id, type: r.type, name: r.name, createdAt: r.createdAt, updatedAt: r.updatedAt })),
proxyPools: db.all(`SELECT * FROM proxyPools`).map((r) => ({ ...parseJson(r.data, {}), id: r.id, isActive: r.isActive === 1, testStatus: r.testStatus, createdAt: r.createdAt, updatedAt: r.updatedAt })),
apiKeys: db.all(`SELECT * FROM apiKeys`).map((r) => ({ id: r.id, key: r.key, name: r.name, machineId: r.machineId, isActive: r.isActive === 1, createdAt: r.createdAt })),
combos: db.all(`SELECT * FROM combos`).map((r) => ({ id: r.id, name: r.name, kind: r.kind, models: parseJson(r.models, []), createdAt: r.createdAt, updatedAt: r.updatedAt })),
combos: db.all(`SELECT * FROM combos`).map((r) => ({ id: r.id, name: r.name, kind: r.kind, models: parseJson(r.models, []), tags: parseJson(r.tags, []), createdAt: r.createdAt, updatedAt: r.updatedAt })),
modelAliases: {},
customModels: [],
mitmAlias: {},
@@ -144,8 +144,8 @@ export async function importDb(payload) {
}
for (const c of payload.combos || []) {
db.run(
`INSERT OR REPLACE INTO combos(id, name, kind, models, createdAt, updatedAt) VALUES(?, ?, ?, ?, ?, ?)`,
[c.id, c.name, c.kind || null, stringifyJson(c.models || []), c.createdAt || new Date().toISOString(), c.updatedAt || new Date().toISOString()]
`INSERT OR REPLACE INTO combos(id, name, kind, models, tags, createdAt, updatedAt) VALUES(?, ?, ?, ?, ?, ?, ?)`,
[c.id, c.name, c.kind || null, stringifyJson(c.models || []), stringifyJson(c.tags || []), c.createdAt || new Date().toISOString(), c.updatedAt || new Date().toISOString()]
);
}
for (const [a, m] of Object.entries(payload.modelAliases || {})) {

View File

@@ -149,8 +149,8 @@ function importLegacyMain(adapter, data) {
importWithAssertion(adapter, "combos", data.combos || [], (c) => {
adapter.run(
`INSERT OR REPLACE INTO combos(id, name, kind, models, createdAt, updatedAt) VALUES(?, ?, ?, ?, ?, ?)`,
[c.id, c.name, c.kind || null, stringifyJson(c.models || []), c.createdAt || new Date().toISOString(), c.updatedAt || new Date().toISOString()]
`INSERT OR REPLACE INTO combos(id, name, kind, models, tags, createdAt, updatedAt) VALUES(?, ?, ?, ?, ?, ?, ?)`,
[c.id, c.name, c.kind || null, stringifyJson(c.models || []), stringifyJson(c.tags || []), c.createdAt || new Date().toISOString(), c.updatedAt || new Date().toISOString()]
);
}, (c) => ({ id: c.id ?? null, name: c.name ?? null }));

View File

@@ -0,0 +1,12 @@
// Adds a `tags` JSON array column to combos so users can group combos in the
// dashboard. Tags are pure metadata — routing / fallback still uses combo.name.
export default {
version: 3,
name: "combo-tags",
up(db) {
const existing = db.all(`PRAGMA table_info(combos)`) || [];
if (!existing.some((c) => c.name === "tags")) {
db.exec(`ALTER TABLE combos ADD COLUMN tags TEXT NOT NULL DEFAULT '[]'`);
}
},
};

View File

@@ -1,10 +1,8 @@
// Migration registry — append new entries when schema changes.
// Each migration: { version: number, name: string, up(db): void }
// Versions MUST be unique and monotonically increasing.
import m001 from "./001-initial.js";
import m002 from "./002-request-details-apikey.js";
import m003 from "./003-combo-tags.js";
export const MIGRATIONS = [m001, m002].sort((a, b) => a.version - b.version);
export const MIGRATIONS = [m001, m002, m003].sort((a, b) => a.version - b.version);
export function latestVersion() {
return MIGRATIONS.length ? MIGRATIONS[MIGRATIONS.length - 1].version : 0;

View File

@@ -10,6 +10,7 @@ function rowToCombo(row) {
kind: row.kind,
models: parseJson(row.models, []),
enabled: row.enabled === 1 || row.enabled === true,
tags: parseJson(row.tags, []),
createdAt: row.createdAt,
updatedAt: row.updatedAt,
};
@@ -42,17 +43,19 @@ export async function createCombo(data) {
kind: data.kind || null,
models: data.models || [],
enabled: data.enabled !== false,
tags: Array.isArray(data.tags) ? data.tags : [],
createdAt: now,
updatedAt: now,
};
db.run(
`INSERT INTO combos(id, name, kind, models, enabled, createdAt, updatedAt) VALUES(?, ?, ?, ?, ?, ?, ?)`,
`INSERT INTO combos(id, name, kind, models, enabled, tags, createdAt, updatedAt) VALUES(?, ?, ?, ?, ?, ?, ?, ?)`,
[
combo.id,
combo.name,
combo.kind,
stringifyJson(combo.models),
combo.enabled !== false ? 1 : 0,
stringifyJson(combo.tags),
combo.createdAt,
combo.updatedAt,
],
@@ -71,13 +74,17 @@ export async function updateCombo(id, data) {
...data,
updatedAt: new Date().toISOString(),
};
// `tags` may arrive undefined on partial updates — fall back to whatever
// the row already had. An explicit empty array clears them.
if (!Array.isArray(merged.tags)) merged.tags = rowToCombo(row).tags;
db.run(
`UPDATE combos SET name = ?, kind = ?, models = ?, enabled = ?, updatedAt = ? WHERE id = ?`,
`UPDATE combos SET name = ?, kind = ?, models = ?, enabled = ?, tags = ?, updatedAt = ? WHERE id = ?`,
[
merged.name,
merged.kind,
stringifyJson(merged.models || []),
merged.enabled ? 1 : 0,
stringifyJson(merged.tags),
merged.updatedAt,
id,
],

View File

@@ -3,7 +3,7 @@
// pre-change safety backup in migrate.js: when the stored version is lower,
// one lightweight DB backup is taken before applying schema changes. Forgetting
// to bump only skips that backup — it does NOT break the additive auto-sync.
export const SCHEMA_VERSION = 3;
export const SCHEMA_VERSION = 4;
export const PRAGMA_SQL = `
PRAGMA journal_mode = WAL;
@@ -83,6 +83,7 @@ export const TABLES = {
machineId: "TEXT",
isActive: "INTEGER DEFAULT 1",
allowedModels: "TEXT",
createdAt: "TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))",
},
indexes: ["CREATE INDEX IF NOT EXISTS idx_ak_key ON apiKeys(key)"],
},
@@ -93,6 +94,7 @@ export const TABLES = {
kind: "TEXT",
models: "TEXT NOT NULL",
enabled: "INTEGER NOT NULL DEFAULT 1",
tags: "TEXT NOT NULL DEFAULT '[]'",
createdAt: "TEXT NOT NULL",
updatedAt: "TEXT NOT NULL",
},

View File

@@ -0,0 +1,83 @@
"use client";
import { useState } from "react";
// Pill input for free-form tags. Enter / comma commits. Backspace on empty
// input pops the last tag. Pure local state — caller owns the canonical list.
export default function TagInput({ value = [], onChange, placeholder = "Add tag…", maxTags = 12, disabled = false }) {
const [draft, setDraft] = useState("");
const commit = (raw) => {
const next = (raw || "")
.split(/[,\n]/)
.map((s) => s.trim().toLowerCase())
.filter(Boolean);
if (next.length === 0) return;
const merged = [...value];
const seen = new Set(merged);
for (const t of next) {
if (seen.has(t)) continue;
if (t.length > 32) continue;
if (merged.length >= maxTags) break;
merged.push(t);
seen.add(t);
}
if (merged.length !== value.length) onChange(merged);
setDraft("");
};
const remove = (t) => {
onChange(value.filter((x) => x !== t));
};
const onKeyDown = (e) => {
if (e.key === "Enter" || e.key === ",") {
e.preventDefault();
commit(draft);
} else if (e.key === "Backspace" && draft === "" && value.length > 0) {
onChange(value.slice(0, -1));
} else if (e.key === "Escape") {
setDraft("");
}
};
return (
<div
className={`flex flex-wrap items-center gap-1.5 min-h-[34px] w-full rounded border border-black/10 dark:border-white/10 bg-white dark:bg-black/20 px-2 py-1.5 focus-within:border-primary ${disabled ? "opacity-50 pointer-events-none" : ""}`}
onClick={(e) => {
// Clicking the pill row focuses the input so the user can keep typing.
if (e.target === e.currentTarget) {
e.currentTarget.querySelector("input")?.focus();
}
}}
>
{value.map((t) => (
<span
key={t}
className="inline-flex items-center gap-1 rounded bg-primary/10 px-1.5 py-0.5 text-[11px] font-medium text-primary"
>
<span className="material-symbols-outlined text-[12px]">sell</span>
<span>{t}</span>
<button
type="button"
onClick={() => remove(t)}
className="-mr-0.5 rounded p-0.5 text-primary/70 hover:text-primary hover:bg-primary/20"
title={`Remove ${t}`}
>
<span className="material-symbols-outlined text-[12px]">close</span>
</button>
</span>
))}
<input
type="text"
value={draft}
onChange={(e) => setDraft(e.target.value)}
onKeyDown={onKeyDown}
onBlur={() => { if (draft.trim()) commit(draft); }}
placeholder={value.length === 0 ? placeholder : ""}
maxLength={32}
className="flex-1 min-w-[80px] bg-transparent text-xs outline-none placeholder:text-text-muted"
/>
</div>
);
}

View File

@@ -37,6 +37,7 @@ export { default as SegmentedControl } from "./SegmentedControl";
export { default as Tooltip } from "./Tooltip";
export { default as ProviderInfoCard } from "./ProviderInfoCard";
export { default as CapacityBadges } from "./CapacityBadges";
export { default as TagInput } from "./TagInput";
export { default as ApiExplorerModal } from "./ApiExplorerModal";
// Layouts

View File

@@ -0,0 +1,15 @@
{
"pages": {
"/_app": []
},
"devFiles": [],
"polyfillFiles": [],
"lowPriorityFiles": [
"static/development/_buildManifest.js",
"static/development/_ssgManifest.js",
"static/development/_clientMiddlewareManifest.js"
],
"rootMainFiles": [],
"rootMainFilesTree": {},
"pagesChunkGroupBootstrapParams": {}
}

1
tmp/9r-smoke-dist/dev/cache/.rscinfo vendored Normal file
View File

@@ -0,0 +1 @@
{"encryption.key":"+PzC19pP70GybS3k4K8t6RsdWKuh0Yqhwm2/p8Gxynw=","encryption.expire_at":1788861894573}

View File

@@ -0,0 +1,7 @@
{
"telemetry": {
"notifiedAt": "1787652294386",
"anonymousId": "603e54ced475f5c44789656952c172a5cb54960610abe0044c7cf55588e68b9b",
"salt": "2780e49a8a0d18bcf1e75adcca6c2702"
}
}

View File

@@ -0,0 +1 @@
{"max_sequence_number":0,"commit_time":"2026-08-25T10:04:54.578821Z"}

View File

@@ -0,0 +1,15 @@
{
"pages": {
"/_app": []
},
"devFiles": [],
"polyfillFiles": [],
"lowPriorityFiles": [
"static/development/_buildManifest.js",
"static/development/_ssgManifest.js",
"static/development/_clientMiddlewareManifest.js"
],
"rootMainFiles": [],
"rootMainFilesTree": {},
"pagesChunkGroupBootstrapParams": {}
}

View File

@@ -0,0 +1 @@
{"pid":19305,"port":20199,"hostname":"localhost","appUrl":"http://localhost:20199","startedAt":1787652294484}

View File

@@ -0,0 +1,8 @@
{"timestamp":"00:00:00.929","source":"Server","level":"LOG","message":"- Experiments (use with caution):"}
{"timestamp":"00:00:00.929","source":"Server","level":"LOG","message":" · optimizePackageImports"}
{"timestamp":"00:00:00.929","source":"Server","level":"LOG","message":" · proxyClientMaxBodySize: \"128mb\""}
{"timestamp":"00:00:00.929","source":"Server","level":"LOG","message":""}
{"timestamp":"00:00:00.930","source":"Server","level":"LOG","message":"✓ Generated CLAUDE.md for AI agents. Set `agentRules: false` in next.config to disable."}
{"timestamp":"00:00:00.953","source":"Server","level":"ERROR","message":"Error: write EPIPE"}
{"timestamp":"00:00:00.954","source":"Server","level":"ERROR","message":"⨯ \"uncaughtException:\" Error: write EPIPE"}
{"timestamp":"00:00:00.954","source":"Server","level":"ERROR","message":"⨯ \"uncaughtException: \" Error: write EPIPE"}

View File

@@ -0,0 +1 @@
../../../../node_modules/better-sqlite3

View File

@@ -0,0 +1 @@
../../../../node_modules/sql.js

View File

@@ -0,0 +1,3 @@
{
"type": "commonjs"
}

View File

@@ -0,0 +1,11 @@
{
"version": 4,
"routes": {},
"dynamicRoutes": {},
"notFoundRoutes": [],
"preview": {
"previewModeId": "ccf2fc358f0d676072b2c8a97c13bb23",
"previewModeSigningKey": "2d92942cd8170b1f834dbada55904274205957eacff5167dcde5bb52c19403ec",
"previewModeEncryptionKey": "a139b9d52f12448b1c4fe91cb68fc842bda57b8c35acb25a5a052a0e16e145fa"
}
}

View File

@@ -0,0 +1 @@
{"version":3,"caseSensitive":false,"basePath":"","rewrites":{"beforeFiles":[],"afterFiles":[{"source":"/v1/v1/:path*","destination":"/api/v1/:path*","regex":"^\\/v1\\/v1(?:\\/((?:[^\\/]+?)(?:\\/(?:[^\\/]+?))*))?(?:\\/)?$","check":true},{"source":"/v1/v1","destination":"/api/v1","regex":"^\\/v1\\/v1(?:\\/)?$","check":true},{"source":"/codex/:path*","destination":"/api/v1/responses","regex":"^\\/codex(?:\\/((?:[^\\/]+?)(?:\\/(?:[^\\/]+?))*))?(?:\\/)?$","check":true},{"source":"/responses","destination":"/api/v1/responses","regex":"^\\/responses(?:\\/)?$","check":true},{"source":"/v1beta/:path*","destination":"/api/v1beta/:path*","regex":"^\\/v1beta(?:\\/((?:[^\\/]+?)(?:\\/(?:[^\\/]+?))*))?(?:\\/)?$","check":true},{"source":"/v1beta","destination":"/api/v1beta","regex":"^\\/v1beta(?:\\/)?$","check":true},{"source":"/v1/:path*","destination":"/api/v1/:path*","regex":"^\\/v1(?:\\/((?:[^\\/]+?)(?:\\/(?:[^\\/]+?))*))?(?:\\/)?$","check":true},{"source":"/v1","destination":"/api/v1","regex":"^\\/v1(?:\\/)?$","check":true}],"fallback":[]},"redirects":[{"source":"/:path+/","destination":"/:path+","permanent":true,"internal":true,"priority":true,"regex":"^(?:\\/((?:[^\\/]+?)(?:\\/(?:[^\\/]+?))*))\\/$"}],"headers":[],"onMatchHeaders":[]}

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1,8 @@
module.exports = [
"[externals]/bun:sqlite [external] (bun:sqlite, cjs)", ((__turbopack_context__, module, exports) => {
var mod = __turbopack_context__.x("bun:sqlite", () => require("bun:sqlite"));
module.exports = mod;
}),
];

View File

@@ -0,0 +1,5 @@
{
"version": 3,
"sources": [],
"sections": []
}

View File

@@ -0,0 +1,12 @@
module.exports = [
"[externals]/bun:sqlite [external] (bun:sqlite, cjs, async loader)", ((__turbopack_context__) => {
__turbopack_context__.v((parentImport) => {
return Promise.all([
"server/chunks/[externals]_bun_sqlite_1aygq0y._.js"
].map((chunk) => __turbopack_context__.l(chunk))).then(() => {
return parentImport("[externals]/bun:sqlite [external] (bun:sqlite, cjs)");
});
});
}),
];

View File

@@ -0,0 +1,5 @@
{
"version": 3,
"sources": [],
"sections": []
}

View File

@@ -0,0 +1,275 @@
module.exports = [
"[externals]/better-sqlite3 [external] (better-sqlite3, cjs, [project]/node_modules/better-sqlite3)", ((__turbopack_context__, module, exports) => {
var mod = __turbopack_context__.x("better-sqlite3-90e2652d1716b047", () => require("better-sqlite3-90e2652d1716b047"));
module.exports = mod;
}),
"[project]/src/lib/db/adapters/betterSqliteAdapter.js [middleware] (ecmascript)", ((__turbopack_context__) => {
"use strict";
__turbopack_context__.s([
"createBetterSqliteAdapter",
()=>createBetterSqliteAdapter
]);
var __TURBOPACK__imported__module__$5b$externals$5d2f$better$2d$sqlite3__$5b$external$5d$__$28$better$2d$sqlite3$2c$__cjs$2c$__$5b$project$5d2f$node_modules$2f$better$2d$sqlite3$29$__ = __turbopack_context__.i("[externals]/better-sqlite3 [external] (better-sqlite3, cjs, [project]/node_modules/better-sqlite3)");
var __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$schema$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/src/lib/db/schema.js [middleware] (ecmascript)");
;
;
// Periodic checkpoint to keep WAL file small (avoid huge -wal/-shm growth)
const CHECKPOINT_INTERVAL_MS = 60 * 1000;
function createBetterSqliteAdapter(filePath) {
const db = new __TURBOPACK__imported__module__$5b$externals$5d2f$better$2d$sqlite3__$5b$external$5d$__$28$better$2d$sqlite3$2c$__cjs$2c$__$5b$project$5d2f$node_modules$2f$better$2d$sqlite3$29$__["default"](filePath);
db.exec(__TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$schema$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["PRAGMA_SQL"]);
// Schema is created/synced by migrate.js after adapter init
const stmtCache = new Map();
function prepare(sql) {
let stmt = stmtCache.get(sql);
if (!stmt) {
stmt = db.prepare(sql);
stmtCache.set(sql, stmt);
}
return stmt;
}
// Truncate WAL periodically so file stays small for backup/copy
const checkpointTimer = setInterval(()=>{
try {
db.pragma("wal_checkpoint(TRUNCATE)");
} catch {}
}, CHECKPOINT_INTERVAL_MS);
if (typeof checkpointTimer.unref === "function") checkpointTimer.unref();
function gracefulClose() {
try {
db.pragma("wal_checkpoint(TRUNCATE)");
} catch {}
try {
stmtCache.clear();
} catch {}
try {
db.close();
} catch {}
}
// Ensure WAL is flushed and -wal/-shm files removed on shutdown
const onShutdown = ()=>gracefulClose();
process.once("beforeExit", onShutdown);
process.once("SIGINT", ()=>{
onShutdown();
process.exit(0);
});
process.once("SIGTERM", ()=>{
onShutdown();
process.exit(0);
});
return {
driver: "better-sqlite3",
run (sql, params = []) {
return prepare(sql).run(...params);
},
get (sql, params = []) {
return prepare(sql).get(...params);
},
all (sql, params = []) {
return prepare(sql).all(...params);
},
exec (sql) {
return db.exec(sql);
},
transaction (fn) {
return db.transaction(fn)();
},
checkpoint () {
try {
db.pragma("wal_checkpoint(TRUNCATE)");
} catch {}
},
close () {
clearInterval(checkpointTimer);
gracefulClose();
},
raw: db
};
}
}),
"[project]/src/lib/db/schema.js [middleware] (ecmascript)", ((__turbopack_context__) => {
"use strict";
// ⚠️ AGENT/DEV: Bump this by +1 EVERY TIME you change the schema below
// (add/remove/alter a table, column, or index in TABLES). It drives the
// pre-change safety backup in migrate.js: when the stored version is lower,
// one lightweight DB backup is taken before applying schema changes. Forgetting
// to bump only skips that backup — it does NOT break the additive auto-sync.
__turbopack_context__.s([
"PRAGMA_SQL",
()=>PRAGMA_SQL,
"SCHEMA_VERSION",
()=>SCHEMA_VERSION,
"TABLES",
()=>TABLES,
"buildCreateTableSql",
()=>buildCreateTableSql
]);
const SCHEMA_VERSION = 3;
const PRAGMA_SQL = `
PRAGMA journal_mode = WAL;
PRAGMA synchronous = NORMAL;
PRAGMA temp_store = MEMORY;
PRAGMA mmap_size = 30000000;
PRAGMA cache_size = -64000;
PRAGMA foreign_keys = ON;
PRAGMA busy_timeout = 5000;
`;
const TABLES = {
_meta: {
columns: {
key: "TEXT PRIMARY KEY",
value: "TEXT NOT NULL"
}
},
settings: {
columns: {
id: "INTEGER PRIMARY KEY CHECK (id = 1)",
data: "TEXT NOT NULL"
}
},
providerConnections: {
columns: {
id: "TEXT PRIMARY KEY",
provider: "TEXT NOT NULL",
authType: "TEXT NOT NULL",
name: "TEXT",
email: "TEXT",
priority: "INTEGER",
isActive: "INTEGER DEFAULT 1",
data: "TEXT NOT NULL",
createdAt: "TEXT NOT NULL",
updatedAt: "TEXT NOT NULL"
},
indexes: [
"CREATE INDEX IF NOT EXISTS idx_pc_provider ON providerConnections(provider)",
"CREATE INDEX IF NOT EXISTS idx_pc_provider_active ON providerConnections(provider, isActive)",
"CREATE INDEX IF NOT EXISTS idx_pc_priority ON providerConnections(provider, priority)"
]
},
providerNodes: {
columns: {
id: "TEXT PRIMARY KEY",
type: "TEXT",
name: "TEXT",
data: "TEXT NOT NULL",
createdAt: "TEXT NOT NULL",
updatedAt: "TEXT NOT NULL"
},
indexes: [
"CREATE INDEX IF NOT EXISTS idx_pn_type ON providerNodes(type)"
]
},
proxyPools: {
columns: {
id: "TEXT PRIMARY KEY",
isActive: "INTEGER DEFAULT 1",
testStatus: "TEXT",
data: "TEXT NOT NULL",
createdAt: "TEXT NOT NULL",
updatedAt: "TEXT NOT NULL"
},
indexes: [
"CREATE INDEX IF NOT EXISTS idx_pp_active ON proxyPools(isActive)",
"CREATE INDEX IF NOT EXISTS idx_pp_status ON proxyPools(testStatus)"
]
},
apiKeys: {
columns: {
id: "TEXT PRIMARY KEY",
key: "TEXT UNIQUE NOT NULL",
name: "TEXT",
machineId: "TEXT",
isActive: "INTEGER DEFAULT 1",
allowedModels: "TEXT"
},
indexes: [
"CREATE INDEX IF NOT EXISTS idx_ak_key ON apiKeys(key)"
]
},
combos: {
columns: {
id: "TEXT PRIMARY KEY",
name: "TEXT UNIQUE NOT NULL",
kind: "TEXT",
models: "TEXT NOT NULL",
enabled: "INTEGER NOT NULL DEFAULT 1",
createdAt: "TEXT NOT NULL",
updatedAt: "TEXT NOT NULL"
},
indexes: [
"CREATE INDEX IF NOT EXISTS idx_combo_name ON combos(name)"
]
},
kv: {
columns: {
scope: "TEXT NOT NULL",
key: "TEXT NOT NULL",
value: "TEXT NOT NULL"
},
primaryKey: "PRIMARY KEY (scope, key)",
indexes: [
"CREATE INDEX IF NOT EXISTS idx_kv_scope ON kv(scope)"
]
},
usageHistory: {
columns: {
id: "INTEGER PRIMARY KEY AUTOINCREMENT",
timestamp: "TEXT NOT NULL",
provider: "TEXT",
model: "TEXT",
connectionId: "TEXT",
apiKey: "TEXT",
endpoint: "TEXT",
promptTokens: "INTEGER DEFAULT 0",
completionTokens: "INTEGER DEFAULT 0",
cost: "REAL DEFAULT 0",
status: "TEXT",
tokens: "TEXT",
meta: "TEXT"
},
indexes: [
"CREATE INDEX IF NOT EXISTS idx_uh_ts ON usageHistory(timestamp DESC)",
"CREATE INDEX IF NOT EXISTS idx_uh_provider ON usageHistory(provider)",
"CREATE INDEX IF NOT EXISTS idx_uh_model ON usageHistory(model)",
"CREATE INDEX IF NOT EXISTS idx_uh_conn ON usageHistory(connectionId)"
]
},
usageDaily: {
columns: {
dateKey: "TEXT PRIMARY KEY",
data: "TEXT NOT NULL"
}
},
requestDetails: {
columns: {
id: "TEXT PRIMARY KEY",
timestamp: "TEXT NOT NULL",
provider: "TEXT",
model: "TEXT",
connectionId: "TEXT",
status: "TEXT",
apiKey: "TEXT",
data: "TEXT NOT NULL"
},
indexes: [
"CREATE INDEX IF NOT EXISTS idx_rd_ts ON requestDetails(timestamp DESC)",
"CREATE INDEX IF NOT EXISTS idx_rd_provider ON requestDetails(provider)",
"CREATE INDEX IF NOT EXISTS idx_rd_model ON requestDetails(model)",
"CREATE INDEX IF NOT EXISTS idx_rd_conn ON requestDetails(connectionId)",
"CREATE INDEX IF NOT EXISTS idx_rd_apikey ON requestDetails(apiKey)"
]
}
};
function buildCreateTableSql(name, def) {
const cols = Object.entries(def.columns).map(([k, v])=>`${k} ${v}`);
if (def.primaryKey) cols.push(def.primaryKey);
return `CREATE TABLE IF NOT EXISTS ${name} (${cols.join(", ")})`;
}
}),
];
//# sourceMappingURL=%5Broot-of-the-server%5D__16a8kp1._.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,324 @@
module.exports = [
"[externals]/sql.js [external] (sql.js, cjs, [project]/node_modules/sql.js)", ((__turbopack_context__, module, exports) => {
var mod = __turbopack_context__.x("sql.js-59d66b30daa0a8d2", () => require("sql.js-59d66b30daa0a8d2"));
module.exports = mod;
}),
"[project]/src/lib/db/adapters/sqljsAdapter.js [middleware] (ecmascript)", ((__turbopack_context__) => {
"use strict";
__turbopack_context__.s([
"createSqlJsAdapter",
()=>createSqlJsAdapter
]);
var __TURBOPACK__imported__module__$5b$externals$5d2f$node$3a$fs__$5b$external$5d$__$28$node$3a$fs$2c$__cjs$29$__ = __turbopack_context__.i("[externals]/node:fs [external] (node:fs, cjs)");
var __TURBOPACK__imported__module__$5b$externals$5d2f$sql$2e$js__$5b$external$5d$__$28$sql$2e$js$2c$__cjs$2c$__$5b$project$5d2f$node_modules$2f$sql$2e$js$29$__ = __turbopack_context__.i("[externals]/sql.js [external] (sql.js, cjs, [project]/node_modules/sql.js)");
var __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$schema$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/src/lib/db/schema.js [middleware] (ecmascript)");
;
;
;
let SQL = null;
async function loadSql() {
if (SQL) return SQL;
SQL = await (0, __TURBOPACK__imported__module__$5b$externals$5d2f$sql$2e$js__$5b$external$5d$__$28$sql$2e$js$2c$__cjs$2c$__$5b$project$5d2f$node_modules$2f$sql$2e$js$29$__["default"])();
return SQL;
}
async function createSqlJsAdapter(filePath) {
const SQLLib = await loadSql();
const buf = __TURBOPACK__imported__module__$5b$externals$5d2f$node$3a$fs__$5b$external$5d$__$28$node$3a$fs$2c$__cjs$29$__["default"].existsSync(filePath) ? __TURBOPACK__imported__module__$5b$externals$5d2f$node$3a$fs__$5b$external$5d$__$28$node$3a$fs$2c$__cjs$29$__["default"].readFileSync(filePath) : null;
const db = new SQLLib.Database(buf);
db.exec(__TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$schema$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["PRAGMA_SQL"]);
// Schema is created/synced by migrate.js after adapter init
let dirty = false;
let saveTimer = null;
const SAVE_DEBOUNCE_MS = 100;
function persist() {
const data = db.export();
__TURBOPACK__imported__module__$5b$externals$5d2f$node$3a$fs__$5b$external$5d$__$28$node$3a$fs$2c$__cjs$29$__["default"].writeFileSync(filePath, Buffer.from(data));
dirty = false;
}
function scheduleSave() {
dirty = true;
if (saveTimer) clearTimeout(saveTimer);
saveTimer = setTimeout(()=>{
saveTimer = null;
if (dirty) {
try {
persist();
} catch (e) {
console.error("[sqljs] save failed:", e);
}
}
}, SAVE_DEBOUNCE_MS);
}
function paramsObj(params) {
if (!params || Array.isArray(params) && params.length === 0) return undefined;
return params;
}
function run(sql, params = []) {
const stmt = db.prepare(sql);
try {
stmt.bind(paramsObj(params));
stmt.step();
const changes = db.getRowsModified();
const lastInsertRowid = db.exec("SELECT last_insert_rowid() as id")[0]?.values?.[0]?.[0] ?? null;
scheduleSave();
return {
changes,
lastInsertRowid
};
} finally{
stmt.free();
}
}
function get(sql, params = []) {
const stmt = db.prepare(sql);
try {
stmt.bind(paramsObj(params));
if (stmt.step()) return stmt.getAsObject();
return undefined;
} finally{
stmt.free();
}
}
function all(sql, params = []) {
const stmt = db.prepare(sql);
try {
stmt.bind(paramsObj(params));
const rows = [];
while(stmt.step())rows.push(stmt.getAsObject());
return rows;
} finally{
stmt.free();
}
}
function exec(sql) {
db.exec(sql);
scheduleSave();
}
function transaction(fn) {
const sp = `sp_${Math.random().toString(36).slice(2)}`;
db.exec(`SAVEPOINT ${sp}`);
try {
const result = fn();
db.exec(`RELEASE ${sp}`);
scheduleSave();
return result;
} catch (e) {
try {
db.exec(`ROLLBACK TO ${sp}`);
db.exec(`RELEASE ${sp}`);
} catch {}
throw e;
}
}
function close() {
if (saveTimer) clearTimeout(saveTimer);
if (dirty) persist();
db.close();
}
// Flush on shutdown
const flush = ()=>{
if (dirty) try {
persist();
} catch {}
};
process.on("beforeExit", flush);
process.on("SIGINT", flush);
process.on("SIGTERM", flush);
return {
driver: "sql.js",
run,
get,
all,
exec,
transaction,
close,
raw: db
};
}
}),
"[project]/src/lib/db/schema.js [middleware] (ecmascript)", ((__turbopack_context__) => {
"use strict";
// ⚠️ AGENT/DEV: Bump this by +1 EVERY TIME you change the schema below
// (add/remove/alter a table, column, or index in TABLES). It drives the
// pre-change safety backup in migrate.js: when the stored version is lower,
// one lightweight DB backup is taken before applying schema changes. Forgetting
// to bump only skips that backup — it does NOT break the additive auto-sync.
__turbopack_context__.s([
"PRAGMA_SQL",
()=>PRAGMA_SQL,
"SCHEMA_VERSION",
()=>SCHEMA_VERSION,
"TABLES",
()=>TABLES,
"buildCreateTableSql",
()=>buildCreateTableSql
]);
const SCHEMA_VERSION = 3;
const PRAGMA_SQL = `
PRAGMA journal_mode = WAL;
PRAGMA synchronous = NORMAL;
PRAGMA temp_store = MEMORY;
PRAGMA mmap_size = 30000000;
PRAGMA cache_size = -64000;
PRAGMA foreign_keys = ON;
PRAGMA busy_timeout = 5000;
`;
const TABLES = {
_meta: {
columns: {
key: "TEXT PRIMARY KEY",
value: "TEXT NOT NULL"
}
},
settings: {
columns: {
id: "INTEGER PRIMARY KEY CHECK (id = 1)",
data: "TEXT NOT NULL"
}
},
providerConnections: {
columns: {
id: "TEXT PRIMARY KEY",
provider: "TEXT NOT NULL",
authType: "TEXT NOT NULL",
name: "TEXT",
email: "TEXT",
priority: "INTEGER",
isActive: "INTEGER DEFAULT 1",
data: "TEXT NOT NULL",
createdAt: "TEXT NOT NULL",
updatedAt: "TEXT NOT NULL"
},
indexes: [
"CREATE INDEX IF NOT EXISTS idx_pc_provider ON providerConnections(provider)",
"CREATE INDEX IF NOT EXISTS idx_pc_provider_active ON providerConnections(provider, isActive)",
"CREATE INDEX IF NOT EXISTS idx_pc_priority ON providerConnections(provider, priority)"
]
},
providerNodes: {
columns: {
id: "TEXT PRIMARY KEY",
type: "TEXT",
name: "TEXT",
data: "TEXT NOT NULL",
createdAt: "TEXT NOT NULL",
updatedAt: "TEXT NOT NULL"
},
indexes: [
"CREATE INDEX IF NOT EXISTS idx_pn_type ON providerNodes(type)"
]
},
proxyPools: {
columns: {
id: "TEXT PRIMARY KEY",
isActive: "INTEGER DEFAULT 1",
testStatus: "TEXT",
data: "TEXT NOT NULL",
createdAt: "TEXT NOT NULL",
updatedAt: "TEXT NOT NULL"
},
indexes: [
"CREATE INDEX IF NOT EXISTS idx_pp_active ON proxyPools(isActive)",
"CREATE INDEX IF NOT EXISTS idx_pp_status ON proxyPools(testStatus)"
]
},
apiKeys: {
columns: {
id: "TEXT PRIMARY KEY",
key: "TEXT UNIQUE NOT NULL",
name: "TEXT",
machineId: "TEXT",
isActive: "INTEGER DEFAULT 1",
allowedModels: "TEXT"
},
indexes: [
"CREATE INDEX IF NOT EXISTS idx_ak_key ON apiKeys(key)"
]
},
combos: {
columns: {
id: "TEXT PRIMARY KEY",
name: "TEXT UNIQUE NOT NULL",
kind: "TEXT",
models: "TEXT NOT NULL",
enabled: "INTEGER NOT NULL DEFAULT 1",
createdAt: "TEXT NOT NULL",
updatedAt: "TEXT NOT NULL"
},
indexes: [
"CREATE INDEX IF NOT EXISTS idx_combo_name ON combos(name)"
]
},
kv: {
columns: {
scope: "TEXT NOT NULL",
key: "TEXT NOT NULL",
value: "TEXT NOT NULL"
},
primaryKey: "PRIMARY KEY (scope, key)",
indexes: [
"CREATE INDEX IF NOT EXISTS idx_kv_scope ON kv(scope)"
]
},
usageHistory: {
columns: {
id: "INTEGER PRIMARY KEY AUTOINCREMENT",
timestamp: "TEXT NOT NULL",
provider: "TEXT",
model: "TEXT",
connectionId: "TEXT",
apiKey: "TEXT",
endpoint: "TEXT",
promptTokens: "INTEGER DEFAULT 0",
completionTokens: "INTEGER DEFAULT 0",
cost: "REAL DEFAULT 0",
status: "TEXT",
tokens: "TEXT",
meta: "TEXT"
},
indexes: [
"CREATE INDEX IF NOT EXISTS idx_uh_ts ON usageHistory(timestamp DESC)",
"CREATE INDEX IF NOT EXISTS idx_uh_provider ON usageHistory(provider)",
"CREATE INDEX IF NOT EXISTS idx_uh_model ON usageHistory(model)",
"CREATE INDEX IF NOT EXISTS idx_uh_conn ON usageHistory(connectionId)"
]
},
usageDaily: {
columns: {
dateKey: "TEXT PRIMARY KEY",
data: "TEXT NOT NULL"
}
},
requestDetails: {
columns: {
id: "TEXT PRIMARY KEY",
timestamp: "TEXT NOT NULL",
provider: "TEXT",
model: "TEXT",
connectionId: "TEXT",
status: "TEXT",
apiKey: "TEXT",
data: "TEXT NOT NULL"
},
indexes: [
"CREATE INDEX IF NOT EXISTS idx_rd_ts ON requestDetails(timestamp DESC)",
"CREATE INDEX IF NOT EXISTS idx_rd_provider ON requestDetails(provider)",
"CREATE INDEX IF NOT EXISTS idx_rd_model ON requestDetails(model)",
"CREATE INDEX IF NOT EXISTS idx_rd_conn ON requestDetails(connectionId)",
"CREATE INDEX IF NOT EXISTS idx_rd_apikey ON requestDetails(apiKey)"
]
}
};
function buildCreateTableSql(name, def) {
const cols = Object.entries(def.columns).map(([k, v])=>`${k} ${v}`);
if (def.primaryKey) cols.push(def.primaryKey);
return `CREATE TABLE IF NOT EXISTS ${name} (${cols.join(", ")})`;
}
}),
];
//# sourceMappingURL=%5Broot-of-the-server%5D__1_pghx_._.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,113 @@
module.exports = [
"[project]/open-sse/providers/pricing.js [middleware] (ecmascript, async loader)", ((__turbopack_context__) => {
__turbopack_context__.v((parentImport) => {
return Promise.all([
"server/chunks/open-sse_providers_pricing_078i37x.js"
].map((chunk) => __turbopack_context__.l(chunk))).then(() => {
return parentImport("[project]/open-sse/providers/pricing.js [middleware] (ecmascript)");
});
});
}),
"[project]/src/lib/db/adapters/betterSqliteAdapter.js [middleware] (ecmascript, async loader)", ((__turbopack_context__) => {
__turbopack_context__.v((parentImport) => {
return Promise.all([
"server/chunks/[root-of-the-server]__16a8kp1._.js"
].map((chunk) => __turbopack_context__.l(chunk))).then(() => {
return parentImport("[project]/src/lib/db/adapters/betterSqliteAdapter.js [middleware] (ecmascript)");
});
});
}),
"[project]/src/lib/db/adapters/bunSqliteAdapter.js [middleware] (ecmascript, async loader)", ((__turbopack_context__) => {
__turbopack_context__.v((parentImport) => {
return Promise.all([
"server/chunks/[externals]_bun_sqlite_1xsgfj6._.js",
"server/chunks/src_lib_db_04-1tv7._.js"
].map((chunk) => __turbopack_context__.l(chunk))).then(() => {
return parentImport("[project]/src/lib/db/adapters/bunSqliteAdapter.js [middleware] (ecmascript)");
});
});
}),
"[project]/src/lib/db/adapters/nodeSqliteAdapter.js [middleware] (ecmascript, async loader)", ((__turbopack_context__) => {
__turbopack_context__.v((parentImport) => {
return Promise.all([
"server/chunks/src_lib_db_1rn2qxk._.js"
].map((chunk) => __turbopack_context__.l(chunk))).then(() => {
return parentImport("[project]/src/lib/db/adapters/nodeSqliteAdapter.js [middleware] (ecmascript)");
});
});
}),
"[project]/src/lib/db/adapters/sqljsAdapter.js [middleware] (ecmascript, async loader)", ((__turbopack_context__) => {
__turbopack_context__.v((parentImport) => {
return Promise.all([
"server/chunks/[root-of-the-server]__1_pghx_._.js"
].map((chunk) => __turbopack_context__.l(chunk))).then(() => {
return parentImport("[project]/src/lib/db/adapters/sqljsAdapter.js [middleware] (ecmascript)");
});
});
}),
"[project]/src/lib/db/migrate.js [middleware] (ecmascript, async loader)", ((__turbopack_context__) => {
__turbopack_context__.v((parentImport) => {
return Promise.all([
"server/chunks/src_lib_db_1jtxnmd._.js"
].map((chunk) => __turbopack_context__.l(chunk))).then(() => {
return parentImport("[project]/src/lib/db/migrate.js [middleware] (ecmascript)");
});
});
}),
"[project]/src/lib/db/repos/apiKeysRepo.js [middleware] (ecmascript, async loader)", ((__turbopack_context__) => {
__turbopack_context__.v((parentImport) => {
return Promise.resolve().then(() => {
return parentImport("[project]/src/lib/db/repos/apiKeysRepo.js [middleware] (ecmascript)");
});
});
}),
"[project]/src/lib/db/repos/connectionsRepo.js [middleware] (ecmascript, async loader)", ((__turbopack_context__) => {
__turbopack_context__.v((parentImport) => {
return Promise.resolve().then(() => {
return parentImport("[project]/src/lib/db/repos/connectionsRepo.js [middleware] (ecmascript)");
});
});
}),
"[project]/src/lib/db/repos/nodesRepo.js [middleware] (ecmascript, async loader)", ((__turbopack_context__) => {
__turbopack_context__.v((parentImport) => {
return Promise.resolve().then(() => {
return parentImport("[project]/src/lib/db/repos/nodesRepo.js [middleware] (ecmascript)");
});
});
}),
"[project]/src/lib/db/repos/pricingRepo.js [middleware] (ecmascript, async loader)", ((__turbopack_context__) => {
__turbopack_context__.v((parentImport) => {
return Promise.resolve().then(() => {
return parentImport("[project]/src/lib/db/repos/pricingRepo.js [middleware] (ecmascript)");
});
});
}),
"[project]/src/lib/db/repos/settingsRepo.js [middleware] (ecmascript, async loader)", ((__turbopack_context__) => {
__turbopack_context__.v((parentImport) => {
return Promise.resolve().then(() => {
return parentImport("[project]/src/lib/db/repos/settingsRepo.js [middleware] (ecmascript)");
});
});
}),
"[project]/src/shared/utils/apiKey.js [middleware] (ecmascript, async loader)", ((__turbopack_context__) => {
__turbopack_context__.v((parentImport) => {
return Promise.all([
"server/chunks/src_shared_utils_apiKey_0dq53wj.js"
].map((chunk) => __turbopack_context__.l(chunk))).then(() => {
return parentImport("[project]/src/shared/utils/apiKey.js [middleware] (ecmascript)");
});
});
}),
];

View File

@@ -0,0 +1,5 @@
{
"version": 3,
"sources": [],
"sections": []
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,18 @@
module.exports = [
"[project]/src/instrumentation.js [instrumentation] (ecmascript)", ((__turbopack_context__) => {
"use strict";
__turbopack_context__.s([
"register",
()=>register
]);
async function register() {
if ("TURBOPACK compile-time truthy", 1) {
const { initConsoleLogCapture } = await __turbopack_context__.A("[project]/src/lib/consoleLogBuffer.js [instrumentation] (ecmascript, async loader)");
initConsoleLogCapture();
}
}
}),
];
//# sourceMappingURL=src_instrumentation_0_51q1v.js.map

View File

@@ -0,0 +1,6 @@
{
"version": 3,
"sources": [],
"sections": [
{"offset": {"line": 4, "column": 0}, "map": {"version":3,"sources":["file:///Users/luulam/Developer/tool/9router/src/instrumentation.js"],"sourcesContent":["export async function register() {\n if (process.env.NEXT_RUNTIME === \"nodejs\") {\n const { initConsoleLogCapture } = await import(\"@/lib/consoleLogBuffer\");\n initConsoleLogCapture();\n }\n}\n"],"names":["register","initConsoleLogCapture"],"mappings":";;;;AAAO,eAAeA;IACpB,wCAA2C;QACzC,MAAM,EAAEC,qBAAqB,EAAE,GAAG;QAClCA;IACF;AACF"}}]
}

View File

@@ -0,0 +1,12 @@
module.exports = [
"[project]/src/lib/consoleLogBuffer.js [instrumentation] (ecmascript, async loader)", ((__turbopack_context__) => {
__turbopack_context__.v((parentImport) => {
return Promise.all([
"server/chunks/[root-of-the-server]__1mtjwcs._.js"
].map((chunk) => __turbopack_context__.l(chunk))).then(() => {
return parentImport("[project]/src/lib/consoleLogBuffer.js [instrumentation] (ecmascript)");
});
});
}),
];

View File

@@ -0,0 +1,5 @@
{
"version": 3,
"sources": [],
"sections": []
}

View File

@@ -0,0 +1,275 @@
module.exports = [
"[project]/src/lib/db/adapters/bunSqliteAdapter.js [middleware] (ecmascript)", ((__turbopack_context__) => {
"use strict";
__turbopack_context__.s([
"createBunSqliteAdapter",
()=>createBunSqliteAdapter
]);
// Bun runtime adapter — uses built-in bun:sqlite (native, fastest under Bun).
// Loaded only when process.versions.bun is present.
var __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$schema$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/src/lib/db/schema.js [middleware] (ecmascript)");
;
const CHECKPOINT_INTERVAL_MS = 60 * 1000;
async function createBunSqliteAdapter(filePath) {
// Dynamic import — only resolves under Bun runtime
const { Database } = await __turbopack_context__.A("[externals]/bun:sqlite [external] (bun:sqlite, cjs, async loader)");
const db = new Database(filePath, {
create: true
});
db.exec(__TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$schema$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["PRAGMA_SQL"]);
const stmtCache = new Map();
function prepare(sql) {
let stmt = stmtCache.get(sql);
if (!stmt) {
stmt = db.prepare(sql);
stmtCache.set(sql, stmt);
}
return stmt;
}
const checkpointTimer = setInterval(()=>{
try {
db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
} catch {}
}, CHECKPOINT_INTERVAL_MS);
if (typeof checkpointTimer.unref === "function") checkpointTimer.unref();
function gracefulClose() {
try {
db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
} catch {}
try {
stmtCache.clear();
} catch {}
try {
db.close();
} catch {}
}
const onShutdown = ()=>gracefulClose();
process.once("beforeExit", onShutdown);
process.once("SIGINT", ()=>{
onShutdown();
process.exit(0);
});
process.once("SIGTERM", ()=>{
onShutdown();
process.exit(0);
});
return {
driver: "bun:sqlite",
run (sql, params = []) {
const r = prepare(sql).run(...params);
return {
changes: Number(r.changes ?? 0),
lastInsertRowid: Number(r.lastInsertRowid ?? 0)
};
},
get (sql, params = []) {
return prepare(sql).get(...params);
},
all (sql, params = []) {
return prepare(sql).all(...params);
},
exec (sql) {
return db.exec(sql);
},
transaction (fn) {
// bun:sqlite has db.transaction() API (similar to better-sqlite3)
const tx = db.transaction(fn);
return tx();
},
checkpoint () {
try {
db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
} catch {}
},
close () {
clearInterval(checkpointTimer);
gracefulClose();
},
raw: db
};
}
}),
"[project]/src/lib/db/schema.js [middleware] (ecmascript)", ((__turbopack_context__) => {
"use strict";
// ⚠️ AGENT/DEV: Bump this by +1 EVERY TIME you change the schema below
// (add/remove/alter a table, column, or index in TABLES). It drives the
// pre-change safety backup in migrate.js: when the stored version is lower,
// one lightweight DB backup is taken before applying schema changes. Forgetting
// to bump only skips that backup — it does NOT break the additive auto-sync.
__turbopack_context__.s([
"PRAGMA_SQL",
()=>PRAGMA_SQL,
"SCHEMA_VERSION",
()=>SCHEMA_VERSION,
"TABLES",
()=>TABLES,
"buildCreateTableSql",
()=>buildCreateTableSql
]);
const SCHEMA_VERSION = 3;
const PRAGMA_SQL = `
PRAGMA journal_mode = WAL;
PRAGMA synchronous = NORMAL;
PRAGMA temp_store = MEMORY;
PRAGMA mmap_size = 30000000;
PRAGMA cache_size = -64000;
PRAGMA foreign_keys = ON;
PRAGMA busy_timeout = 5000;
`;
const TABLES = {
_meta: {
columns: {
key: "TEXT PRIMARY KEY",
value: "TEXT NOT NULL"
}
},
settings: {
columns: {
id: "INTEGER PRIMARY KEY CHECK (id = 1)",
data: "TEXT NOT NULL"
}
},
providerConnections: {
columns: {
id: "TEXT PRIMARY KEY",
provider: "TEXT NOT NULL",
authType: "TEXT NOT NULL",
name: "TEXT",
email: "TEXT",
priority: "INTEGER",
isActive: "INTEGER DEFAULT 1",
data: "TEXT NOT NULL",
createdAt: "TEXT NOT NULL",
updatedAt: "TEXT NOT NULL"
},
indexes: [
"CREATE INDEX IF NOT EXISTS idx_pc_provider ON providerConnections(provider)",
"CREATE INDEX IF NOT EXISTS idx_pc_provider_active ON providerConnections(provider, isActive)",
"CREATE INDEX IF NOT EXISTS idx_pc_priority ON providerConnections(provider, priority)"
]
},
providerNodes: {
columns: {
id: "TEXT PRIMARY KEY",
type: "TEXT",
name: "TEXT",
data: "TEXT NOT NULL",
createdAt: "TEXT NOT NULL",
updatedAt: "TEXT NOT NULL"
},
indexes: [
"CREATE INDEX IF NOT EXISTS idx_pn_type ON providerNodes(type)"
]
},
proxyPools: {
columns: {
id: "TEXT PRIMARY KEY",
isActive: "INTEGER DEFAULT 1",
testStatus: "TEXT",
data: "TEXT NOT NULL",
createdAt: "TEXT NOT NULL",
updatedAt: "TEXT NOT NULL"
},
indexes: [
"CREATE INDEX IF NOT EXISTS idx_pp_active ON proxyPools(isActive)",
"CREATE INDEX IF NOT EXISTS idx_pp_status ON proxyPools(testStatus)"
]
},
apiKeys: {
columns: {
id: "TEXT PRIMARY KEY",
key: "TEXT UNIQUE NOT NULL",
name: "TEXT",
machineId: "TEXT",
isActive: "INTEGER DEFAULT 1",
allowedModels: "TEXT"
},
indexes: [
"CREATE INDEX IF NOT EXISTS idx_ak_key ON apiKeys(key)"
]
},
combos: {
columns: {
id: "TEXT PRIMARY KEY",
name: "TEXT UNIQUE NOT NULL",
kind: "TEXT",
models: "TEXT NOT NULL",
enabled: "INTEGER NOT NULL DEFAULT 1",
createdAt: "TEXT NOT NULL",
updatedAt: "TEXT NOT NULL"
},
indexes: [
"CREATE INDEX IF NOT EXISTS idx_combo_name ON combos(name)"
]
},
kv: {
columns: {
scope: "TEXT NOT NULL",
key: "TEXT NOT NULL",
value: "TEXT NOT NULL"
},
primaryKey: "PRIMARY KEY (scope, key)",
indexes: [
"CREATE INDEX IF NOT EXISTS idx_kv_scope ON kv(scope)"
]
},
usageHistory: {
columns: {
id: "INTEGER PRIMARY KEY AUTOINCREMENT",
timestamp: "TEXT NOT NULL",
provider: "TEXT",
model: "TEXT",
connectionId: "TEXT",
apiKey: "TEXT",
endpoint: "TEXT",
promptTokens: "INTEGER DEFAULT 0",
completionTokens: "INTEGER DEFAULT 0",
cost: "REAL DEFAULT 0",
status: "TEXT",
tokens: "TEXT",
meta: "TEXT"
},
indexes: [
"CREATE INDEX IF NOT EXISTS idx_uh_ts ON usageHistory(timestamp DESC)",
"CREATE INDEX IF NOT EXISTS idx_uh_provider ON usageHistory(provider)",
"CREATE INDEX IF NOT EXISTS idx_uh_model ON usageHistory(model)",
"CREATE INDEX IF NOT EXISTS idx_uh_conn ON usageHistory(connectionId)"
]
},
usageDaily: {
columns: {
dateKey: "TEXT PRIMARY KEY",
data: "TEXT NOT NULL"
}
},
requestDetails: {
columns: {
id: "TEXT PRIMARY KEY",
timestamp: "TEXT NOT NULL",
provider: "TEXT",
model: "TEXT",
connectionId: "TEXT",
status: "TEXT",
apiKey: "TEXT",
data: "TEXT NOT NULL"
},
indexes: [
"CREATE INDEX IF NOT EXISTS idx_rd_ts ON requestDetails(timestamp DESC)",
"CREATE INDEX IF NOT EXISTS idx_rd_provider ON requestDetails(provider)",
"CREATE INDEX IF NOT EXISTS idx_rd_model ON requestDetails(model)",
"CREATE INDEX IF NOT EXISTS idx_rd_conn ON requestDetails(connectionId)",
"CREATE INDEX IF NOT EXISTS idx_rd_apikey ON requestDetails(apiKey)"
]
}
};
function buildCreateTableSql(name, def) {
const cols = Object.entries(def.columns).map(([k, v])=>`${k} ${v}`);
if (def.primaryKey) cols.push(def.primaryKey);
return `CREATE TABLE IF NOT EXISTS ${name} (${cols.join(", ")})`;
}
}),
];
//# sourceMappingURL=src_lib_db_04-1tv7._.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,741 @@
module.exports = [
"[project]/src/lib/db/backup.js [middleware] (ecmascript)", ((__turbopack_context__) => {
"use strict";
__turbopack_context__.s([
"backupDbLite",
()=>backupDbLite,
"backupFile",
()=>backupFile,
"makeBackupDir",
()=>makeBackupDir,
"pruneOldBackups",
()=>pruneOldBackups
]);
// DB safety backups — taken ONLY before a schema change (see migrate.js).
//
// ⚠️ AGENT/DEV NOTES:
// - Backups are a best-effort safety net before schema migrations. There is NO
// automated restore path; recovery is manual (copy a backup file back).
// - Backups intentionally EXCLUDE the `requestDetails` table (observability log,
// auto-pruned, non-critical) so a multi-hundred-MB DB backs up as a few MB.
// - Only the newest KEEP_BACKUPS are kept; older ones are pruned automatically.
var __TURBOPACK__imported__module__$5b$externals$5d2f$node$3a$fs__$5b$external$5d$__$28$node$3a$fs$2c$__cjs$29$__ = __turbopack_context__.i("[externals]/node:fs [external] (node:fs, cjs)");
var __TURBOPACK__imported__module__$5b$externals$5d2f$node$3a$path__$5b$external$5d$__$28$node$3a$path$2c$__cjs$29$__ = __turbopack_context__.i("[externals]/node:path [external] (node:path, cjs)");
var __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$paths$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/src/lib/db/paths.js [middleware] (ecmascript)");
var __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$version$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/src/lib/db/version.js [middleware] (ecmascript)");
;
;
;
;
const KEEP_BACKUPS = 3;
// Tables excluded from safety backups (large, non-critical, reproducible).
const BACKUP_EXCLUDE_TABLES = [
"requestDetails"
];
function makeBackupDir(label) {
(0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$paths$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["ensureDirs"])();
const ver = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$version$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["getAppVersion"])();
const slug = `${label}-${ver}-${(0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$version$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["timestampSlug"])()}`;
const dir = __TURBOPACK__imported__module__$5b$externals$5d2f$node$3a$path__$5b$external$5d$__$28$node$3a$path$2c$__cjs$29$__["default"].join(__TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$paths$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["BACKUPS_DIR"], slug);
__TURBOPACK__imported__module__$5b$externals$5d2f$node$3a$fs__$5b$external$5d$__$28$node$3a$fs$2c$__cjs$29$__["default"].mkdirSync(dir, {
recursive: true
});
return dir;
}
function backupFile(srcPath, destDir, destName = null) {
if (!__TURBOPACK__imported__module__$5b$externals$5d2f$node$3a$fs__$5b$external$5d$__$28$node$3a$fs$2c$__cjs$29$__["default"].existsSync(srcPath)) return null;
const name = destName || __TURBOPACK__imported__module__$5b$externals$5d2f$node$3a$path__$5b$external$5d$__$28$node$3a$path$2c$__cjs$29$__["default"].basename(srcPath);
const dest = __TURBOPACK__imported__module__$5b$externals$5d2f$node$3a$path__$5b$external$5d$__$28$node$3a$path$2c$__cjs$29$__["default"].join(destDir, name);
__TURBOPACK__imported__module__$5b$externals$5d2f$node$3a$fs__$5b$external$5d$__$28$node$3a$fs$2c$__cjs$29$__["default"].copyFileSync(srcPath, dest);
return dest;
}
function backupDbLite(adapter, destDir, destName = "data.sqlite") {
const dest = __TURBOPACK__imported__module__$5b$externals$5d2f$node$3a$path__$5b$external$5d$__$28$node$3a$path$2c$__cjs$29$__["default"].join(destDir, destName);
try {
__TURBOPACK__imported__module__$5b$externals$5d2f$node$3a$fs__$5b$external$5d$__$28$node$3a$fs$2c$__cjs$29$__["default"].rmSync(dest, {
force: true
});
} catch {}
const escaped = dest.replace(/'/g, "''");
adapter.exec(`ATTACH DATABASE '${escaped}' AS bak`);
try {
const excluded = new Set(BACKUP_EXCLUDE_TABLES);
const tables = adapter.all(`SELECT name, sql FROM main.sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'`).filter((t)=>!excluded.has(t.name));
adapter.transaction(()=>{
for (const t of tables){
// Recreate table structure in backup DB, then copy rows.
const createSql = t.sql.replace(/CREATE TABLE\s+/i, "CREATE TABLE bak.");
adapter.exec(createSql);
adapter.exec(`INSERT INTO bak.${t.name} SELECT * FROM main.${t.name}`);
}
});
} finally{
try {
adapter.exec("DETACH DATABASE bak");
} catch {}
}
return dest;
}
function pruneOldBackups() {
if (!__TURBOPACK__imported__module__$5b$externals$5d2f$node$3a$fs__$5b$external$5d$__$28$node$3a$fs$2c$__cjs$29$__["default"].existsSync(__TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$paths$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["BACKUPS_DIR"])) return;
const entries = __TURBOPACK__imported__module__$5b$externals$5d2f$node$3a$fs__$5b$external$5d$__$28$node$3a$fs$2c$__cjs$29$__["default"].readdirSync(__TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$paths$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["BACKUPS_DIR"], {
withFileTypes: true
}).filter((e)=>e.isDirectory()).map((e)=>({
name: e.name,
full: __TURBOPACK__imported__module__$5b$externals$5d2f$node$3a$path__$5b$external$5d$__$28$node$3a$path$2c$__cjs$29$__["default"].join(__TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$paths$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["BACKUPS_DIR"], e.name),
mtime: __TURBOPACK__imported__module__$5b$externals$5d2f$node$3a$fs__$5b$external$5d$__$28$node$3a$fs$2c$__cjs$29$__["default"].statSync(__TURBOPACK__imported__module__$5b$externals$5d2f$node$3a$path__$5b$external$5d$__$28$node$3a$path$2c$__cjs$29$__["default"].join(__TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$paths$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["BACKUPS_DIR"], e.name)).mtimeMs
})).sort((a, b)=>b.mtime - a.mtime);
for (const old of entries.slice(KEEP_BACKUPS)){
try {
__TURBOPACK__imported__module__$5b$externals$5d2f$node$3a$fs__$5b$external$5d$__$28$node$3a$fs$2c$__cjs$29$__["default"].rmSync(old.full, {
recursive: true,
force: true
});
} catch {}
}
}
}),
"[project]/src/lib/db/migrate.js [middleware] (ecmascript)", ((__turbopack_context__) => {
"use strict";
__turbopack_context__.s([
"MigrationAborted",
()=>MigrationAborted,
"runMigrationOnce",
()=>runMigrationOnce
]);
var __TURBOPACK__imported__module__$5b$externals$5d2f$node$3a$fs__$5b$external$5d$__$28$node$3a$fs$2c$__cjs$29$__ = __turbopack_context__.i("[externals]/node:fs [external] (node:fs, cjs)");
var __TURBOPACK__imported__module__$5b$externals$5d2f$node$3a$path__$5b$external$5d$__$28$node$3a$path$2c$__cjs$29$__ = __turbopack_context__.i("[externals]/node:path [external] (node:path, cjs)");
var __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$paths$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/src/lib/db/paths.js [middleware] (ecmascript)");
var __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$schema$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/src/lib/db/schema.js [middleware] (ecmascript)");
var __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$migrations$2f$index$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/src/lib/db/migrations/index.js [middleware] (ecmascript)");
var __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$helpers$2f$metaStore$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/src/lib/db/helpers/metaStore.js [middleware] (ecmascript)");
var __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$backup$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/src/lib/db/backup.js [middleware] (ecmascript)");
var __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$version$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/src/lib/db/version.js [middleware] (ecmascript)");
var __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$helpers$2f$jsonCol$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/src/lib/db/helpers/jsonCol.js [middleware] (ecmascript)");
;
;
;
;
;
;
;
;
;
// Marker file: prevents re-importing legacy JSON when user wipes data.sqlite.
const MIGRATED_MARKER = __TURBOPACK__imported__module__$5b$externals$5d2f$node$3a$path__$5b$external$5d$__$28$node$3a$path$2c$__cjs$29$__["default"].join(__TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$paths$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["DB_DIR"], ".migrated-from-json");
// Track per-adapter so reusing same adapter skips re-run, but new adapter (after reset) re-runs.
const _migratedAdapters = new WeakSet();
class MigrationAborted extends Error {
constructor(message, droppedRows){
super(message);
this.name = "MigrationAborted";
this.droppedRows = droppedRows;
}
}
// Insert rows one-by-one, collect failures, then assert COUNT(*) matches input length.
function importWithAssertion(adapter, tableName, rows, insertFn, rowMeta) {
const dropped = [];
for (const row of rows){
try {
insertFn(row);
} catch (err) {
dropped.push({
...rowMeta(row),
reason: err.message
});
}
}
const inserted = adapter.get(`SELECT COUNT(*) as c FROM ${tableName}`)?.c ?? 0;
if (inserted !== rows.length) {
console.warn(`[DB][migrate] ${tableName} row-count mismatch: expected ${rows.length}, got ${inserted}. Dropped:`, dropped);
throw new MigrationAborted(`${tableName} row-count mismatch: expected ${rows.length}, got ${inserted}`, dropped);
}
}
function readJsonSafe(file) {
if (!__TURBOPACK__imported__module__$5b$externals$5d2f$node$3a$fs__$5b$external$5d$__$28$node$3a$fs$2c$__cjs$29$__["default"].existsSync(file)) return null;
try {
return JSON.parse(__TURBOPACK__imported__module__$5b$externals$5d2f$node$3a$fs__$5b$external$5d$__$28$node$3a$fs$2c$__cjs$29$__["default"].readFileSync(file, "utf-8"));
} catch {
return null;
}
}
function isFreshDb(adapter) {
// Table _meta may not exist yet on truly fresh DB
try {
const row = adapter.get(`SELECT COUNT(*) as c FROM _meta`);
return !row || row.c === 0;
} catch {
return true;
}
}
// ─── Versioned migrations runner (skip-version safe) ─────────────────────
function runVersionedMigrations(adapter) {
// Bootstrap _meta first so we can read schemaVersion
adapter.exec((0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$schema$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["buildCreateTableSql"])("_meta", __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$schema$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["TABLES"]._meta));
const current = parseInt((0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$helpers$2f$metaStore$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["getMetaSync"])(adapter, "schemaVersion", "0"), 10) || 0;
const target = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$migrations$2f$index$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["latestVersion"])();
if (current >= target) return {
applied: 0,
from: current,
to: current
};
const pending = __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$migrations$2f$index$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["MIGRATIONS"].filter((m)=>m.version > current);
let lastApplied = current;
for (const m of pending){
adapter.transaction(()=>{
m.up(adapter);
(0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$helpers$2f$metaStore$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["setMetaSync"])(adapter, "schemaVersion", m.version);
});
lastApplied = m.version;
console.log(`[DB][migrate] applied #${m.version} ${m.name}`);
}
return {
applied: pending.length,
from: current,
to: lastApplied
};
}
// ─── Auto-sync (additive only): add missing tables/columns/indexes ───────
function syncSchemaFromTables(adapter) {
for (const [tableName, def] of Object.entries(__TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$schema$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["TABLES"])){
// Create table if absent
adapter.exec((0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$schema$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["buildCreateTableSql"])(tableName, def));
// Diff columns
const existing = adapter.all(`PRAGMA table_info(${tableName})`);
const existingNames = new Set(existing.map((r)=>r.name));
for (const [colName, colDef] of Object.entries(def.columns)){
if (!existingNames.has(colName)) {
// SQLite ADD COLUMN restrictions: no PRIMARY KEY / UNIQUE w/o NULL ok.
// We strip PRIMARY KEY / UNIQUE since those are only valid at create time.
const safeDef = colDef.replace(/PRIMARY KEY( AUTOINCREMENT)?/i, "").replace(/UNIQUE/i, "").trim();
try {
adapter.exec(`ALTER TABLE ${tableName} ADD COLUMN ${colName} ${safeDef}`);
console.log(`[DB][sync] +column ${tableName}.${colName}`);
} catch (e) {
console.warn(`[DB][sync] add column ${tableName}.${colName} failed: ${e.message}`);
}
}
}
// Indexes (idempotent)
for (const idx of def.indexes || []){
try {
adapter.exec(idx);
} catch {}
}
}
}
// ─── Legacy JSON import (one-time) ───────────────────────────────────────
function importLegacyMain(adapter, data) {
if (!data || typeof data !== "object") return;
if (data.settings) {
adapter.run(`INSERT INTO settings(id, data) VALUES(1, ?) ON CONFLICT(id) DO UPDATE SET data = excluded.data`, [
(0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$helpers$2f$jsonCol$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["stringifyJson"])(data.settings)
]);
}
importWithAssertion(adapter, "providerConnections", data.providerConnections || [], (c)=>{
const { id, provider, authType, name, email, priority, isActive, createdAt, updatedAt, ...rest } = c;
adapter.run(`INSERT OR REPLACE INTO providerConnections(id, provider, authType, name, email, priority, isActive, data, createdAt, updatedAt) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
id,
provider,
authType || "oauth",
name || null,
email || null,
priority || null,
isActive === false ? 0 : 1,
(0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$helpers$2f$jsonCol$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["stringifyJson"])(rest),
createdAt || new Date().toISOString(),
updatedAt || new Date().toISOString()
]);
}, (c)=>({
id: c.id ?? null,
provider: c.provider ?? null,
name: c.name ?? null
}));
importWithAssertion(adapter, "providerNodes", data.providerNodes || [], (n)=>{
const { id, type, name, createdAt, updatedAt, ...rest } = n;
adapter.run(`INSERT OR REPLACE INTO providerNodes(id, type, name, data, createdAt, updatedAt) VALUES(?, ?, ?, ?, ?, ?)`, [
id,
type || null,
name || null,
(0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$helpers$2f$jsonCol$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["stringifyJson"])(rest),
createdAt || new Date().toISOString(),
updatedAt || new Date().toISOString()
]);
}, (n)=>({
id: n.id ?? null,
type: n.type ?? null,
name: n.name ?? null
}));
importWithAssertion(adapter, "proxyPools", data.proxyPools || [], (p)=>{
const { id, isActive, testStatus, createdAt, updatedAt, ...rest } = p;
adapter.run(`INSERT OR REPLACE INTO proxyPools(id, isActive, testStatus, data, createdAt, updatedAt) VALUES(?, ?, ?, ?, ?, ?)`, [
id,
isActive === false ? 0 : 1,
testStatus || "unknown",
(0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$helpers$2f$jsonCol$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["stringifyJson"])(rest),
createdAt || new Date().toISOString(),
updatedAt || new Date().toISOString()
]);
}, (p)=>({
id: p.id ?? null
}));
importWithAssertion(adapter, "apiKeys", data.apiKeys || [], (k)=>{
adapter.run(`INSERT OR REPLACE INTO apiKeys(id, key, name, machineId, isActive, createdAt) VALUES(?, ?, ?, ?, ?, ?)`, [
k.id,
k.key,
k.name || null,
k.machineId || null,
k.isActive === false ? 0 : 1,
k.createdAt || new Date().toISOString()
]);
}, (k)=>({
id: k.id ?? null,
name: k.name ?? null
}));
importWithAssertion(adapter, "combos", data.combos || [], (c)=>{
adapter.run(`INSERT OR REPLACE INTO combos(id, name, kind, models, createdAt, updatedAt) VALUES(?, ?, ?, ?, ?, ?)`, [
c.id,
c.name,
c.kind || null,
(0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$helpers$2f$jsonCol$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["stringifyJson"])(c.models || []),
c.createdAt || new Date().toISOString(),
c.updatedAt || new Date().toISOString()
]);
}, (c)=>({
id: c.id ?? null,
name: c.name ?? null
}));
for (const [alias, model] of Object.entries(data.modelAliases || {})){
adapter.run(`INSERT OR REPLACE INTO kv(scope, key, value) VALUES('modelAliases', ?, ?)`, [
alias,
(0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$helpers$2f$jsonCol$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["stringifyJson"])(model)
]);
}
for (const m of data.customModels || []){
const k = `${m.providerAlias}|${m.id}|${m.type || "llm"}`;
adapter.run(`INSERT OR REPLACE INTO kv(scope, key, value) VALUES('customModels', ?, ?)`, [
k,
(0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$helpers$2f$jsonCol$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["stringifyJson"])(m)
]);
}
for (const [tool, mappings] of Object.entries(data.mitmAlias || {})){
adapter.run(`INSERT OR REPLACE INTO kv(scope, key, value) VALUES('mitmAlias', ?, ?)`, [
tool,
(0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$helpers$2f$jsonCol$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["stringifyJson"])(mappings || {})
]);
}
for (const [provider, models] of Object.entries(data.pricing || {})){
adapter.run(`INSERT OR REPLACE INTO kv(scope, key, value) VALUES('pricing', ?, ?)`, [
provider,
(0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$helpers$2f$jsonCol$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["stringifyJson"])(models || {})
]);
}
}
function importLegacyUsage(adapter, data) {
if (!data || typeof data !== "object") return;
for (const e of data.history || []){
const t = e.tokens || {};
adapter.run(`INSERT INTO usageHistory(timestamp, provider, model, connectionId, apiKey, endpoint, promptTokens, completionTokens, cost, status, tokens, meta) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
e.timestamp || new Date().toISOString(),
e.provider || null,
e.model || null,
e.connectionId || null,
e.apiKey || null,
e.endpoint || null,
t.prompt_tokens || t.input_tokens || 0,
t.completion_tokens || t.output_tokens || 0,
e.cost || 0,
e.status || "ok",
(0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$helpers$2f$jsonCol$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["stringifyJson"])(t),
(0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$helpers$2f$jsonCol$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["stringifyJson"])({})
]);
}
for (const [dateKey, day] of Object.entries(data.dailySummary || {})){
adapter.run(`INSERT OR REPLACE INTO usageDaily(dateKey, data) VALUES(?, ?)`, [
dateKey,
(0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$helpers$2f$jsonCol$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["stringifyJson"])(day)
]);
}
if (typeof data.totalRequestsLifetime === "number") {
(0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$helpers$2f$metaStore$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["setMetaSync"])(adapter, "totalRequestsLifetime", data.totalRequestsLifetime);
}
}
function importLegacyDisabled(adapter, data) {
if (!data || typeof data.disabled !== "object") return;
for (const [provider, ids] of Object.entries(data.disabled)){
adapter.run(`INSERT OR REPLACE INTO kv(scope, key, value) VALUES('disabledModels', ?, ?)`, [
provider,
(0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$helpers$2f$jsonCol$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["stringifyJson"])(ids || [])
]);
}
}
function importLegacyDetails(adapter, data) {
if (!data || !Array.isArray(data.records)) return;
for (const r of data.records){
adapter.run(`INSERT OR REPLACE INTO requestDetails(id, timestamp, provider, model, connectionId, status, data) VALUES(?, ?, ?, ?, ?, ?, ?)`, [
r.id,
r.timestamp || new Date().toISOString(),
r.provider || null,
r.model || null,
r.connectionId || null,
r.status || null,
(0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$helpers$2f$jsonCol$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["stringifyJson"])(r)
]);
}
}
async function runMigrationOnce(adapter) {
if (_migratedAdapters.has(adapter)) return;
_migratedAdapters.add(adapter);
// Capture freshness BEFORE migrations stamp _meta (otherwise we'd misclassify
// a brand-new DB as non-fresh once schemaVersion is written).
const fresh = isFreshDb(adapter);
// Prune stale backups every boot so old oversized backups shrink to KEEP.
(0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$backup$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["pruneOldBackups"])();
// Bootstrap _meta so we can read the stored backup schema version below
// (runVersionedMigrations also ensures this, but we need it earlier here).
adapter.exec((0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$schema$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["buildCreateTableSql"])("_meta", __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$schema$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["TABLES"]._meta));
// Detect a pending schema change via the central SCHEMA_VERSION const.
// A lightweight backup is taken BEFORE any schema mutation below.
const storedSchemaVer = parseInt((0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$helpers$2f$metaStore$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["getMetaSync"])(adapter, "backupSchemaVersion", "0"), 10) || 0;
const schemaChanging = !fresh && storedSchemaVer < __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$schema$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["SCHEMA_VERSION"];
if (schemaChanging) {
try {
const backupDir = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$backup$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["makeBackupDir"])(`schema-${storedSchemaVer}-to-${__TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$schema$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["SCHEMA_VERSION"]}`);
(0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$backup$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["backupDbLite"])(adapter, backupDir);
(0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$backup$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["pruneOldBackups"])();
console.log(`[DB][migrate] pre-schema backup ${storedSchemaVer} → ${__TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$schema$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["SCHEMA_VERSION"]}: ${backupDir}`);
} catch (e) {
console.warn(`[DB][migrate] pre-schema backup failed (continuing): ${e.message}`);
}
}
// 1. Always run versioned migrations chain (skip-version safe)
const migInfo = runVersionedMigrations(adapter);
// 2. Additive sync (auto add missing columns/indexes declared in TABLES)
syncSchemaFromTables(adapter);
// Stamp the schema version we just reached so future boots skip re-backup.
(0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$helpers$2f$metaStore$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["setMetaSync"])(adapter, "backupSchemaVersion", __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$schema$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["SCHEMA_VERSION"]);
// 3. One-time legacy JSON import (only if DB was fresh on entry)
const alreadyImported = __TURBOPACK__imported__module__$5b$externals$5d2f$node$3a$fs__$5b$external$5d$__$28$node$3a$fs$2c$__cjs$29$__["default"].existsSync(MIGRATED_MARKER);
const legacyMain = readJsonSafe(__TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$paths$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["LEGACY_FILES"].main);
const legacyUsage = readJsonSafe(__TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$paths$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["LEGACY_FILES"].usage);
const legacyDisabled = readJsonSafe(__TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$paths$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["LEGACY_FILES"].disabled);
const legacyDetails = readJsonSafe(__TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$paths$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["LEGACY_FILES"].details);
const hasLegacy = !!(legacyMain || legacyUsage || legacyDisabled || legacyDetails);
if (fresh && hasLegacy && !alreadyImported) {
const t0 = Date.now();
const backupDir = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$backup$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["makeBackupDir"])("migrate-from-json");
for (const f of Object.values(__TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$paths$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["LEGACY_FILES"]))(0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$backup$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["backupFile"])(f, backupDir);
try {
adapter.transaction(()=>{
importLegacyMain(adapter, legacyMain);
importLegacyUsage(adapter, legacyUsage);
importLegacyDisabled(adapter, legacyDisabled);
importLegacyDetails(adapter, legacyDetails);
(0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$helpers$2f$metaStore$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["setMetaSync"])(adapter, "appVersion", (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$version$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["getAppVersion"])());
(0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$helpers$2f$metaStore$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["setMetaSync"])(adapter, "backupSchemaVersion", __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$schema$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["SCHEMA_VERSION"]);
(0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$helpers$2f$metaStore$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["setMetaSync"])(adapter, "migratedAt", new Date().toISOString());
});
} catch (err) {
if (err instanceof MigrationAborted) {
console.error(`[DB][migrate] aborted: ${err.message} | legacy JSON kept | backup: ${backupDir}`);
return;
}
throw err;
}
try {
__TURBOPACK__imported__module__$5b$externals$5d2f$node$3a$fs__$5b$external$5d$__$28$node$3a$fs$2c$__cjs$29$__["default"].writeFileSync(MIGRATED_MARKER, new Date().toISOString());
} catch {}
(0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$backup$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["pruneOldBackups"])();
console.log(`[DB][migrate] JSON → SQLite in ${Date.now() - t0}ms | legacy JSON kept at DATA_DIR | backup: ${backupDir}`);
return;
}
// Track app version for informational purposes only. App version bumps no
// longer trigger a DB backup — only real schema changes (SCHEMA_VERSION) do.
const newVer = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$version$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["getAppVersion"])();
const oldVer = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$helpers$2f$metaStore$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["getMetaSync"])(adapter, "appVersion", null);
if (oldVer !== newVer) (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$helpers$2f$metaStore$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["setMetaSync"])(adapter, "appVersion", newVer);
}
}),
"[project]/src/lib/db/migrations/001-initial.js [middleware] (ecmascript)", ((__turbopack_context__) => {
"use strict";
__turbopack_context__.s([
"default",
()=>__TURBOPACK__default__export__
]);
// Initial schema bootstrap. For fresh DB this creates all tables/indexes.
// For existing DB at version 0 (legacy unstamped), it's idempotent (IF NOT EXISTS).
var __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$schema$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/src/lib/db/schema.js [middleware] (ecmascript)");
;
const __TURBOPACK__default__export__ = {
version: 1,
name: "initial",
up (db) {
for (const [name, def] of Object.entries(__TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$schema$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["TABLES"])){
db.exec((0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$schema$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["buildCreateTableSql"])(name, def));
for (const idx of def.indexes || [])db.exec(idx);
}
}
};
}),
"[project]/src/lib/db/migrations/002-request-details-apikey.js [middleware] (ecmascript)", ((__turbopack_context__) => {
"use strict";
// Adds an indexed apiKey column to requestDetails so the Details tab can
// filter by (masked) API key without parsing the JSON data blob.
__turbopack_context__.s([
"default",
()=>__TURBOPACK__default__export__
]);
const __TURBOPACK__default__export__ = {
version: 2,
name: "request-details-apikey",
up (db) {
// Idempotent: fresh DBs already get the column from TABLES schema.
const existing = db.all(`PRAGMA table_info(requestDetails)`) || [];
if (!existing.some((c)=>c.name === "apiKey")) {
db.exec(`ALTER TABLE requestDetails ADD COLUMN apiKey TEXT`);
}
db.exec(`CREATE INDEX IF NOT EXISTS idx_rd_apikey ON requestDetails(apiKey)`);
}
};
}),
"[project]/src/lib/db/migrations/index.js [middleware] (ecmascript)", ((__turbopack_context__) => {
"use strict";
__turbopack_context__.s([
"MIGRATIONS",
()=>MIGRATIONS,
"latestVersion",
()=>latestVersion
]);
// Migration registry — append new entries when schema changes.
// Each migration: { version: number, name: string, up(db): void }
// Versions MUST be unique and monotonically increasing.
var __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$migrations$2f$001$2d$initial$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/src/lib/db/migrations/001-initial.js [middleware] (ecmascript)");
var __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$migrations$2f$002$2d$request$2d$details$2d$apikey$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/src/lib/db/migrations/002-request-details-apikey.js [middleware] (ecmascript)");
;
;
const MIGRATIONS = [
__TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$migrations$2f$001$2d$initial$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["default"],
__TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$migrations$2f$002$2d$request$2d$details$2d$apikey$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["default"]
].sort((a, b)=>a.version - b.version);
function latestVersion() {
return MIGRATIONS.length ? MIGRATIONS[MIGRATIONS.length - 1].version : 0;
}
}),
"[project]/src/lib/db/schema.js [middleware] (ecmascript)", ((__turbopack_context__) => {
"use strict";
// ⚠️ AGENT/DEV: Bump this by +1 EVERY TIME you change the schema below
// (add/remove/alter a table, column, or index in TABLES). It drives the
// pre-change safety backup in migrate.js: when the stored version is lower,
// one lightweight DB backup is taken before applying schema changes. Forgetting
// to bump only skips that backup — it does NOT break the additive auto-sync.
__turbopack_context__.s([
"PRAGMA_SQL",
()=>PRAGMA_SQL,
"SCHEMA_VERSION",
()=>SCHEMA_VERSION,
"TABLES",
()=>TABLES,
"buildCreateTableSql",
()=>buildCreateTableSql
]);
const SCHEMA_VERSION = 3;
const PRAGMA_SQL = `
PRAGMA journal_mode = WAL;
PRAGMA synchronous = NORMAL;
PRAGMA temp_store = MEMORY;
PRAGMA mmap_size = 30000000;
PRAGMA cache_size = -64000;
PRAGMA foreign_keys = ON;
PRAGMA busy_timeout = 5000;
`;
const TABLES = {
_meta: {
columns: {
key: "TEXT PRIMARY KEY",
value: "TEXT NOT NULL"
}
},
settings: {
columns: {
id: "INTEGER PRIMARY KEY CHECK (id = 1)",
data: "TEXT NOT NULL"
}
},
providerConnections: {
columns: {
id: "TEXT PRIMARY KEY",
provider: "TEXT NOT NULL",
authType: "TEXT NOT NULL",
name: "TEXT",
email: "TEXT",
priority: "INTEGER",
isActive: "INTEGER DEFAULT 1",
data: "TEXT NOT NULL",
createdAt: "TEXT NOT NULL",
updatedAt: "TEXT NOT NULL"
},
indexes: [
"CREATE INDEX IF NOT EXISTS idx_pc_provider ON providerConnections(provider)",
"CREATE INDEX IF NOT EXISTS idx_pc_provider_active ON providerConnections(provider, isActive)",
"CREATE INDEX IF NOT EXISTS idx_pc_priority ON providerConnections(provider, priority)"
]
},
providerNodes: {
columns: {
id: "TEXT PRIMARY KEY",
type: "TEXT",
name: "TEXT",
data: "TEXT NOT NULL",
createdAt: "TEXT NOT NULL",
updatedAt: "TEXT NOT NULL"
},
indexes: [
"CREATE INDEX IF NOT EXISTS idx_pn_type ON providerNodes(type)"
]
},
proxyPools: {
columns: {
id: "TEXT PRIMARY KEY",
isActive: "INTEGER DEFAULT 1",
testStatus: "TEXT",
data: "TEXT NOT NULL",
createdAt: "TEXT NOT NULL",
updatedAt: "TEXT NOT NULL"
},
indexes: [
"CREATE INDEX IF NOT EXISTS idx_pp_active ON proxyPools(isActive)",
"CREATE INDEX IF NOT EXISTS idx_pp_status ON proxyPools(testStatus)"
]
},
apiKeys: {
columns: {
id: "TEXT PRIMARY KEY",
key: "TEXT UNIQUE NOT NULL",
name: "TEXT",
machineId: "TEXT",
isActive: "INTEGER DEFAULT 1",
allowedModels: "TEXT"
},
indexes: [
"CREATE INDEX IF NOT EXISTS idx_ak_key ON apiKeys(key)"
]
},
combos: {
columns: {
id: "TEXT PRIMARY KEY",
name: "TEXT UNIQUE NOT NULL",
kind: "TEXT",
models: "TEXT NOT NULL",
enabled: "INTEGER NOT NULL DEFAULT 1",
createdAt: "TEXT NOT NULL",
updatedAt: "TEXT NOT NULL"
},
indexes: [
"CREATE INDEX IF NOT EXISTS idx_combo_name ON combos(name)"
]
},
kv: {
columns: {
scope: "TEXT NOT NULL",
key: "TEXT NOT NULL",
value: "TEXT NOT NULL"
},
primaryKey: "PRIMARY KEY (scope, key)",
indexes: [
"CREATE INDEX IF NOT EXISTS idx_kv_scope ON kv(scope)"
]
},
usageHistory: {
columns: {
id: "INTEGER PRIMARY KEY AUTOINCREMENT",
timestamp: "TEXT NOT NULL",
provider: "TEXT",
model: "TEXT",
connectionId: "TEXT",
apiKey: "TEXT",
endpoint: "TEXT",
promptTokens: "INTEGER DEFAULT 0",
completionTokens: "INTEGER DEFAULT 0",
cost: "REAL DEFAULT 0",
status: "TEXT",
tokens: "TEXT",
meta: "TEXT"
},
indexes: [
"CREATE INDEX IF NOT EXISTS idx_uh_ts ON usageHistory(timestamp DESC)",
"CREATE INDEX IF NOT EXISTS idx_uh_provider ON usageHistory(provider)",
"CREATE INDEX IF NOT EXISTS idx_uh_model ON usageHistory(model)",
"CREATE INDEX IF NOT EXISTS idx_uh_conn ON usageHistory(connectionId)"
]
},
usageDaily: {
columns: {
dateKey: "TEXT PRIMARY KEY",
data: "TEXT NOT NULL"
}
},
requestDetails: {
columns: {
id: "TEXT PRIMARY KEY",
timestamp: "TEXT NOT NULL",
provider: "TEXT",
model: "TEXT",
connectionId: "TEXT",
status: "TEXT",
apiKey: "TEXT",
data: "TEXT NOT NULL"
},
indexes: [
"CREATE INDEX IF NOT EXISTS idx_rd_ts ON requestDetails(timestamp DESC)",
"CREATE INDEX IF NOT EXISTS idx_rd_provider ON requestDetails(provider)",
"CREATE INDEX IF NOT EXISTS idx_rd_model ON requestDetails(model)",
"CREATE INDEX IF NOT EXISTS idx_rd_conn ON requestDetails(connectionId)",
"CREATE INDEX IF NOT EXISTS idx_rd_apikey ON requestDetails(apiKey)"
]
}
};
function buildCreateTableSql(name, def) {
const cols = Object.entries(def.columns).map(([k, v])=>`${k} ${v}`);
if (def.primaryKey) cols.push(def.primaryKey);
return `CREATE TABLE IF NOT EXISTS ${name} (${cols.join(", ")})`;
}
}),
"[project]/src/lib/db/version.js [middleware] (ecmascript)", ((__turbopack_context__) => {
"use strict";
__turbopack_context__.s([
"getAppVersion",
()=>getAppVersion,
"timestampSlug",
()=>timestampSlug
]);
var __TURBOPACK__imported__module__$5b$externals$5d2f$node$3a$fs__$5b$external$5d$__$28$node$3a$fs$2c$__cjs$29$__ = __turbopack_context__.i("[externals]/node:fs [external] (node:fs, cjs)");
var __TURBOPACK__imported__module__$5b$externals$5d2f$node$3a$path__$5b$external$5d$__$28$node$3a$path$2c$__cjs$29$__ = __turbopack_context__.i("[externals]/node:path [external] (node:path, cjs)");
;
;
let cachedVersion = null;
function getAppVersion() {
if (cachedVersion) return cachedVersion;
try {
const pkgPath = __TURBOPACK__imported__module__$5b$externals$5d2f$node$3a$path__$5b$external$5d$__$28$node$3a$path$2c$__cjs$29$__["default"].join(process.cwd(), "package.json");
const pkg = JSON.parse(__TURBOPACK__imported__module__$5b$externals$5d2f$node$3a$fs__$5b$external$5d$__$28$node$3a$fs$2c$__cjs$29$__["default"].readFileSync(pkgPath, "utf-8"));
cachedVersion = pkg.version || "0.0.0";
} catch {
cachedVersion = "0.0.0";
}
return cachedVersion;
}
function timestampSlug(date = new Date()) {
const pad = (n)=>String(n).padStart(2, "0");
return `${date.getFullYear()}${pad(date.getMonth() + 1)}${pad(date.getDate())}-${pad(date.getHours())}${pad(date.getMinutes())}${pad(date.getSeconds())}`;
}
}),
];
//# sourceMappingURL=src_lib_db_1jtxnmd._.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,295 @@
module.exports = [
"[project]/src/lib/db/adapters/nodeSqliteAdapter.js [middleware] (ecmascript)", ((__turbopack_context__) => {
"use strict";
__turbopack_context__.s([
"createNodeSqliteAdapter",
()=>createNodeSqliteAdapter
]);
// Built-in node:sqlite adapter — available in Node >= 22.5.0.
// No native build, no npm install. API mirrors betterSqliteAdapter.
var __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$schema$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/src/lib/db/schema.js [middleware] (ecmascript)");
;
const CHECKPOINT_INTERVAL_MS = 60 * 1000;
async function createNodeSqliteAdapter(filePath) {
// Suppress "ExperimentalWarning: SQLite is an experimental feature" from node:sqlite.
// Stable enough for production use as of Node 22.x (RC quality).
const origEmit = process.emit;
process.emit = function(name, data, ...rest) {
if (name === "warning" && data?.name === "ExperimentalWarning" && /SQLite/i.test(data.message || "")) {
return false;
}
return origEmit.call(process, name, data, ...rest);
};
// Dynamic import — fails on Node < 22.5 → driver.js falls back to sql.js
const sqlite = await Promise.resolve().then(()=>__turbopack_context__.x("node:sqlite", ()=>require("node:sqlite"), true));
const Database = sqlite.DatabaseSync;
const db = new Database(filePath);
db.exec(__TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$db$2f$schema$2e$js__$5b$middleware$5d$__$28$ecmascript$29$__["PRAGMA_SQL"]);
const stmtCache = new Map();
function prepare(sql) {
let stmt = stmtCache.get(sql);
if (!stmt) {
stmt = db.prepare(sql);
stmtCache.set(sql, stmt);
}
return stmt;
}
// Periodic WAL checkpoint to keep -wal/-shm small
const checkpointTimer = setInterval(()=>{
try {
db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
} catch {}
}, CHECKPOINT_INTERVAL_MS);
if (typeof checkpointTimer.unref === "function") checkpointTimer.unref();
function gracefulClose() {
try {
db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
} catch {}
try {
stmtCache.clear();
} catch {}
try {
db.close();
} catch {}
}
const onShutdown = ()=>gracefulClose();
process.once("beforeExit", onShutdown);
process.once("SIGINT", ()=>{
onShutdown();
process.exit(0);
});
process.once("SIGTERM", ()=>{
onShutdown();
process.exit(0);
});
return {
driver: "node:sqlite",
run (sql, params = []) {
const r = prepare(sql).run(...params);
return {
changes: Number(r.changes ?? 0),
lastInsertRowid: Number(r.lastInsertRowid ?? 0)
};
},
get (sql, params = []) {
return prepare(sql).get(...params);
},
all (sql, params = []) {
return prepare(sql).all(...params);
},
exec (sql) {
return db.exec(sql);
},
transaction (fn) {
// node:sqlite has no transaction wrapper. Use SAVEPOINT for nested support.
const sp = `sp_${Math.random().toString(36).slice(2)}`;
db.exec(`SAVEPOINT ${sp}`);
try {
const r = fn();
db.exec(`RELEASE ${sp}`);
return r;
} catch (e) {
try {
db.exec(`ROLLBACK TO ${sp}`);
db.exec(`RELEASE ${sp}`);
} catch {}
throw e;
}
},
checkpoint () {
try {
db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
} catch {}
},
close () {
clearInterval(checkpointTimer);
gracefulClose();
},
raw: db
};
}
}),
"[project]/src/lib/db/schema.js [middleware] (ecmascript)", ((__turbopack_context__) => {
"use strict";
// ⚠️ AGENT/DEV: Bump this by +1 EVERY TIME you change the schema below
// (add/remove/alter a table, column, or index in TABLES). It drives the
// pre-change safety backup in migrate.js: when the stored version is lower,
// one lightweight DB backup is taken before applying schema changes. Forgetting
// to bump only skips that backup — it does NOT break the additive auto-sync.
__turbopack_context__.s([
"PRAGMA_SQL",
()=>PRAGMA_SQL,
"SCHEMA_VERSION",
()=>SCHEMA_VERSION,
"TABLES",
()=>TABLES,
"buildCreateTableSql",
()=>buildCreateTableSql
]);
const SCHEMA_VERSION = 3;
const PRAGMA_SQL = `
PRAGMA journal_mode = WAL;
PRAGMA synchronous = NORMAL;
PRAGMA temp_store = MEMORY;
PRAGMA mmap_size = 30000000;
PRAGMA cache_size = -64000;
PRAGMA foreign_keys = ON;
PRAGMA busy_timeout = 5000;
`;
const TABLES = {
_meta: {
columns: {
key: "TEXT PRIMARY KEY",
value: "TEXT NOT NULL"
}
},
settings: {
columns: {
id: "INTEGER PRIMARY KEY CHECK (id = 1)",
data: "TEXT NOT NULL"
}
},
providerConnections: {
columns: {
id: "TEXT PRIMARY KEY",
provider: "TEXT NOT NULL",
authType: "TEXT NOT NULL",
name: "TEXT",
email: "TEXT",
priority: "INTEGER",
isActive: "INTEGER DEFAULT 1",
data: "TEXT NOT NULL",
createdAt: "TEXT NOT NULL",
updatedAt: "TEXT NOT NULL"
},
indexes: [
"CREATE INDEX IF NOT EXISTS idx_pc_provider ON providerConnections(provider)",
"CREATE INDEX IF NOT EXISTS idx_pc_provider_active ON providerConnections(provider, isActive)",
"CREATE INDEX IF NOT EXISTS idx_pc_priority ON providerConnections(provider, priority)"
]
},
providerNodes: {
columns: {
id: "TEXT PRIMARY KEY",
type: "TEXT",
name: "TEXT",
data: "TEXT NOT NULL",
createdAt: "TEXT NOT NULL",
updatedAt: "TEXT NOT NULL"
},
indexes: [
"CREATE INDEX IF NOT EXISTS idx_pn_type ON providerNodes(type)"
]
},
proxyPools: {
columns: {
id: "TEXT PRIMARY KEY",
isActive: "INTEGER DEFAULT 1",
testStatus: "TEXT",
data: "TEXT NOT NULL",
createdAt: "TEXT NOT NULL",
updatedAt: "TEXT NOT NULL"
},
indexes: [
"CREATE INDEX IF NOT EXISTS idx_pp_active ON proxyPools(isActive)",
"CREATE INDEX IF NOT EXISTS idx_pp_status ON proxyPools(testStatus)"
]
},
apiKeys: {
columns: {
id: "TEXT PRIMARY KEY",
key: "TEXT UNIQUE NOT NULL",
name: "TEXT",
machineId: "TEXT",
isActive: "INTEGER DEFAULT 1",
allowedModels: "TEXT"
},
indexes: [
"CREATE INDEX IF NOT EXISTS idx_ak_key ON apiKeys(key)"
]
},
combos: {
columns: {
id: "TEXT PRIMARY KEY",
name: "TEXT UNIQUE NOT NULL",
kind: "TEXT",
models: "TEXT NOT NULL",
enabled: "INTEGER NOT NULL DEFAULT 1",
createdAt: "TEXT NOT NULL",
updatedAt: "TEXT NOT NULL"
},
indexes: [
"CREATE INDEX IF NOT EXISTS idx_combo_name ON combos(name)"
]
},
kv: {
columns: {
scope: "TEXT NOT NULL",
key: "TEXT NOT NULL",
value: "TEXT NOT NULL"
},
primaryKey: "PRIMARY KEY (scope, key)",
indexes: [
"CREATE INDEX IF NOT EXISTS idx_kv_scope ON kv(scope)"
]
},
usageHistory: {
columns: {
id: "INTEGER PRIMARY KEY AUTOINCREMENT",
timestamp: "TEXT NOT NULL",
provider: "TEXT",
model: "TEXT",
connectionId: "TEXT",
apiKey: "TEXT",
endpoint: "TEXT",
promptTokens: "INTEGER DEFAULT 0",
completionTokens: "INTEGER DEFAULT 0",
cost: "REAL DEFAULT 0",
status: "TEXT",
tokens: "TEXT",
meta: "TEXT"
},
indexes: [
"CREATE INDEX IF NOT EXISTS idx_uh_ts ON usageHistory(timestamp DESC)",
"CREATE INDEX IF NOT EXISTS idx_uh_provider ON usageHistory(provider)",
"CREATE INDEX IF NOT EXISTS idx_uh_model ON usageHistory(model)",
"CREATE INDEX IF NOT EXISTS idx_uh_conn ON usageHistory(connectionId)"
]
},
usageDaily: {
columns: {
dateKey: "TEXT PRIMARY KEY",
data: "TEXT NOT NULL"
}
},
requestDetails: {
columns: {
id: "TEXT PRIMARY KEY",
timestamp: "TEXT NOT NULL",
provider: "TEXT",
model: "TEXT",
connectionId: "TEXT",
status: "TEXT",
apiKey: "TEXT",
data: "TEXT NOT NULL"
},
indexes: [
"CREATE INDEX IF NOT EXISTS idx_rd_ts ON requestDetails(timestamp DESC)",
"CREATE INDEX IF NOT EXISTS idx_rd_provider ON requestDetails(provider)",
"CREATE INDEX IF NOT EXISTS idx_rd_model ON requestDetails(model)",
"CREATE INDEX IF NOT EXISTS idx_rd_conn ON requestDetails(connectionId)",
"CREATE INDEX IF NOT EXISTS idx_rd_apikey ON requestDetails(apiKey)"
]
}
};
function buildCreateTableSql(name, def) {
const cols = Object.entries(def.columns).map(([k, v])=>`${k} ${v}`);
if (def.primaryKey) cols.push(def.primaryKey);
return `CREATE TABLE IF NOT EXISTS ${name} (${cols.join(", ")})`;
}
}),
];
//# sourceMappingURL=src_lib_db_1rn2qxk._.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,82 @@
module.exports = [
"[project]/src/shared/utils/apiKey.js [middleware] (ecmascript)", ((__turbopack_context__) => {
"use strict";
__turbopack_context__.s([
"generateApiKeyWithMachine",
()=>generateApiKeyWithMachine,
"isNewFormatKey",
()=>isNewFormatKey,
"parseApiKey",
()=>parseApiKey,
"verifyApiKeyCrc",
()=>verifyApiKeyCrc
]);
var __TURBOPACK__imported__module__$5b$externals$5d2f$crypto__$5b$external$5d$__$28$crypto$2c$__cjs$29$__ = __turbopack_context__.i("[externals]/crypto [external] (crypto, cjs)");
;
const API_KEY_SECRET = process.env.API_KEY_SECRET || "endpoint-proxy-api-key-secret";
/**
* Generate 6-char random keyId
*/ function generateKeyId() {
const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
let result = "";
for(let i = 0; i < 6; i++){
result += chars.charAt(Math.floor(Math.random() * chars.length));
}
return result;
}
/**
* Generate CRC (8-char HMAC)
*/ function generateCrc(machineId, keyId) {
return __TURBOPACK__imported__module__$5b$externals$5d2f$crypto__$5b$external$5d$__$28$crypto$2c$__cjs$29$__["default"].createHmac("sha256", API_KEY_SECRET).update(machineId + keyId).digest("hex").slice(0, 8);
}
function generateApiKeyWithMachine(machineId) {
const keyId = generateKeyId();
const crc = generateCrc(machineId, keyId);
const key = `sk-${machineId}-${keyId}-${crc}`;
return {
key,
keyId
};
}
function parseApiKey(apiKey) {
if (!apiKey || !apiKey.startsWith("sk-")) return null;
const parts = apiKey.split("-");
// New format: sk-{machineId}-{keyId}-{crc8} = 4 parts
if (parts.length === 4) {
const [, machineId, keyId, crc] = parts;
// Validate CRC
const expectedCrc = generateCrc(machineId, keyId);
if (crc !== expectedCrc) return null;
return {
machineId,
keyId,
isNewFormat: true
};
}
// Old format: sk-{random8} = 2 parts
if (parts.length === 2) {
return {
machineId: null,
keyId: parts[1],
isNewFormat: false
};
}
return null;
}
function verifyApiKeyCrc(apiKey) {
const parsed = parseApiKey(apiKey);
if (!parsed) return false;
// Old format doesn't have CRC, always valid if parsed
if (!parsed.isNewFormat) return true;
// New format already verified in parseApiKey
return true;
}
function isNewFormatKey(apiKey) {
const parsed = parseApiKey(apiKey);
return parsed?.isNewFormat === true;
}
}),
];
//# sourceMappingURL=src_shared_utils_apiKey_0dq53wj.js.map

View File

@@ -0,0 +1,6 @@
{
"version": 3,
"sources": [],
"sections": [
{"offset": {"line": 4, "column": 0}, "map": {"version":3,"sources":["file:///Users/luulam/Developer/tool/9router/src/shared/utils/apiKey.js"],"sourcesContent":["import crypto from \"crypto\";\n\nconst API_KEY_SECRET = process.env.API_KEY_SECRET || \"endpoint-proxy-api-key-secret\";\n\n/**\n * Generate 6-char random keyId\n */\nfunction generateKeyId() {\n const chars = \"abcdefghijklmnopqrstuvwxyz0123456789\";\n let result = \"\";\n for (let i = 0; i < 6; i++) {\n result += chars.charAt(Math.floor(Math.random() * chars.length));\n }\n return result;\n}\n\n/**\n * Generate CRC (8-char HMAC)\n */\nfunction generateCrc(machineId, keyId) {\n return crypto\n .createHmac(\"sha256\", API_KEY_SECRET)\n .update(machineId + keyId)\n .digest(\"hex\")\n .slice(0, 8);\n}\n\n/**\n * Generate API key with machineId embedded\n * Format: sk-{machineId}-{keyId}-{crc8}\n * @param {string} machineId - 16-char machine ID\n * @returns {{ key: string, keyId: string }}\n */\nexport function generateApiKeyWithMachine(machineId) {\n const keyId = generateKeyId();\n const crc = generateCrc(machineId, keyId);\n const key = `sk-${machineId}-${keyId}-${crc}`;\n return { key, keyId };\n}\n\n/**\n * Parse API key and extract machineId + keyId\n * Supports both formats:\n * - New: sk-{machineId}-{keyId}-{crc8}\n * - Old: sk-{random8}\n * @param {string} apiKey\n * @returns {{ machineId: string, keyId: string, isNewFormat: boolean } | null}\n */\nexport function parseApiKey(apiKey) {\n if (!apiKey || !apiKey.startsWith(\"sk-\")) return null;\n\n const parts = apiKey.split(\"-\");\n \n // New format: sk-{machineId}-{keyId}-{crc8} = 4 parts\n if (parts.length === 4) {\n const [, machineId, keyId, crc] = parts;\n \n // Validate CRC\n const expectedCrc = generateCrc(machineId, keyId);\n if (crc !== expectedCrc) return null;\n \n return { machineId, keyId, isNewFormat: true };\n }\n \n // Old format: sk-{random8} = 2 parts\n if (parts.length === 2) {\n return { machineId: null, keyId: parts[1], isNewFormat: false };\n }\n \n return null;\n}\n\n/**\n * Verify API key CRC (only for new format)\n * @param {string} apiKey\n * @returns {boolean}\n */\nexport function verifyApiKeyCrc(apiKey) {\n const parsed = parseApiKey(apiKey);\n if (!parsed) return false;\n \n // Old format doesn't have CRC, always valid if parsed\n if (!parsed.isNewFormat) return true;\n \n // New format already verified in parseApiKey\n return true;\n}\n\n/**\n * Check if API key is new format (contains machineId)\n * @param {string} apiKey\n * @returns {boolean}\n */\nexport function isNewFormatKey(apiKey) {\n const parsed = parseApiKey(apiKey);\n return parsed?.isNewFormat === true;\n}\n\n"],"names":["API_KEY_SECRET","process","env","generateKeyId","chars","result","i","charAt","Math","floor","random","length","generateCrc","machineId","keyId","createHmac","update","digest","slice","generateApiKeyWithMachine","crc","key","parseApiKey","apiKey","startsWith","parts","split","expectedCrc","isNewFormat","verifyApiKeyCrc","parsed","isNewFormatKey"],"mappings":";;;;;;;;;;AAAA;;AAEA,MAAMA,iBAAiBC,QAAQC,GAAG,CAACF,cAAc,IAAI;AAErD;;CAEC,GACD,SAASG;IACP,MAAMC,QAAQ;IACd,IAAIC,SAAS;IACb,IAAK,IAAIC,IAAI,GAAGA,IAAI,GAAGA,IAAK;QAC1BD,UAAUD,MAAMG,MAAM,CAACC,KAAKC,KAAK,CAACD,KAAKE,MAAM,KAAKN,MAAMO,MAAM;IAChE;IACA,OAAON;AACT;AAEA;;CAEC,GACD,SAASO,YAAYC,SAAS,EAAEC,KAAK;IACnC,OAAO,gHAAM,CACVC,UAAU,CAAC,UAAUf,gBACrBgB,MAAM,CAACH,YAAYC,OACnBG,MAAM,CAAC,OACPC,KAAK,CAAC,GAAG;AACd;AAQO,SAASC,0BAA0BN,SAAS;IACjD,MAAMC,QAAQX;IACd,MAAMiB,MAAMR,YAAYC,WAAWC;IACnC,MAAMO,MAAM,CAAC,GAAG,EAAER,UAAU,CAAC,EAAEC,MAAM,CAAC,EAAEM,KAAK;IAC7C,OAAO;QAAEC;QAAKP;IAAM;AACtB;AAUO,SAASQ,YAAYC,MAAM;IAChC,IAAI,CAACA,UAAU,CAACA,OAAOC,UAAU,CAAC,QAAQ,OAAO;IAEjD,MAAMC,QAAQF,OAAOG,KAAK,CAAC;IAE3B,sDAAsD;IACtD,IAAID,MAAMd,MAAM,KAAK,GAAG;QACtB,MAAM,GAAGE,WAAWC,OAAOM,IAAI,GAAGK;QAElC,eAAe;QACf,MAAME,cAAcf,YAAYC,WAAWC;QAC3C,IAAIM,QAAQO,aAAa,OAAO;QAEhC,OAAO;YAAEd;YAAWC;YAAOc,aAAa;QAAK;IAC/C;IAEA,qCAAqC;IACrC,IAAIH,MAAMd,MAAM,KAAK,GAAG;QACtB,OAAO;YAAEE,WAAW;YAAMC,OAAOW,KAAK,CAAC,EAAE;YAAEG,aAAa;QAAM;IAChE;IAEA,OAAO;AACT;AAOO,SAASC,gBAAgBN,MAAM;IACpC,MAAMO,SAASR,YAAYC;IAC3B,IAAI,CAACO,QAAQ,OAAO;IAEpB,sDAAsD;IACtD,IAAI,CAACA,OAAOF,WAAW,EAAE,OAAO;IAEhC,6CAA6C;IAC7C,OAAO;AACT;AAOO,SAASG,eAAeR,MAAM;IACnC,MAAMO,SAASR,YAAYC;IAC3B,OAAOO,QAAQF,gBAAgB;AACjC"}}]
}

View File

@@ -0,0 +1,40 @@
(globalThis["TURBOPACK"] || (globalThis["TURBOPACK"] = [])).push(["chunks/_06oe804._.js",
"[project]/node_modules/next/dist/esm/build/templates/edge-wrapper.js { MODULE => \"[project]/src/instrumentation.js [instrumentation-edge] (ecmascript)\" } [instrumentation-edge] (ecmascript)", ((__turbopack_context__, module, exports) => {
// The wrapped module could be an async module, we handle that with the proxy
// here. The comma expression makes sure we don't call the function with the
// module as the "this" arg.
// Turn exports into functions that are also a thenable. This way you can await the whole object
// or exports (e.g. for Components) or call them directly as though they are async functions
// (e.g. edge functions/middleware, this is what the Edge Runtime does).
// Catch promise to prevent UnhandledPromiseRejectionWarning, this will be propagated through
// the awaited export(s) anyway.
self._ENTRIES ||= {};
const modProm = Promise.resolve().then(()=>__turbopack_context__.i("[project]/src/instrumentation.js [instrumentation-edge] (ecmascript)"));
modProm.catch(()=>{});
self._ENTRIES["middleware_instrumentation"] = new Proxy(modProm, {
get (innerModProm, name) {
if (name === 'then') {
return (res, rej)=>innerModProm.then(res, rej);
}
let result = (...args)=>innerModProm.then((mod)=>(0, mod[name])(...args));
result.then = (res, rej)=>innerModProm.then((mod)=>mod[name]).then(res, rej);
return result;
}
});
}),
"[project]/src/instrumentation.js [instrumentation-edge] (ecmascript)", ((__turbopack_context__) => {
"use strict";
__turbopack_context__.s([
"register",
()=>register
]);
async function register() {
if ("TURBOPACK compile-time falsy", 0) //TURBOPACK unreachable
;
}
}),
]);
//# sourceMappingURL=_06oe804._.js.map

View File

@@ -0,0 +1,7 @@
{
"version": 3,
"sources": [],
"sections": [
{"offset": {"line": 3, "column": 0}, "map": {"version":3,"sources":["file:///Users/luulam/Developer/tool/9router/node_modules/next/src/build/templates/edge-wrapper.js"],"sourcesContent":["// The wrapped module could be an async module, we handle that with the proxy\n// here. The comma expression makes sure we don't call the function with the\n// module as the \"this\" arg.\n// Turn exports into functions that are also a thenable. This way you can await the whole object\n// or exports (e.g. for Components) or call them directly as though they are async functions\n// (e.g. edge functions/middleware, this is what the Edge Runtime does).\n// Catch promise to prevent UnhandledPromiseRejectionWarning, this will be propagated through\n// the awaited export(s) anyway.\nself._ENTRIES ||= {}\nconst modProm = import('MODULE')\nmodProm.catch(() => {})\nself._ENTRIES['VAR_ENTRY_NAME'] = new Proxy(modProm, {\n get(innerModProm, name) {\n if (name === 'then') {\n return (res, rej) => innerModProm.then(res, rej)\n }\n let result = (...args) =>\n innerModProm.then((mod) => (0, mod[name])(...args))\n result.then = (res, rej) =>\n innerModProm.then((mod) => mod[name]).then(res, rej)\n return result\n },\n})\n"],"names":["self","_ENTRIES","modProm","catch","Proxy","get","innerModProm","name","res","rej","then","result","args","mod"],"mappings":"AAAA,6EAA6E;AAC7E,4EAA4E;AAC5E,4BAA4B;AAC5B,gGAAgG;AAChG,6FAA6F;AAC7F,wEAAwE;AACxE,6FAA6F;AAC7F,gCAAgC;AAChCA,KAAKC,QAAQ,KAAK,CAAC;AACnB,MAAMC,UAAU,MAAM,CAAC;AACvBA,QAAQC,KAAK,CAAC,KAAO;AACrBH,KAAKC,QAAQ,CAAC,iBAAiB,GAAG,IAAIG,KAAAA,CAAMF,EAAAA,IAAAA,GAAS,GAAA,SAAA;IACnDG,KAAIC,YAAY,EAAEC,IAAI;QACpB,IAAIA,SAAS,QAAQ;YACnB,OAAO,CAACC,KAAKC,MAAQH,aAAaI,IAAI,CAACF,KAAKC;QAC9C;QACA,IAAIE,SAAS,CAAC,GAAGC,OACfN,aAAaI,IAAI,CAAC,CAACG,MAAS,CAAA,GAAGA,GAAG,CAACN,KAAI,KAAMK;QAC/CD,OAAOD,IAAI,GAAG,CAACF,KAAKC,MAClBH,aAAaI,IAAI,CAAC,CAACG,MAAQA,GAAG,CAACN,KAAK,EAAEG,IAAI,CAACF,KAAKC;QAClD,OAAOE;IACT;AACF","ignoreList":[0]}},
{"offset": {"line": 28, "column": 0}, "map": {"version":3,"sources":["file:///Users/luulam/Developer/tool/9router/src/instrumentation.js"],"sourcesContent":["export async function register() {\n if (process.env.NEXT_RUNTIME === \"nodejs\") {\n const { initConsoleLogCapture } = await import(\"@/lib/consoleLogBuffer\");\n initConsoleLogCapture();\n }\n}\n"],"names":["register"],"mappings":";;;;AAAO,eAAeA;IACpB;;AAIF"}}]
}

View File

@@ -0,0 +1,5 @@
var R=require("./chunks/[turbopack]_runtime.js")("server/instrumentation.js")
R.c("server/chunks/src_lib_consoleLogBuffer_1rh8dli.js")
R.c("server/chunks/src_instrumentation_0_51q1v.js")
R.m("[project]/src/instrumentation.js [instrumentation] (ecmascript)")
module.exports=R.m("[project]/src/instrumentation.js [instrumentation] (ecmascript)").exports

View File

@@ -0,0 +1,5 @@
{
"version": 3,
"sources": [],
"sections": []
}

View File

@@ -0,0 +1,12 @@
{
"sorted_middleware": [],
"middleware": {},
"instrumentation": {
"files": [
"server/edge/chunks/_06oe804._.js",
"server/edge/chunks/turbopack-node_modules_next_dist_esm_build_templates_edge-wrapper_0xnf3d4.js"
],
"name": "instrumentation"
},
"functions": {}
}

View File

@@ -0,0 +1 @@
self.__INTERCEPTION_ROUTE_REWRITE_MANIFEST="[]";

View File

@@ -0,0 +1,15 @@
globalThis.__BUILD_MANIFEST = {
"pages": {
"/_app": []
},
"devFiles": [],
"polyfillFiles": [],
"lowPriorityFiles": [
"static/development/_buildManifest.js",
"static/development/_ssgManifest.js",
"static/development/_clientMiddlewareManifest.js"
],
"rootMainFiles": [],
"rootMainFilesTree": {},
"pagesChunkGroupBootstrapParams": {}
};

View File

@@ -0,0 +1,6 @@
{
"version": 3,
"middleware": {},
"sortedMiddleware": [],
"functions": {}
}

View File

@@ -0,0 +1,6 @@
var R=require("./chunks/[turbopack]_runtime.js")("server/middleware.js")
R.c("server/chunks/_0khumf3._.js")
R.c("server/chunks/[root-of-the-server]__18b1j7k._.js")
R.c("server/chunks/node_modules_0j-238y._.js")
R.m("[project]/node_modules/next/dist/esm/build/templates/middleware.js { INNER_MIDDLEWARE_MODULE => \"[project]/src/proxy.js [middleware] (ecmascript)\" } [middleware] (ecmascript)")
module.exports=R.m("[project]/node_modules/next/dist/esm/build/templates/middleware.js { INNER_MIDDLEWARE_MODULE => \"[project]/src/proxy.js [middleware] (ecmascript)\" } [middleware] (ecmascript)").exports

View File

@@ -0,0 +1,5 @@
{
"version": 3,
"sources": [],
"sections": []
}

View File

@@ -0,0 +1,6 @@
{
"sorted_middleware": [],
"middleware": {},
"instrumentation": null,
"functions": {}
}

View File

@@ -0,0 +1 @@
self.__NEXT_FONT_MANIFEST="{\n \"app\": {},\n \"appUsingSizeAdjust\": false,\n \"pages\": {},\n \"pagesUsingSizeAdjust\": false\n}"

View File

@@ -0,0 +1,6 @@
{
"app": {},
"appUsingSizeAdjust": false,
"pages": {},
"pagesUsingSizeAdjust": false
}

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
self.__RSC_SERVER_MANIFEST="{\n \"node\": {},\n \"edge\": {},\n \"encryptionKey\": \"+PzC19pP70GybS3k4K8t6RsdWKuh0Yqhwm2/p8Gxynw=\"\n}"

View File

@@ -0,0 +1,5 @@
{
"node": {},
"edge": {},
"encryptionKey": "+PzC19pP70GybS3k4K8t6RsdWKuh0Yqhwm2/p8Gxynw="
}

View File

@@ -0,0 +1,44 @@
self.__BUILD_MANIFEST = {
"__rewrites": {
"afterFiles": [
{
"source": "/v1/v1/:path*",
"destination": "/api/v1/:path*"
},
{
"source": "/v1/v1",
"destination": "/api/v1"
},
{
"source": "/codex/:path*",
"destination": "/api/v1/responses"
},
{
"source": "/responses",
"destination": "/api/v1/responses"
},
{
"source": "/v1beta/:path*",
"destination": "/api/v1beta/:path*"
},
{
"source": "/v1beta",
"destination": "/api/v1beta"
},
{
"source": "/v1/:path*",
"destination": "/api/v1/:path*"
},
{
"source": "/v1",
"destination": "/api/v1"
}
],
"beforeFiles": [],
"fallback": []
},
"sortedPages": [
"/_app",
"/_error"
]
};self.__BUILD_MANIFEST_CB && self.__BUILD_MANIFEST_CB()

View File

@@ -0,0 +1 @@
self.__MIDDLEWARE_MATCHERS = [];self.__MIDDLEWARE_MATCHERS_CB && self.__MIDDLEWARE_MATCHERS_CB()

View File

@@ -0,0 +1 @@
self.__SSG_MANIFEST=new Set;self.__SSG_MANIFEST_CB&&self.__SSG_MANIFEST_CB()

View File

@@ -0,0 +1,3 @@
[{"name":"hot-reloader","duration":36,"timestamp":112715988344,"id":3,"parentId":1,"tags":{"version":"16.3.1"},"startTime":1787652294570,"traceId":"bd2c8cef21146950"},{"name":"compile-path","duration":66940,"timestamp":112716077567,"id":4,"parentId":1,"tags":{"trigger":"instrumentation Node.js"},"startTime":1787652294659,"traceId":"bd2c8cef21146950"}]
[{"name":"compile-path","duration":15824,"timestamp":112716144673,"id":5,"parentId":1,"tags":{"trigger":"instrumentation Edge"},"startTime":1787652294726,"traceId":"bd2c8cef21146950"}]
[{"name":"compile-path","duration":126781,"timestamp":112716164657,"id":6,"parentId":1,"tags":{"trigger":"proxy"},"startTime":1787652294746,"traceId":"bd2c8cef21146950"}]

55
tmp/9r-smoke-dist/dev/types/routes.d.ts vendored Normal file
View File

@@ -0,0 +1,55 @@
// This file is generated automatically by Next.js
// Do not edit this file manually
type AppRoutes = never
type PageRoutes = never
type LayoutRoutes = never
type RedirectRoutes = never
type RewriteRoutes = never
type Routes = AppRoutes | PageRoutes | LayoutRoutes | RedirectRoutes | RewriteRoutes
interface ParamMap {
}
export type ParamsOf<Route extends Routes> = ParamMap[Route]
interface LayoutSlotMap {
}
export type { AppRoutes, PageRoutes, LayoutRoutes, RedirectRoutes, RewriteRoutes, ParamMap }
declare global {
/**
* Props for Next.js App Router page components
* @example
* ```tsx
* export default function Page(props: PageProps<'/blog/[slug]'>) {
* const { slug } = await props.params
* return <div>Blog post: {slug}</div>
* }
* ```
*/
interface PageProps<AppRoute extends AppRoutes> {
params: Promise<ParamMap[AppRoute]>
searchParams: Promise<Record<string, string | string[] | undefined>>
}
/**
* Props for Next.js App Router layout components
* @example
* ```tsx
* export default function Layout(props: LayoutProps<'/dashboard'>) {
* return <div>{props.children}</div>
* }
* ```
*/
type LayoutProps<LayoutRoute extends LayoutRoutes> = {
params: Promise<ParamMap[LayoutRoute]>
children: React.ReactNode
} & {
[K in LayoutSlotMap[LayoutRoute]]: React.ReactNode
}
}

1
xd:/browser Normal file
View File

@@ -0,0 +1 @@
{"action":"run","name":"combos","code":"async (page) => { await page.setCookie(...require(\"fs\").readFileSync(\"/tmp/9r-cookies.txt\",\"utf8\").split(\"\\n\").filter(l=>!l.startsWith(\"#\")&&l.trim()).map(l=>{const p=l.split(\"\\t\"); return {name:p[5], value:p[6], domain:\"localhost\", path:\"/\"};})); await page.goto(\"http://localhost:20128/dashboard/combos\", {waitUntil:\"networkidle0\"}); return await page.evaluate(()=>document.body.innerText.substring(0,1500)); }","i":"load combos with auth"}