Framework:
- server/settings.py: key/value settings + secrets, env-injected into operator
subprocesses, secret values masked in API and redacted from job logs
- runner: gpu/cpu/net concurrency lanes, job cancel/retry/delete, multi-input,
graceful 'not installed' error when a tool venv is missing
- db: settings table, asset_ids column (migrated), MODELBEAST_DATA test override
- main: settings + job-action endpoints, inbox watch folder auto-ingest
- store: operators can tag output asset kind (splat, colmap_dataset)
Operators (11 total):
- fal_trellis/trellis2/hunyuan3d/rodin via shared _lib/fal_common.py (verified
params + endpoint ids; recursive result-URL extractor handles per-endpoint keys)
- sf3d, trellis_mac: local MPS image-to-3D, installed with Metal kernels built,
gated on owner HuggingFace auth
- colmap_poses (COLMAP 4.x + GLOMAP global mapper), brush_train (native Metal 3DGS)
- Scan pipeline validated end-to-end through the UI: frames -> colmap (48/48
registered, 0.6px) -> brush -> splat.ply -> in-app SplatViewer
Frontend:
- Settings modal, operator gating (lock + disabled run when requires_env unmet),
job cancel/retry/delete, Compare grid (multi-select side-by-side viewers),
SplatViewer (gaussian-splats-3d, Ply format forced for extensionless URLs)
Tooling: scripts/install_{colmap,brush,sf3d,trellis_mac}.sh; vendor/ + venvs/
gitignored; tests/smoke.sh (12 checks passing); BENCHMARKS.md
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
64 lines
2.2 KiB
JavaScript
64 lines
2.2 KiB
JavaScript
import { useEffect, useState } from "react";
|
|
import { getSettings, putSettings } from "./api";
|
|
|
|
const LABELS = {
|
|
fal_key: "fal.ai API key",
|
|
tripo_key: "Tripo API key",
|
|
meshy_key: "Meshy API key",
|
|
replicate_token: "Replicate token",
|
|
hf_token: "HuggingFace token (for SF3D / TRELLIS.2 weights)",
|
|
models_dir: "Models directory",
|
|
archive_host: "Archive host (rsync)",
|
|
archive_path: "Archive path",
|
|
};
|
|
|
|
export default function Settings({ onClose, onSaved }) {
|
|
const [data, setData] = useState(null);
|
|
const [edits, setEdits] = useState({});
|
|
const [saving, setSaving] = useState(false);
|
|
|
|
useEffect(() => { getSettings().then(setData); }, []);
|
|
if (!data) return null;
|
|
|
|
const secretKeys = new Set(data._secret_keys || []);
|
|
const keys = data._env_keys || [];
|
|
|
|
const save = async () => {
|
|
setSaving(true);
|
|
const fresh = await putSettings(edits);
|
|
setData(fresh); setEdits({}); setSaving(false);
|
|
onSaved?.(fresh);
|
|
};
|
|
|
|
return (
|
|
<div className="modal-backdrop" onClick={onClose}>
|
|
<div className="modal" onClick={(e) => e.stopPropagation()}>
|
|
<h2>Settings</h2>
|
|
<p className="dim">API keys unlock cloud operators. HuggingFace token lets local SF3D / TRELLIS.2 download their gated weights. Secrets are masked and never written to logs.</p>
|
|
{keys.map((k) => {
|
|
const isSecret = secretKeys.has(k);
|
|
const current = data[k] || "";
|
|
const placeholder = isSecret && current ? "•••••••• (set — leave blank to keep)" : "";
|
|
return (
|
|
<label key={k} className="setting">
|
|
<span>{LABELS[k] || k}</span>
|
|
<input
|
|
type={isSecret ? "password" : "text"}
|
|
placeholder={placeholder}
|
|
defaultValue={isSecret ? "" : current}
|
|
onChange={(e) => setEdits({ ...edits, [k]: e.target.value })}
|
|
/>
|
|
</label>
|
|
);
|
|
})}
|
|
<div className="modal-actions">
|
|
<button onClick={onClose}>Close</button>
|
|
<button className="go" onClick={save} disabled={saving || !Object.keys(edits).length}>
|
|
{saving ? "Saving…" : "Save"}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|