omnius 1.0.882 → 1.0.883
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +2 -2
- package/dist/library.js +3482 -1918
- package/dist/omni-setup-worker.js +49 -25
- package/dist/tui/native/worker.js +87 -16
- package/dist/update-worker-runtime.js +369 -165
- package/dist/update-worker.js +4 -4
- package/npm-shrinkwrap.json +5 -5
- package/package.json +1 -1
|
@@ -246907,12 +246907,16 @@ async function hostFacts(d) {
|
|
|
246907
246907
|
{},
|
|
246908
246908
|
false
|
|
246909
246909
|
).catch(() => ({ code: -1, stdout: "", stderr: "" }));
|
|
246910
|
-
const discovery =
|
|
246911
|
-
|
|
246910
|
+
const discovery = Buffer.byteLength(discovered.stdout) <= 1024 * 1024 ? json(discovered.stdout) : void 0;
|
|
246911
|
+
let installationAbsent = false;
|
|
246912
|
+
const installedBytes = await d.readHostFile("/usr/local/share/ollama-unify/gpu-negotiator.json").catch((error2) => {
|
|
246913
|
+
installationAbsent = error2.code === "ENOENT";
|
|
246914
|
+
return Buffer.alloc(0);
|
|
246915
|
+
});
|
|
246912
246916
|
const installed = installedBytes.length <= 1024 * 1024 ? json(installedBytes.toString()) : void 0;
|
|
246913
|
-
const brokerDetected = discovered.code === 0 ||
|
|
246914
|
-
const broker = discovery?.protocol === BROKER_PROTOCOL && discovery.available === true && typeof discovery.control_socket === "string" && discovery.control_socket.startsWith("/");
|
|
246915
|
-
const ollamaHost = broker ? loopbackOllama(discovery?.public_ollama_api) ?? (installed?.protocol === BROKER_PROTOCOL ? loopbackOllama(installed.public_ollama_api) : void 0) : !brokerDetected
|
|
246917
|
+
const brokerDetected = discovered.code === 0 || discovery?.protocol === BROKER_PROTOCOL || !installationAbsent;
|
|
246918
|
+
const broker = discovered.code === 0 && discovery?.protocol === BROKER_PROTOCOL && discovery.available === true && typeof discovery.control_socket === "string" && discovery.control_socket.startsWith("/");
|
|
246919
|
+
const ollamaHost = broker ? loopbackOllama(discovery?.public_ollama_api) ?? (installed?.protocol === BROKER_PROTOCOL ? loopbackOllama(installed.public_ollama_api) : void 0) : !brokerDetected ? "http://127.0.0.1:11434" : void 0;
|
|
246916
246920
|
const warnings = Array.isArray(discovery?.warnings) ? discovery.warnings.slice(0, 12).map((warning) => safeSummary(warning)) : [];
|
|
246917
246921
|
const activeLeases = Array.isArray(discovery?.active_leases) ? discovery.active_leases : [];
|
|
246918
246922
|
const summaries = activeLeases.slice(0, 12).map(
|
|
@@ -246947,7 +246951,7 @@ async function hostFacts(d) {
|
|
|
246947
246951
|
);
|
|
246948
246952
|
const capable = Number.isFinite(Number(memory)) && Number(memory) >= OMNI_RUNTIME_RESERVATIONS_MIB.ornith15 && Number.isFinite(Number(compute)) && Number(compute) >= 7;
|
|
246949
246953
|
const eligible = Boolean(
|
|
246950
|
-
|
|
246954
|
+
capable && !held && (!brokerDetected && !tegra || policyReady && selected && !quarantined && !missing && !transitioning) && ollamaHost
|
|
246951
246955
|
);
|
|
246952
246956
|
devices.push({
|
|
246953
246957
|
uuid,
|
|
@@ -246955,7 +246959,7 @@ async function hostFacts(d) {
|
|
|
246955
246959
|
eligible,
|
|
246956
246960
|
...Number.isFinite(Number(memory)) ? { memoryMiB: Number(memory) } : {},
|
|
246957
246961
|
...!eligible ? {
|
|
246958
|
-
reason: held ? "Operator inference hold is active." : !broker ? "GPU broker discovery is unavailable or invalid." : !policyReady ? "GPU broker health or admission metadata does not permit new work." : !selected || missing ? "GPU is outside the healthy broker-selected scope." : quarantined ? "GPU has quarantined unregistered work." : transitioning ? "GPU has a pending or revoking lease." : !capable ? "Requires compute capability 7.0+ and at least 24,000 MiB VRAM; Qwen requires 45,000 MiB." : "A verified loopback Ollama service endpoint is required."
|
|
246962
|
+
reason: !brokerDetected && tegra ? "Integrated Tegra deployments require the supported SoC admission path." : held ? "Operator inference hold is active." : brokerDetected && !broker ? "GPU broker discovery is unavailable or invalid." : brokerDetected && !policyReady ? "GPU broker health or admission metadata does not permit new work." : brokerDetected && (!selected || missing) ? "GPU is outside the healthy broker-selected scope." : brokerDetected && quarantined ? "GPU has quarantined unregistered work." : brokerDetected && transitioning ? "GPU has a pending or revoking lease." : !capable ? "Requires compute capability 7.0+ and at least 24,000 MiB VRAM; Qwen requires 45,000 MiB." : "A verified loopback Ollama service endpoint is required."
|
|
246959
246963
|
} : {}
|
|
246960
246964
|
});
|
|
246961
246965
|
}
|
|
@@ -246991,6 +246995,7 @@ async function inspectOmniHost(input, options = {}) {
|
|
|
246991
246995
|
`Derived speech GGUF cache: ${join13(p.runtime, "components")} (additional local disk usage, retained on cancellation).`,
|
|
246992
246996
|
"GPU reservation: Ornith 24,000 MiB; Qwen 45,000 MiB. Download size is not a VRAM promise; Tegra uses upstream UMA admission.",
|
|
246993
246997
|
`Verified local Ollama endpoint: ${facts.ollamaHost ?? "unavailable"}`,
|
|
246998
|
+
facts.brokerDetected ? "GPU admission uses the installed broker; invalid discovery, holds and lease transitions block setup." : "No GPU broker installation was found. The upstream direct supervisor requires the chosen accelerator, current capacity and actual owned-runner residency proof; it does not coordinate external GPU workloads.",
|
|
246994
246999
|
`Private operation diagnostics: ${join13(p.root, "setup-<operationId>.log")} (bounded, owner-only).`,
|
|
246995
247000
|
...facts.warnings.map((warning) => `Broker warning: ${warning}`),
|
|
246996
247001
|
...facts.summaries
|
|
@@ -247008,7 +247013,8 @@ async function inspectOmniHost(input, options = {}) {
|
|
|
247008
247013
|
"ffmpeg",
|
|
247009
247014
|
"ollama",
|
|
247010
247015
|
"systemctl",
|
|
247011
|
-
|
|
247016
|
+
...!facts.tegra || facts.broker ? ["nvidia-smi"] : [],
|
|
247017
|
+
...facts.broker ? ["docker", "jq", "curl", "openssl", "setsid", "ss"] : []
|
|
247012
247018
|
]) {
|
|
247013
247019
|
const found = await command(
|
|
247014
247020
|
d,
|
|
@@ -247032,10 +247038,6 @@ async function inspectOmniHost(input, options = {}) {
|
|
|
247032
247038
|
).catch(() => ({ code: -1, stdout: "", stderr: "" }));
|
|
247033
247039
|
if (!["running", "degraded"].includes(userBus.stdout.trim()))
|
|
247034
247040
|
blockedReasons.push("A working systemd user manager is required.");
|
|
247035
|
-
if (!facts.brokerDetected && !facts.tegra)
|
|
247036
|
-
blockedReasons.push(
|
|
247037
|
-
"Discrete CUDA deployment requires the ollama-unify GPU broker; direct or CPU fallback is disabled."
|
|
247038
|
-
);
|
|
247039
247041
|
if (facts.brokerDetected && !facts.broker)
|
|
247040
247042
|
blockedReasons.push(
|
|
247041
247043
|
"Installed GPU broker discovery is unavailable or invalid; direct fallback is disabled."
|
|
@@ -247243,7 +247245,7 @@ async function proof(d, p, model, gpuUuid, direct, startedAt, signal) {
|
|
|
247243
247245
|
const state = json(
|
|
247244
247246
|
(await optionalFile(join13(p.runtime, "state", "daemon-status.json")))?.toString() ?? ""
|
|
247245
247247
|
);
|
|
247246
|
-
if (state?.schema !== "robit.qwen-omni-daemon.status.v1" || state.state !== "ready" || state.model !== model || state.pid !== status.pid || state.updated_at * 1e3 < startedAt || state.startup_smoke !== false)
|
|
247248
|
+
if (state?.schema !== "robit.qwen-omni-daemon.status.v1" || state.state !== "ready" || state.model !== model || state.pid !== status.pid || !Number.isFinite(state.updated_at) || state.updated_at * 1e3 < startedAt || state.startup_smoke !== false)
|
|
247247
247249
|
return false;
|
|
247248
247250
|
const child = Array.isArray(state.children) ? state.children.find((item) => item.name === "comprehension") : void 0;
|
|
247249
247251
|
if (!child || !Number.isSafeInteger(child.pid) || !pids.includes(child.pid))
|
|
@@ -247253,14 +247255,17 @@ async function proof(d, p, model, gpuUuid, direct, startedAt, signal) {
|
|
|
247253
247255
|
p.python,
|
|
247254
247256
|
[
|
|
247255
247257
|
"-c",
|
|
247256
|
-
"import json,sys; from qwen_omni_adapters.accelerator import process_is_gpu_resident,residency_backend; print(json.dumps({'resident':process_is_gpu_resident(int(sys.argv[1])),'backend':residency_backend()}))",
|
|
247257
|
-
String(child.pid)
|
|
247258
|
+
"import json,sys; from qwen_omni_adapters.accelerator import process_is_gpu_resident,residency_backend; print(json.dumps({'resident':process_is_gpu_resident(int(sys.argv[1]),sys.argv[2]),'backend':residency_backend()}))",
|
|
247259
|
+
String(child.pid),
|
|
247260
|
+
gpuUuid === "tegra-integrated" ? "" : gpuUuid
|
|
247258
247261
|
],
|
|
247259
247262
|
{ signal, timeoutMs: 5e3 },
|
|
247260
247263
|
false
|
|
247261
247264
|
);
|
|
247262
247265
|
const resident = json(result.stdout);
|
|
247263
|
-
|
|
247266
|
+
if (result.code !== 0 || resident?.resident !== true) return false;
|
|
247267
|
+
if (gpuUuid === "tegra-integrated") return resident.backend === "tegra-device-handles";
|
|
247268
|
+
return resident.backend === "nvidia-smi-compute-apps" && await discreteResidency(d, p, pids, gpuUuid, signal, child.pid);
|
|
247264
247269
|
}
|
|
247265
247270
|
const ready = await command(
|
|
247266
247271
|
d,
|
|
@@ -247297,6 +247302,9 @@ async function proof(d, p, model, gpuUuid, direct, startedAt, signal) {
|
|
|
247297
247302
|
(lease) => lease.owner === p.ownerName && lease.state === "active" && Array.isArray(lease.gpu_uuids) && lease.gpu_uuids.length === 1 && lease.gpu_uuids[0] === gpuUuid
|
|
247298
247303
|
))
|
|
247299
247304
|
return false;
|
|
247305
|
+
return discreteResidency(d, p, pids, gpuUuid, signal);
|
|
247306
|
+
}
|
|
247307
|
+
async function discreteResidency(d, p, pids, gpuUuid, signal, requiredPid) {
|
|
247300
247308
|
const processes = await command(
|
|
247301
247309
|
d,
|
|
247302
247310
|
"nvidia-smi",
|
|
@@ -247318,7 +247326,7 @@ async function proof(d, p, model, gpuUuid, direct, startedAt, signal) {
|
|
|
247318
247326
|
"Owned CUDA work was observed outside the designated accelerator."
|
|
247319
247327
|
);
|
|
247320
247328
|
const cmd = (await d.readHostFile(`/proc/${pid}/cmdline`)).toString().split("\0"), env = (await d.readHostFile(`/proc/${pid}/environ`)).toString().split("\0");
|
|
247321
|
-
if (cmd[0] === join13(
|
|
247329
|
+
if ((requiredPid === void 0 || requiredPid === pid) && cmd[0] === join13(
|
|
247322
247330
|
p.checkout,
|
|
247323
247331
|
"vendor",
|
|
247324
247332
|
"llama.cpp",
|
|
@@ -247330,6 +247338,13 @@ async function proof(d, p, model, gpuUuid, direct, startedAt, signal) {
|
|
|
247330
247338
|
}
|
|
247331
247339
|
return seen;
|
|
247332
247340
|
}
|
|
247341
|
+
async function admitDirectDiscrete(d, gpuUuid, requiredMiB, signal) {
|
|
247342
|
+
const result = await command(d, "nvidia-smi", ["--query-gpu=uuid,memory.free", "--format=csv,noheader,nounits"], { signal, timeoutMs: 5e3 }, false);
|
|
247343
|
+
const rows = result.stdout.split("\n").map((line) => line.split(",").map((value) => value.trim())).filter((row) => row[0] === gpuUuid);
|
|
247344
|
+
const freeMiB = rows.length === 1 && rows[0].length === 2 ? Number(rows[0][1]) : NaN;
|
|
247345
|
+
if (result.code !== 0 || !Number.isFinite(freeMiB) || freeMiB < requiredMiB)
|
|
247346
|
+
fail("accelerator_not_admitted", "The chosen GPU does not currently report sufficient free VRAM for the selected profile. No service was started; no external workload was stopped.");
|
|
247347
|
+
}
|
|
247333
247348
|
async function verifySource(d, p, signal) {
|
|
247334
247349
|
const revision = await command(
|
|
247335
247350
|
d,
|
|
@@ -247571,7 +247586,8 @@ ${diagnostic.stderr.slice(-65536)}` : ""}`,
|
|
|
247571
247586
|
"An existing healthy Omni stack must be attached explicitly; it will not be replaced."
|
|
247572
247587
|
);
|
|
247573
247588
|
const facts = await hostFacts(d);
|
|
247574
|
-
direct =
|
|
247589
|
+
direct = !facts.brokerDetected;
|
|
247590
|
+
const directTegra = direct && facts.tegra;
|
|
247575
247591
|
if (facts.held)
|
|
247576
247592
|
return {
|
|
247577
247593
|
state: "held-paused",
|
|
@@ -247586,7 +247602,7 @@ ${diagnostic.stderr.slice(-65536)}` : ""}`,
|
|
|
247586
247602
|
"accelerator_required",
|
|
247587
247603
|
"Choose an exact eligible accelerator from the current plan."
|
|
247588
247604
|
);
|
|
247589
|
-
if (
|
|
247605
|
+
if (directTegra && input.gpuUuid !== "tegra-integrated" || !directTegra && !GPU_UUID.test(input.gpuUuid))
|
|
247590
247606
|
fail(
|
|
247591
247607
|
"accelerator_required",
|
|
247592
247608
|
"The selected accelerator does not match this host's deployment mode."
|
|
@@ -247594,7 +247610,7 @@ ${diagnostic.stderr.slice(-65536)}` : ""}`,
|
|
|
247594
247610
|
const selectedDevice = facts.devices.find(
|
|
247595
247611
|
(device) => device.uuid === input.gpuUuid
|
|
247596
247612
|
);
|
|
247597
|
-
if (!
|
|
247613
|
+
if (!directTegra && (selectedDevice.memoryMiB ?? 0) < OMNI_RUNTIME_RESERVATIONS_MIB[input.profile])
|
|
247598
247614
|
fail(
|
|
247599
247615
|
"insufficient_vram",
|
|
247600
247616
|
`The selected profile requires at least ${OMNI_RUNTIME_RESERVATIONS_MIB[input.profile].toLocaleString("en-US")} MiB total GPU memory before any build or model download.`
|
|
@@ -247747,7 +247763,7 @@ ${diagnostic.stderr.slice(-65536)}` : ""}`,
|
|
|
247747
247763
|
OMNI_LANGUAGE_MODEL: profile.model,
|
|
247748
247764
|
OMNI_PORTAL_RUNTIME_ROOT: p.runtime,
|
|
247749
247765
|
OMNI_COMPREHENSION_GPU_UUID: input.gpuUuid,
|
|
247750
|
-
CUDA_VISIBLE_DEVICES:
|
|
247766
|
+
CUDA_VISIBLE_DEVICES: directTegra ? "0" : input.gpuUuid,
|
|
247751
247767
|
HIP_VISIBLE_DEVICES: "-1",
|
|
247752
247768
|
ROCR_VISIBLE_DEVICES: "-1",
|
|
247753
247769
|
OMNI_ENABLE_POINTING: "0",
|
|
@@ -247756,7 +247772,7 @@ ${diagnostic.stderr.slice(-65536)}` : ""}`,
|
|
|
247756
247772
|
OMNI_STARTUP_SMOKE: "0",
|
|
247757
247773
|
OMNI_PRINT_ACCESS_URL: "0",
|
|
247758
247774
|
OMNI_KEEP_CACHE: "1",
|
|
247759
|
-
OMNI_COMPREHENSION_CONTEXT_TOKENS:
|
|
247775
|
+
OMNI_COMPREHENSION_CONTEXT_TOKENS: directTegra && input.profile === "qwen38" ? "16384" : "65536",
|
|
247760
247776
|
OMNI_COMPREHENSION_VRAM_MIB: String(
|
|
247761
247777
|
OMNI_RUNTIME_RESERVATIONS_MIB[input.profile]
|
|
247762
247778
|
),
|
|
@@ -247797,13 +247813,13 @@ ${diagnostic.stderr.slice(-65536)}` : ""}`,
|
|
|
247797
247813
|
message: "Runtime source and private configuration are installed; operator pause is preserved and no readiness is claimed."
|
|
247798
247814
|
};
|
|
247799
247815
|
if (fresh.broker !== facts.broker || fresh.brokerDetected !== facts.brokerDetected || fresh.ollamaHost !== facts.ollamaHost || !fresh.devices.some(
|
|
247800
|
-
(device) => device.uuid === input.gpuUuid && device.eligible && (
|
|
247816
|
+
(device) => device.uuid === input.gpuUuid && device.eligible && (directTegra || (device.memoryMiB ?? 0) >= OMNI_RUNTIME_RESERVATIONS_MIB[input.profile])
|
|
247801
247817
|
))
|
|
247802
247818
|
fail(
|
|
247803
247819
|
"accelerator_policy_changed",
|
|
247804
247820
|
"Accelerator admission changed before service activation."
|
|
247805
247821
|
);
|
|
247806
|
-
if (
|
|
247822
|
+
if (directTegra) {
|
|
247807
247823
|
const admission = await command(
|
|
247808
247824
|
d,
|
|
247809
247825
|
p.python,
|
|
@@ -247821,7 +247837,7 @@ ${diagnostic.stderr.slice(-65536)}` : ""}`,
|
|
|
247821
247837
|
"accelerator_not_admitted",
|
|
247822
247838
|
"Tegra memory and utilization admission did not pass."
|
|
247823
247839
|
);
|
|
247824
|
-
}
|
|
247840
|
+
} else if (direct) await admitDirectDiscrete(d, input.gpuUuid, OMNI_RUNTIME_RESERVATIONS_MIB[input.profile], options.signal);
|
|
247825
247841
|
linked = true;
|
|
247826
247842
|
await command(d, "systemctl", ["--user", "link", p.unitPath], {
|
|
247827
247843
|
signal: options.signal
|
|
@@ -247859,6 +247875,7 @@ ${diagnostic.stderr.slice(-65536)}` : ""}`,
|
|
|
247859
247875
|
adapterUrl: "http://127.0.0.1:8910",
|
|
247860
247876
|
profile: input.profile,
|
|
247861
247877
|
sourceRevision: OMNI_SETUP_SOURCE.revision,
|
|
247878
|
+
accelerator: { mode: direct ? directTegra ? "direct-tegra" : "direct-discrete" : "broker", gpuUuid: input.gpuUuid },
|
|
247862
247879
|
verified: {
|
|
247863
247880
|
adapterContract: true,
|
|
247864
247881
|
runtime: true,
|
|
@@ -300707,8 +300724,14 @@ function stored(root, id2) {
|
|
|
300707
300724
|
if (!value) throw error("setup_job_missing", "Omni setup job was not found.", 404);
|
|
300708
300725
|
if (value.version !== 1 || value.job?.id !== id2 || value.job.schema !== "omnius.omni-setup-job.v1" || !states.has(value.job.state) || !phases.has(value.job.phase) || !ID2.test(value.owner?.token ?? "") || !/^\d+$/.test(value.owner.start) || !Number.isInteger(value.owner.pid) || value.owner.pid <= 0 || !/^[0-9a-f]{64}$/.test(value.configurationRevision) || typeof value.gpuUuid !== "string" || !Number.isFinite(Date.parse(value.job.createdAt)) || !Number.isFinite(Date.parse(value.job.updatedAt)) || value.job.sourceRevision !== OMNI_SETUP_SOURCE.revision || !OMNI_SETUP_PROFILES.some((p) => p.id === value.job.profile && p.model === value.job.model)) throw error("invalid_setup_state", "Omni setup state has an invalid identity.");
|
|
300709
300726
|
if (value.job.state === "ready" && (!value.job.receipt || value.job.receipt.model !== value.job.model || value.job.receipt.sourceRevision !== OMNI_SETUP_SOURCE.revision || value.job.receipt.action !== "deployed" || !value.job.receipt.verified?.adapterContract || !value.job.receipt.verified.runtime || !value.job.receipt.verified.accelerator)) throw error("invalid_setup_state", "Omni setup state is missing its readiness proof.");
|
|
300727
|
+
if (value.job.receipt?.accelerator && !publicAccelerator(value.job.receipt.accelerator, value.gpuUuid)) throw error("invalid_setup_state", "Omni setup accelerator proof does not match the selected device.");
|
|
300710
300728
|
return value;
|
|
300711
300729
|
}
|
|
300730
|
+
function publicAccelerator(value, selected) {
|
|
300731
|
+
if (!value || value.gpuUuid !== selected || !["broker", "direct-discrete", "direct-tegra"].includes(value.mode)) return;
|
|
300732
|
+
if (value.mode === "direct-tegra" ? selected !== "tegra-integrated" : !/^GPU-[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/i.test(selected)) return;
|
|
300733
|
+
return { mode: value.mode, gpuUuid: selected };
|
|
300734
|
+
}
|
|
300712
300735
|
async function runOmniSetupJob(rootPath, id2) {
|
|
300713
300736
|
const root = resolve11(rootPath), path3 = join14(root, "jobs", id2 + ".json");
|
|
300714
300737
|
directory(root);
|
|
@@ -300787,6 +300810,7 @@ async function runOmniSetupJob(rootPath, id2) {
|
|
|
300787
300810
|
throw new DOMException("Setup cancelled before commit", "AbortError");
|
|
300788
300811
|
}
|
|
300789
300812
|
if (receipt.model !== value.job.model || receipt.sourceRevision !== OMNI_SETUP_SOURCE.revision || receipt.action !== "deployed" || !receipt.verified.adapterContract || !receipt.verified.runtime || !receipt.verified.accelerator) throw error("setup_readiness_unverified", "Setup did not return complete runtime and accelerator proof.");
|
|
300813
|
+
if (receipt.accelerator && !publicAccelerator(receipt.accelerator, value.gpuUuid)) throw error("setup_readiness_unverified", "Setup accelerator proof does not match the selected device.");
|
|
300790
300814
|
const previous = readSavedOmniConfiguration(config.path);
|
|
300791
300815
|
const revision = saveOmniConfiguration({ version: 1, model: receipt.model, adapterUrl: receipt.adapterUrl }, value.configurationRevision, config.path);
|
|
300792
300816
|
try {
|
|
@@ -248,13 +248,13 @@ import { spawn, execFile } from "node:child_process";
|
|
|
248
248
|
import { realpath, lstat } from "node:fs/promises";
|
|
249
249
|
import { extname, relative, resolve } from "node:path";
|
|
250
250
|
import { Audio } from "@opentui/core";
|
|
251
|
-
async function validateMediaFile(path, root) {
|
|
251
|
+
async function validateMediaFile(path, root, allowSvg = false) {
|
|
252
252
|
const info = await lstat(path);
|
|
253
253
|
if (!info.isFile() || info.isSymbolicLink()) throw new Error("Media must be a regular local file");
|
|
254
254
|
const canonical = await realpath(path), project = await realpath(root);
|
|
255
255
|
const local = relative(project, canonical);
|
|
256
256
|
if (local === ".." || local.startsWith("../") || resolve(project, local) !== canonical) throw new Error("Media is outside its project");
|
|
257
|
-
if (!/\.(png|jpe?g|webp|gif|mp4|webm|mov|mkv|wav|mp3|flac|ogg|opus|m4a|aiff?)$/i.test(extname(canonical))) throw new Error("Unsupported media file");
|
|
257
|
+
if (!/\.(png|jpe?g|webp|gif|mp4|webm|mov|mkv|wav|mp3|flac|ogg|opus|m4a|aiff?)$/i.test(extname(canonical)) && !(allowSvg && extname(canonical).toLowerCase() === ".svg")) throw new Error("Unsupported media file");
|
|
258
258
|
return canonical;
|
|
259
259
|
}
|
|
260
260
|
async function probeMedia(spec, signal) {
|
|
@@ -585,7 +585,7 @@ var NativeMediaView = class extends BoxRenderable {
|
|
|
585
585
|
}
|
|
586
586
|
if (this.image) {
|
|
587
587
|
this.image.visible = spec.kind === "image" || spec.kind === "video";
|
|
588
|
-
const source = spec.kind === "image" ? spec.path : spec.previewPath;
|
|
588
|
+
const source = spec.kind === "image" ? spec.previewPath ?? spec.path : spec.previewPath;
|
|
589
589
|
if (source && this.image.source !== source && !this.playback.playing) {
|
|
590
590
|
const epoch = this.sourceEpoch;
|
|
591
591
|
void validateMediaFile(source, spec.projectRoot).then((path) => {
|
|
@@ -703,7 +703,7 @@ var NativeMediaView = class extends BoxRenderable {
|
|
|
703
703
|
}
|
|
704
704
|
async open() {
|
|
705
705
|
try {
|
|
706
|
-
const path = await validateMediaFile(this.spec.path, this.spec.projectRoot);
|
|
706
|
+
const path = await validateMediaFile(this.spec.path, this.spec.projectRoot, this.spec.kind === "image" && !!this.spec.previewPath);
|
|
707
707
|
if (this.isDestroyed) return;
|
|
708
708
|
this.sendEvent({ type: "action", action: "media-open", id: this.mediaId, text: path });
|
|
709
709
|
if (process.env["OMNIUS_MEDIA_TEST_NO_OPEN"] === "1") {
|
|
@@ -744,7 +744,7 @@ var NativeMediaView = class extends BoxRenderable {
|
|
|
744
744
|
// packages/cli/src/tui/native/components/model3d.ts
|
|
745
745
|
import { constants } from "node:fs";
|
|
746
746
|
import { open } from "node:fs/promises";
|
|
747
|
-
import { BoxRenderable as BoxRenderable2, FrameBufferRenderable, OptimizedBuffer, TextRenderable as TextRenderable2 } from "@opentui/core";
|
|
747
|
+
import { BoxRenderable as BoxRenderable2, FrameBufferRenderable, OptimizedBuffer, RGBA as RGBA2, TextRenderable as TextRenderable2 } from "@opentui/core";
|
|
748
748
|
|
|
749
749
|
// packages/cli/src/tui/native/model3d.ts
|
|
750
750
|
var MODEL3D_MAX_BYTES = 16 * 1024 * 1024;
|
|
@@ -1008,7 +1008,7 @@ async function createEngine(renderer2, width, height, background2) {
|
|
|
1008
1008
|
if (!process.versions["bun"] || !(process.platform === "linux" && process.arch === "x64" || process.platform === "darwin" || process.platform === "win32" && process.arch === "x64")) throw new Error("WebGPU is not packaged for this runtime.");
|
|
1009
1009
|
const packageName = "@opentui/three";
|
|
1010
1010
|
const { ThreeCliRenderer, SuperSampleType } = await import(packageName);
|
|
1011
|
-
return new ThreeCliRenderer(renderer2, { width, height, autoResize: false, superSample: SuperSampleType.
|
|
1011
|
+
return new ThreeCliRenderer(renderer2, { width, height, autoResize: false, superSample: SuperSampleType.CPU, alpha: true, backgroundColor: RGBA2.fromValues(background2.r, background2.g, background2.b, 0) });
|
|
1012
1012
|
}
|
|
1013
1013
|
async function readCapture(path) {
|
|
1014
1014
|
const file = await open(path, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
|
|
@@ -1032,7 +1032,7 @@ async function readCapture(path) {
|
|
|
1032
1032
|
}
|
|
1033
1033
|
var NativeModel3DView = class extends BoxRenderable2 {
|
|
1034
1034
|
constructor(renderer2, modelId, spec, colors, options = {}) {
|
|
1035
|
-
super(renderer2, { id: `model3d:${modelId}`, width: "100%", flexDirection: "column", flexShrink: 0 });
|
|
1035
|
+
super(renderer2, { id: `model3d:${modelId}`, width: "100%", flexDirection: "column", flexShrink: 0, shouldFill: false });
|
|
1036
1036
|
this.renderer = renderer2;
|
|
1037
1037
|
this.modelId = modelId;
|
|
1038
1038
|
this.colors = colors;
|
|
@@ -1080,7 +1080,19 @@ var NativeModel3DView = class extends BoxRenderable2 {
|
|
|
1080
1080
|
};
|
|
1081
1081
|
this.cpu = new TextRenderable2(renderer2, { id: `model3d-cpu:${modelId}`, width: "100%", height: 12, flexShrink: 0, wrapMode: "none", content: "", fg: colors.accent, selectable: false, ...pointer });
|
|
1082
1082
|
this.cpu.focusable = true;
|
|
1083
|
-
this.surface = new FrameBufferRenderable(renderer2, {
|
|
1083
|
+
this.surface = new FrameBufferRenderable(renderer2, {
|
|
1084
|
+
id: `model3d-surface:${modelId}`,
|
|
1085
|
+
width: 1,
|
|
1086
|
+
height: 12,
|
|
1087
|
+
flexShrink: 0,
|
|
1088
|
+
visible: false,
|
|
1089
|
+
respectAlpha: true,
|
|
1090
|
+
onSizeChange: () => {
|
|
1091
|
+
this.dirty = true;
|
|
1092
|
+
if (!this.closing) this.renderer.requestRender();
|
|
1093
|
+
},
|
|
1094
|
+
...pointer
|
|
1095
|
+
});
|
|
1084
1096
|
this.surface.focusable = true;
|
|
1085
1097
|
this.controls = new BoxRenderable2(renderer2, { id: `model3d-controls:${modelId}`, width: "100%", flexDirection: "row", flexWrap: "wrap", gap: 2, flexShrink: 0 });
|
|
1086
1098
|
for (const [id, label3, action] of [
|
|
@@ -1140,6 +1152,8 @@ var NativeModel3DView = class extends BoxRenderable2 {
|
|
|
1140
1152
|
loading;
|
|
1141
1153
|
drawing;
|
|
1142
1154
|
cleanup;
|
|
1155
|
+
/** Bounded local diagnostic; a failed preview cleanup must not kill its TUI. */
|
|
1156
|
+
cleanupDiagnostic;
|
|
1143
1157
|
resolveClosed;
|
|
1144
1158
|
closed;
|
|
1145
1159
|
closing = false;
|
|
@@ -1268,11 +1282,14 @@ ${model.warnings.join(" ")}` : ""}`;
|
|
|
1268
1282
|
this.camera.updateProjectionMatrix();
|
|
1269
1283
|
this.status.content = `WebGPU · ${stats}${model.warnings.length ? `
|
|
1270
1284
|
${model.warnings.join(" ")}` : ""}`;
|
|
1271
|
-
const staging = OptimizedBuffer.create(width, height, this.renderer.widthMethod, { owner: this.renderer.nativeScene, id: `model3d-stage:${this.modelId}` });
|
|
1285
|
+
const staging = OptimizedBuffer.create(width, height, this.renderer.widthMethod, { owner: this.renderer.nativeScene, respectAlpha: true, id: `model3d-stage:${this.modelId}` });
|
|
1286
|
+
staging.clear(RGBA2.fromValues(0, 0, 0, 0));
|
|
1272
1287
|
this.engine.setSize(width, height);
|
|
1273
1288
|
this.drawing = this.engine.drawScene(this.scene, staging, 0).then(() => {
|
|
1274
|
-
if (!this.closing && !this.surface.isDestroyed && width === this.pixelWidth && height === this.pixelHeight)
|
|
1275
|
-
|
|
1289
|
+
if (!this.closing && !this.surface.isDestroyed && width === this.pixelWidth && height === this.pixelHeight) {
|
|
1290
|
+
this.surface.frameBuffer.clear(RGBA2.fromValues(0, 0, 0, 0));
|
|
1291
|
+
this.surface.frameBuffer.drawFrameBuffer(0, 0, staging);
|
|
1292
|
+
} else if (!this.closing) this.dirty = true;
|
|
1276
1293
|
}).catch(() => {
|
|
1277
1294
|
if (!this.closing) {
|
|
1278
1295
|
this.releaseScene();
|
|
@@ -1356,7 +1373,9 @@ ${model.warnings.join(" ")}` : ""}`;
|
|
|
1356
1373
|
}
|
|
1357
1374
|
}
|
|
1358
1375
|
}
|
|
1359
|
-
})()
|
|
1376
|
+
})().catch((error) => {
|
|
1377
|
+
this.cleanupDiagnostic = plainText(error instanceof Error ? error.message : "3D preview cleanup failed").slice(0, 200);
|
|
1378
|
+
});
|
|
1360
1379
|
return this.cleanup;
|
|
1361
1380
|
}
|
|
1362
1381
|
destroySelf() {
|
|
@@ -1933,13 +1952,17 @@ var NativeBlockView = class {
|
|
|
1933
1952
|
applyTheme() {
|
|
1934
1953
|
const colors = this.context.colors;
|
|
1935
1954
|
this.root.backgroundColor = colors.background;
|
|
1955
|
+
this.root.shouldFill = this.spec.kind !== "model3d";
|
|
1936
1956
|
if (this.spec.kind === "card") this.root.borderColor = colors.border;
|
|
1937
1957
|
else this.root.border = false;
|
|
1938
1958
|
if (this.title) {
|
|
1939
1959
|
this.title.fg = colors.foreground;
|
|
1940
1960
|
this.title.bg = colors.background;
|
|
1941
1961
|
}
|
|
1942
|
-
if (this.body)
|
|
1962
|
+
if (this.body) {
|
|
1963
|
+
this.body.backgroundColor = colors.background;
|
|
1964
|
+
this.body.shouldFill = this.spec.kind !== "model3d";
|
|
1965
|
+
}
|
|
1943
1966
|
if (this.text) {
|
|
1944
1967
|
this.text.fg = colors.foreground;
|
|
1945
1968
|
this.text.bg = colors.background;
|
|
@@ -2575,6 +2598,7 @@ var NativeModalView = class {
|
|
|
2575
2598
|
this.hints.maxHeight = 2;
|
|
2576
2599
|
this.actions = new BoxRenderable5(renderer2, { id: `modal-actions:${spec.id}`, flexDirection: "row", flexWrap: "wrap", flexShrink: 0, gap: 1, width: "100%", marginTop: 0 });
|
|
2577
2600
|
this.panel.add(this.actions);
|
|
2601
|
+
this.panel.renderBefore = this.paintModelChrome;
|
|
2578
2602
|
this.update(spec);
|
|
2579
2603
|
renderer2.on(CliRenderEvents3.RESIZE, this.fit);
|
|
2580
2604
|
this.panel.on("resize", this.fit);
|
|
@@ -2615,6 +2639,31 @@ var NativeModalView = class {
|
|
|
2615
2639
|
revealFocused = false;
|
|
2616
2640
|
mediaViews = /* @__PURE__ */ new Set();
|
|
2617
2641
|
modelViews = /* @__PURE__ */ new Set();
|
|
2642
|
+
transparentModel = false;
|
|
2643
|
+
paintModelChrome = (buffer) => {
|
|
2644
|
+
if (!this.transparentModel || this.disposed || this.frame.animating) return;
|
|
2645
|
+
const left = this.panel.x + 1, right = this.panel.x + this.panel.width - 1;
|
|
2646
|
+
const top = this.panel.y + 1, bottom = this.panel.y + this.panel.height - 1;
|
|
2647
|
+
if (right <= left || bottom <= top) return;
|
|
2648
|
+
const viewport = this.body.viewport;
|
|
2649
|
+
const canvases = [...this.modelViews].flatMap((model) => {
|
|
2650
|
+
const canvas = [model.findDescendantById(`model3d-surface:${model.modelId}`), model.findDescendantById(`model3d-cpu:${model.modelId}`)].find((node) => node?.visible && !node.isDestroyed);
|
|
2651
|
+
if (!canvas) return [];
|
|
2652
|
+
const x = Math.max(left, viewport.x, canvas.x), y = Math.max(top, viewport.y, canvas.y);
|
|
2653
|
+
const endX = Math.min(right, viewport.x + viewport.width, canvas.x + canvas.width);
|
|
2654
|
+
const endY = Math.min(bottom, viewport.y + viewport.height, canvas.y + canvas.height);
|
|
2655
|
+
return endX > x && endY > y ? [{ x, y, endX, endY }] : [];
|
|
2656
|
+
}).sort((a, b) => a.x - b.x);
|
|
2657
|
+
for (let y = top; y < bottom; y++) {
|
|
2658
|
+
let x = left;
|
|
2659
|
+
for (const canvas of canvases) {
|
|
2660
|
+
if (y < canvas.y || y >= canvas.endY) continue;
|
|
2661
|
+
if (canvas.x > x) buffer.fillRect(x, y, canvas.x - x, 1, this.context.colors.background);
|
|
2662
|
+
x = Math.max(x, canvas.endX);
|
|
2663
|
+
}
|
|
2664
|
+
if (right > x) buffer.fillRect(x, y, right - x, 1, this.context.colors.background);
|
|
2665
|
+
}
|
|
2666
|
+
};
|
|
2618
2667
|
addText(id, parent = this.body) {
|
|
2619
2668
|
const text = new TextRenderable4(this.context.renderer, { id, content: "", width: "100%", wrapMode: "word", flexShrink: 0, fg: this.context.colors.foreground, selectable: true });
|
|
2620
2669
|
parent.add(text);
|
|
@@ -2762,6 +2811,13 @@ var NativeModalView = class {
|
|
|
2762
2811
|
this.refreshMask();
|
|
2763
2812
|
this.refreshOptions(patch.selectedIndex);
|
|
2764
2813
|
this.blocks?.replace(spec.blocks ?? []);
|
|
2814
|
+
const transparentModel = spec.kind === "document" && Boolean(spec.blocks?.some((block) => block.kind === "model3d"));
|
|
2815
|
+
if (transparentModel !== this.transparentModel) {
|
|
2816
|
+
for (const box of [this.body, this.body.wrapper, this.body.viewport, this.body.content, this.blocks?.parent])
|
|
2817
|
+
if (box instanceof BoxRenderable5) box.shouldFill = !transparentModel;
|
|
2818
|
+
this.frame.chrome.shouldFill = !transparentModel;
|
|
2819
|
+
this.transparentModel = transparentModel;
|
|
2820
|
+
}
|
|
2765
2821
|
if (this.progress) {
|
|
2766
2822
|
this.progress.visible = spec.progress !== null;
|
|
2767
2823
|
this.progress.content = nativeProgressText(spec.progress ?? void 0, true);
|
|
@@ -3120,6 +3176,14 @@ var NativeModalStack = class {
|
|
|
3120
3176
|
restoreFocus;
|
|
3121
3177
|
views = [];
|
|
3122
3178
|
byId = /* @__PURE__ */ new Map();
|
|
3179
|
+
pendingMedia = /* @__PURE__ */ new Set();
|
|
3180
|
+
trackMedia(promises) {
|
|
3181
|
+
for (const promise of promises) {
|
|
3182
|
+
if (this.pendingMedia.has(promise)) continue;
|
|
3183
|
+
this.pendingMedia.add(promise);
|
|
3184
|
+
void promise.then(() => this.pendingMedia.delete(promise), () => this.pendingMedia.delete(promise));
|
|
3185
|
+
}
|
|
3186
|
+
}
|
|
3123
3187
|
dimUnderlay = (buffer) => {
|
|
3124
3188
|
const panel = this.active?.panel;
|
|
3125
3189
|
if (!panel || panel.isDestroyed || !panel.visible) return;
|
|
@@ -3154,7 +3218,7 @@ var NativeModalStack = class {
|
|
|
3154
3218
|
close(id, result, notify = false) {
|
|
3155
3219
|
const view = this.byId.get(id);
|
|
3156
3220
|
if (!view) return;
|
|
3157
|
-
view.pauseMedia();
|
|
3221
|
+
this.trackMedia(view.pauseMedia());
|
|
3158
3222
|
view.frame.close(() => {
|
|
3159
3223
|
const wasActive = view === this.active;
|
|
3160
3224
|
view.destroy();
|
|
@@ -3176,11 +3240,17 @@ var NativeModalStack = class {
|
|
|
3176
3240
|
for (const view of this.views) view.reconcileMedia(view === this.active);
|
|
3177
3241
|
}
|
|
3178
3242
|
pauseMedia() {
|
|
3179
|
-
return this.views.flatMap((view) => view.pauseMedia());
|
|
3243
|
+
return [...this.pendingMedia, ...this.views.flatMap((view) => view.pauseMedia())];
|
|
3244
|
+
}
|
|
3245
|
+
async settleMedia() {
|
|
3246
|
+
while (this.pendingMedia.size) await Promise.allSettled([...this.pendingMedia]);
|
|
3180
3247
|
}
|
|
3181
3248
|
destroy() {
|
|
3182
3249
|
this.context.renderer.removePostProcessFn(this.dimUnderlay);
|
|
3183
|
-
for (const view of this.views)
|
|
3250
|
+
for (const view of this.views) {
|
|
3251
|
+
this.trackMedia(view.pauseMedia());
|
|
3252
|
+
view.destroy();
|
|
3253
|
+
}
|
|
3184
3254
|
this.views = [];
|
|
3185
3255
|
this.byId.clear();
|
|
3186
3256
|
}
|
|
@@ -9202,6 +9272,7 @@ var NativeFrontend = class {
|
|
|
9202
9272
|
this.content.destroy();
|
|
9203
9273
|
this.tasks.destroy();
|
|
9204
9274
|
await Promise.allSettled(mediaClosed);
|
|
9275
|
+
await this.modals.settleMedia();
|
|
9205
9276
|
this.root.destroyRecursively();
|
|
9206
9277
|
this.context.syntax.destroy();
|
|
9207
9278
|
await this.clipboard.dispose();
|