omnius 1.0.600 → 1.0.602
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 +1003 -254
- package/npm-shrinkwrap.json +5 -5
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -678140,6 +678140,8 @@ ${AGENTS_END}`;
|
|
|
678140
678140
|
var daemon_exports = {};
|
|
678141
678141
|
__export(daemon_exports, {
|
|
678142
678142
|
claimDaemonEndpoint: () => claimDaemonEndpoint,
|
|
678143
|
+
daemonPortFromServiceEnvironment: () => daemonPortFromServiceEnvironment,
|
|
678144
|
+
daemonServiceMatchesPort: () => daemonServiceMatchesPort,
|
|
678143
678145
|
ensureDaemon: () => ensureDaemon,
|
|
678144
678146
|
ensureDaemonVersion: () => ensureDaemonVersion,
|
|
678145
678147
|
forceKillDaemon: () => forceKillDaemon,
|
|
@@ -678155,7 +678157,8 @@ __export(daemon_exports, {
|
|
|
678155
678157
|
releaseDaemonEndpointForCurrentProcess: () => releaseDaemonEndpointForCurrentProcess,
|
|
678156
678158
|
restartDaemon: () => restartDaemon,
|
|
678157
678159
|
startDaemon: () => startDaemon,
|
|
678158
|
-
stopDaemon: () => stopDaemon
|
|
678160
|
+
stopDaemon: () => stopDaemon,
|
|
678161
|
+
stopDaemonAtPort: () => stopDaemonAtPort
|
|
678159
678162
|
});
|
|
678160
678163
|
import { spawn as spawn28 } from "node:child_process";
|
|
678161
678164
|
import { existsSync as existsSync116, readFileSync as readFileSync94, writeFileSync as writeFileSync60, mkdirSync as mkdirSync69, unlinkSync as unlinkSync22, openSync as openSync4, closeSync as closeSync4, writeSync as writeSync3, statSync as statSync46, renameSync as renameSync17 } from "node:fs";
|
|
@@ -678279,7 +678282,9 @@ async function isDaemonRunning(port) {
|
|
|
678279
678282
|
const resp = await fetch(`http://127.0.0.1:${p2}/health`, {
|
|
678280
678283
|
signal: AbortSignal.timeout(2e3)
|
|
678281
678284
|
});
|
|
678282
|
-
|
|
678285
|
+
if (!resp.ok) return false;
|
|
678286
|
+
const health = await resp.json();
|
|
678287
|
+
return health.status === "ok" && (typeof health.boot_version === "string" || typeof health.version === "string");
|
|
678283
678288
|
} catch {
|
|
678284
678289
|
return false;
|
|
678285
678290
|
}
|
|
@@ -678345,12 +678350,49 @@ async function runUserSystemctl(args, timeout2 = 2e4) {
|
|
|
678345
678350
|
return { available: false, ok: false };
|
|
678346
678351
|
}
|
|
678347
678352
|
}
|
|
678353
|
+
async function readUserSystemctl(args, timeout2 = 5e3) {
|
|
678354
|
+
try {
|
|
678355
|
+
const { spawnSync: spawnSync11 } = await import("node:child_process");
|
|
678356
|
+
const result = spawnSync11("systemctl", ["--user", ...args], {
|
|
678357
|
+
encoding: "utf8",
|
|
678358
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
678359
|
+
timeout: timeout2
|
|
678360
|
+
});
|
|
678361
|
+
return result.status === 0 ? result.stdout.trim() : null;
|
|
678362
|
+
} catch {
|
|
678363
|
+
return null;
|
|
678364
|
+
}
|
|
678365
|
+
}
|
|
678348
678366
|
async function hasManagedDaemonService() {
|
|
678349
678367
|
const enabled2 = await runUserSystemctl(["is-enabled", "omnius-daemon.service"], 5e3);
|
|
678350
678368
|
if (enabled2.ok) return true;
|
|
678351
678369
|
const active = await runUserSystemctl(["is-active", "omnius-daemon.service"], 5e3);
|
|
678352
678370
|
return active.ok;
|
|
678353
678371
|
}
|
|
678372
|
+
function daemonPortFromServiceEnvironment(value2) {
|
|
678373
|
+
const host = value2.match(/(?:^|\s)OMNIUS_HOST=(?:"([^"]+)"|'([^']+)'|([^\s]+))/)?.slice(1).find(Boolean);
|
|
678374
|
+
const direct = value2.match(/(?:^|\s)OMNIUS_PORT=(?:"(\d+)"|'(\d+)'|(\d+))/)?.slice(1).find(Boolean);
|
|
678375
|
+
const candidate = host?.match(/:(\d+)$/)?.[1] ?? direct;
|
|
678376
|
+
if (!candidate) return null;
|
|
678377
|
+
const port = Number(candidate);
|
|
678378
|
+
return Number.isInteger(port) && port >= 1 && port <= 65535 ? port : null;
|
|
678379
|
+
}
|
|
678380
|
+
function daemonServiceMatchesPort(requestedPort, effectiveEnvironment) {
|
|
678381
|
+
const effectivePort2 = daemonPortFromServiceEnvironment(effectiveEnvironment);
|
|
678382
|
+
return effectivePort2 === null ? requestedPort === DEFAULT_PORT2 : effectivePort2 === requestedPort;
|
|
678383
|
+
}
|
|
678384
|
+
async function managedDaemonServiceMatchesPort(port) {
|
|
678385
|
+
if (!await hasManagedDaemonService()) return false;
|
|
678386
|
+
const environment = await readUserSystemctl([
|
|
678387
|
+
"show",
|
|
678388
|
+
"omnius-daemon.service",
|
|
678389
|
+
"--property",
|
|
678390
|
+
"Environment",
|
|
678391
|
+
"--value"
|
|
678392
|
+
]);
|
|
678393
|
+
if (environment === null) return false;
|
|
678394
|
+
return daemonServiceMatchesPort(port, environment);
|
|
678395
|
+
}
|
|
678354
678396
|
function systemdQuote(value2) {
|
|
678355
678397
|
return /[\s"\\]/.test(value2) ? `"${value2.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"` : value2;
|
|
678356
678398
|
}
|
|
@@ -678448,6 +678490,43 @@ async function waitForDaemonStopped(port, attempts = DAEMON_GRACEFUL_STOP_ATTEMP
|
|
|
678448
678490
|
}
|
|
678449
678491
|
return false;
|
|
678450
678492
|
}
|
|
678493
|
+
async function reclaimStaleDaemonEndpointClaim(port) {
|
|
678494
|
+
const lockFile = daemonLockFile(port);
|
|
678495
|
+
const record = readDaemonLock(lockFile);
|
|
678496
|
+
if (!record) return true;
|
|
678497
|
+
if (!await daemonPortIsFree(port)) return true;
|
|
678498
|
+
if (!processIsAlive(record.pid)) {
|
|
678499
|
+
const current2 = readDaemonLock(lockFile);
|
|
678500
|
+
if (current2?.pid === record.pid && current2.token === record.token) {
|
|
678501
|
+
try {
|
|
678502
|
+
unlinkSync22(lockFile);
|
|
678503
|
+
} catch {
|
|
678504
|
+
}
|
|
678505
|
+
}
|
|
678506
|
+
return true;
|
|
678507
|
+
}
|
|
678508
|
+
const lease = listProcessLeases({ includeInactive: false }).find(
|
|
678509
|
+
(item) => item.status === "active" && item.pid === record.pid && item.ownerKind === "daemon" && item.ownerId === `daemon:${port}`
|
|
678510
|
+
);
|
|
678511
|
+
if (!lease) return false;
|
|
678512
|
+
const stopped = await stopProcessLease(lease.leaseId, {
|
|
678513
|
+
reason: `stale daemon endpoint claim on port ${port}`,
|
|
678514
|
+
termGraceMs: 1e3
|
|
678515
|
+
});
|
|
678516
|
+
if (stopped.action !== "killed" && stopped.action !== "dead") return false;
|
|
678517
|
+
for (let attempt = 0; attempt < 20 && processIsAlive(record.pid); attempt++) {
|
|
678518
|
+
await delay3(100);
|
|
678519
|
+
}
|
|
678520
|
+
if (processIsAlive(record.pid)) return false;
|
|
678521
|
+
const current = readDaemonLock(lockFile);
|
|
678522
|
+
if (current?.pid === record.pid && current.token === record.token) {
|
|
678523
|
+
try {
|
|
678524
|
+
unlinkSync22(lockFile);
|
|
678525
|
+
} catch {
|
|
678526
|
+
}
|
|
678527
|
+
}
|
|
678528
|
+
return daemonPortIsFree(port);
|
|
678529
|
+
}
|
|
678451
678530
|
async function reclaimOwnedDaemonListener(port = getDaemonPort(), dependencies = DEFAULT_RECLAIM_DEPENDENCIES) {
|
|
678452
678531
|
const holders = await dependencies.holderPids(port);
|
|
678453
678532
|
if (holders === null) {
|
|
@@ -678523,7 +678602,7 @@ async function reclaimOwnedDaemonListener(port = getDaemonPort(), dependencies =
|
|
|
678523
678602
|
}
|
|
678524
678603
|
async function restartDaemon(port, expectedVersion) {
|
|
678525
678604
|
const p2 = port ?? getDaemonPort();
|
|
678526
|
-
const managed = await
|
|
678605
|
+
const managed = await managedDaemonServiceMatchesPort(p2);
|
|
678527
678606
|
if (managed) {
|
|
678528
678607
|
const restarted = await runUserSystemctl(["restart", "omnius-daemon.service"]);
|
|
678529
678608
|
if (restarted.ok && (await waitForDaemonReady(p2, expectedVersion)).ok) return true;
|
|
@@ -678544,6 +678623,7 @@ async function restartDaemon(port, expectedVersion) {
|
|
|
678544
678623
|
const reclaimed = await reclaimOwnedDaemonListener(p2);
|
|
678545
678624
|
if (!reclaimed.ok) return false;
|
|
678546
678625
|
}
|
|
678626
|
+
if (!await reclaimStaleDaemonEndpointClaim(p2)) return false;
|
|
678547
678627
|
const pid = await startDaemon(p2);
|
|
678548
678628
|
if (!pid) return false;
|
|
678549
678629
|
return (await waitForDaemonReady(p2, expectedVersion)).ok;
|
|
@@ -678650,7 +678730,7 @@ async function startDaemon(port = getDaemonPort()) {
|
|
|
678650
678730
|
projectRoot: process.cwd(),
|
|
678651
678731
|
lifecycle: "daemon",
|
|
678652
678732
|
persistent: true,
|
|
678653
|
-
reason: `shared API daemon on port ${
|
|
678733
|
+
reason: `shared API daemon on port ${daemonPort}`,
|
|
678654
678734
|
command: [daemonCommand.command, ...daemonCommand.args].join(" "),
|
|
678655
678735
|
cwd: process.cwd()
|
|
678656
678736
|
});
|
|
@@ -678695,6 +678775,17 @@ function stopDaemon() {
|
|
|
678695
678775
|
return false;
|
|
678696
678776
|
}
|
|
678697
678777
|
}
|
|
678778
|
+
async function stopDaemonAtPort(port = getDaemonPort()) {
|
|
678779
|
+
if (await managedDaemonServiceMatchesPort(port)) {
|
|
678780
|
+
const stopped = await runUserSystemctl(["stop", "omnius-daemon.service"]);
|
|
678781
|
+
if (stopped.ok && await waitForDaemonStopped(port)) return true;
|
|
678782
|
+
}
|
|
678783
|
+
const wasRunning = await isDaemonRunning(port);
|
|
678784
|
+
const reclaimed = await reclaimOwnedDaemonListener(port);
|
|
678785
|
+
if (!reclaimed.ok) return false;
|
|
678786
|
+
const claimCleared = await reclaimStaleDaemonEndpointClaim(port);
|
|
678787
|
+
return claimCleared && (wasRunning || reclaimed.action === "cleared");
|
|
678788
|
+
}
|
|
678698
678789
|
async function forceKillDaemon(port) {
|
|
678699
678790
|
const p2 = port ?? getDaemonPort();
|
|
678700
678791
|
let killed = 0;
|
|
@@ -678804,6 +678895,9 @@ async function ensureDaemonVersion(expectedVersion = getLocalCliVersion(), port
|
|
|
678804
678895
|
process.platform === "linux" && reclaimed.action === "blocked"
|
|
678805
678896
|
);
|
|
678806
678897
|
}
|
|
678898
|
+
if (!await reclaimStaleDaemonEndpointClaim(port)) {
|
|
678899
|
+
return finish(false, "failed", null, 0, process.platform === "linux");
|
|
678900
|
+
}
|
|
678807
678901
|
getDaemonPid();
|
|
678808
678902
|
const pid = await startDaemon(port);
|
|
678809
678903
|
if (!pid) {
|
|
@@ -678888,12 +678982,15 @@ __export(tray_exports, {
|
|
|
678888
678982
|
resolveTrayEndpoint: () => resolveTrayEndpoint,
|
|
678889
678983
|
resolveTrayLaunchCommand: () => resolveTrayLaunchCommand,
|
|
678890
678984
|
resolveTrayPaths: () => resolveTrayPaths,
|
|
678985
|
+
resolveTrayRegistrationCommand: () => resolveTrayRegistrationCommand,
|
|
678891
678986
|
runTrayForeground: () => runTrayForeground,
|
|
678892
678987
|
startTray: () => startTray,
|
|
678893
678988
|
stopTray: () => stopTray,
|
|
678989
|
+
trayProcessCommandLooksOwned: () => trayProcessCommandLooksOwned,
|
|
678894
678990
|
trayRequiresRestart: () => trayRequiresRestart,
|
|
678895
678991
|
traySupport: () => traySupport,
|
|
678896
678992
|
trayUpdatePresentation: () => trayUpdatePresentation,
|
|
678993
|
+
trayVersionPresentation: () => trayVersionPresentation,
|
|
678897
678994
|
uninstallTrayAutostart: () => uninstallTrayAutostart,
|
|
678898
678995
|
verifyTrayHelper: () => verifyTrayHelper
|
|
678899
678996
|
});
|
|
@@ -678906,6 +679003,7 @@ import {
|
|
|
678906
679003
|
mkdirSync as mkdirSync70,
|
|
678907
679004
|
openSync as openSync5,
|
|
678908
679005
|
readFileSync as readFileSync95,
|
|
679006
|
+
realpathSync,
|
|
678909
679007
|
renameSync as renameSync18,
|
|
678910
679008
|
unlinkSync as unlinkSync23,
|
|
678911
679009
|
writeFileSync as writeFileSync61,
|
|
@@ -678921,7 +679019,8 @@ function trayUpdatePresentation(currentVersion, latestVersion, state = null) {
|
|
|
678921
679019
|
title: `Updating to v${state.target_version} — ${state.phase.replaceAll("_", " ")}`,
|
|
678922
679020
|
tooltip: `Operation ${state.operation_id}; progress is shared with the dashboard and CLI`,
|
|
678923
679021
|
enabled: false,
|
|
678924
|
-
targetVersion: state.target_version
|
|
679022
|
+
targetVersion: state.target_version,
|
|
679023
|
+
action: "update-omnius"
|
|
678925
679024
|
};
|
|
678926
679025
|
}
|
|
678927
679026
|
if (state?.status === "failed" && state.target_version === latestVersion) {
|
|
@@ -678929,7 +679028,8 @@ function trayUpdatePresentation(currentVersion, latestVersion, state = null) {
|
|
|
678929
679028
|
title: `Update to v${state.target_version} failed — retry`,
|
|
678930
679029
|
tooltip: state.remediation || state.error || "Open the tray logs for update diagnostics",
|
|
678931
679030
|
enabled: true,
|
|
678932
|
-
targetVersion: state.target_version
|
|
679031
|
+
targetVersion: state.target_version,
|
|
679032
|
+
action: "update-omnius"
|
|
678933
679033
|
};
|
|
678934
679034
|
}
|
|
678935
679035
|
if (currentVersion && latestVersion) {
|
|
@@ -678937,12 +679037,31 @@ function trayUpdatePresentation(currentVersion, latestVersion, state = null) {
|
|
|
678937
679037
|
title: `Update Omnius to v${latestVersion}`,
|
|
678938
679038
|
tooltip: `Install and verify the global package, executable, daemon, and tray (current v${currentVersion})`,
|
|
678939
679039
|
enabled: true,
|
|
678940
|
-
targetVersion: latestVersion
|
|
679040
|
+
targetVersion: latestVersion,
|
|
679041
|
+
action: "update-omnius"
|
|
678941
679042
|
};
|
|
678942
679043
|
}
|
|
678943
679044
|
return {
|
|
678944
|
-
title: currentVersion ? `Omnius v${currentVersion}
|
|
678945
|
-
tooltip: "
|
|
679045
|
+
title: currentVersion ? `Check for Omnius updates (v${currentVersion})` : "Check for Omnius updates",
|
|
679046
|
+
tooltip: "Check npm now; automatic checks continue in the background",
|
|
679047
|
+
enabled: Boolean(currentVersion),
|
|
679048
|
+
action: "check-update"
|
|
679049
|
+
};
|
|
679050
|
+
}
|
|
679051
|
+
function trayVersionPresentation(health, update2) {
|
|
679052
|
+
if (health.kind === "online" && update2.targetVersion) {
|
|
679053
|
+
const current = health.version ? `v${health.version}` : "current version";
|
|
679054
|
+
const running = !update2.enabled && update2.title.startsWith("Updating ");
|
|
679055
|
+
return {
|
|
679056
|
+
title: running ? update2.title : `Omnius ${current} — update to v${update2.targetVersion} available`,
|
|
679057
|
+
tooltip: update2.tooltip,
|
|
679058
|
+
enabled: update2.enabled,
|
|
679059
|
+
action: "update-omnius"
|
|
679060
|
+
};
|
|
679061
|
+
}
|
|
679062
|
+
return {
|
|
679063
|
+
title: health.label,
|
|
679064
|
+
tooltip: health.tooltip,
|
|
678946
679065
|
enabled: false
|
|
678947
679066
|
};
|
|
678948
679067
|
}
|
|
@@ -678994,19 +679113,6 @@ function normalizeTrayEndpoint(value2) {
|
|
|
678994
679113
|
return null;
|
|
678995
679114
|
}
|
|
678996
679115
|
}
|
|
678997
|
-
function systemdDaemonEnvironment() {
|
|
678998
|
-
if (process.platform !== "linux") return "";
|
|
678999
|
-
try {
|
|
679000
|
-
const result = spawnSync9(
|
|
679001
|
-
"systemctl",
|
|
679002
|
-
["--user", "show", SERVICE_LABEL, "--property", "Environment", "--value"],
|
|
679003
|
-
{ encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 2e3 }
|
|
679004
|
-
);
|
|
679005
|
-
return result.status === 0 ? result.stdout.trim() : "";
|
|
679006
|
-
} catch {
|
|
679007
|
-
return "";
|
|
679008
|
-
}
|
|
679009
|
-
}
|
|
679010
679116
|
function endpointFromServiceEnvironment(value2) {
|
|
679011
679117
|
const host = value2.match(/(?:^|\s)OMNIUS_HOST=(?:"([^"]+)"|'([^']+)'|([^\s]+))/)?.slice(1).find(Boolean);
|
|
679012
679118
|
const port = value2.match(/(?:^|\s)OMNIUS_PORT=(?:"(\d+)"|'(\d+)'|(\d+))/)?.slice(1).find(Boolean);
|
|
@@ -679018,7 +679124,6 @@ function resolveTrayEndpoint(explicit) {
|
|
|
679018
679124
|
process.env["OMNIUS_TRAY_ENDPOINT"],
|
|
679019
679125
|
process.env["OMNIUS_HOST"],
|
|
679020
679126
|
process.env["OMNIUS_PORT"],
|
|
679021
|
-
endpointFromServiceEnvironment(systemdDaemonEnvironment()),
|
|
679022
679127
|
DEFAULT_ENDPOINT
|
|
679023
679128
|
];
|
|
679024
679129
|
for (const candidate of candidates) {
|
|
@@ -679158,6 +679263,30 @@ function readProcessState(path16) {
|
|
|
679158
679263
|
return null;
|
|
679159
679264
|
}
|
|
679160
679265
|
}
|
|
679266
|
+
function processCommandLine(pid) {
|
|
679267
|
+
try {
|
|
679268
|
+
if (process.platform === "linux") {
|
|
679269
|
+
return readFileSync95(`/proc/${pid}/cmdline`, "utf8").replace(/\0/g, " ");
|
|
679270
|
+
}
|
|
679271
|
+
if (process.platform !== "win32") {
|
|
679272
|
+
return spawnSync9("ps", ["-p", String(pid), "-o", "command="], {
|
|
679273
|
+
encoding: "utf8",
|
|
679274
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
679275
|
+
timeout: 2e3
|
|
679276
|
+
}).stdout.trim();
|
|
679277
|
+
}
|
|
679278
|
+
return spawnSync9("wmic", ["process", "where", `ProcessId=${pid}`, "get", "CommandLine", "/value"], {
|
|
679279
|
+
encoding: "utf8",
|
|
679280
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
679281
|
+
timeout: 2e3
|
|
679282
|
+
}).stdout;
|
|
679283
|
+
} catch {
|
|
679284
|
+
return "";
|
|
679285
|
+
}
|
|
679286
|
+
}
|
|
679287
|
+
function trayProcessCommandLooksOwned(command) {
|
|
679288
|
+
return /(?:^|\s)tray\s+run(?:\s|$)/i.test(command) && (/(?:^|[\\/\s])omnius(?:[\\/\s]|$)/i.test(command) || /[\\/]dist[\\/]index\.js\b|launcher\.cjs\b/i.test(command));
|
|
679289
|
+
}
|
|
679161
679290
|
function writeProcessState(paths, state) {
|
|
679162
679291
|
mkdirSync70(paths.runtimeDir, { recursive: true, mode: 448 });
|
|
679163
679292
|
const temporary = `${paths.stateFile}.${process.pid}.tmp`;
|
|
@@ -679232,6 +679361,34 @@ function resolveTrayLaunchCommand() {
|
|
|
679232
679361
|
}
|
|
679233
679362
|
return null;
|
|
679234
679363
|
}
|
|
679364
|
+
function installedOmniusLaunchCommand() {
|
|
679365
|
+
try {
|
|
679366
|
+
const result = spawnSync9(process.platform === "win32" ? "where" : "which", ["omnius"], {
|
|
679367
|
+
encoding: "utf8",
|
|
679368
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
679369
|
+
timeout: 2e3
|
|
679370
|
+
});
|
|
679371
|
+
const first2 = result.stdout?.split(/\r?\n/).map((line) => line.trim()).find(Boolean);
|
|
679372
|
+
return first2 && existsSync117(first2) ? commandForEntrypoint2(realpathSync(first2)) : null;
|
|
679373
|
+
} catch {
|
|
679374
|
+
return null;
|
|
679375
|
+
}
|
|
679376
|
+
}
|
|
679377
|
+
function resolveTrayRegistrationCommand() {
|
|
679378
|
+
return installedOmniusLaunchCommand() ?? resolveTrayLaunchCommand();
|
|
679379
|
+
}
|
|
679380
|
+
function registrationIconPath(launch) {
|
|
679381
|
+
const extension3 = process.platform === "win32" ? "ico" : "png";
|
|
679382
|
+
const entrypoints = [...launch.args, launch.command].filter((candidate) => existsSync117(candidate));
|
|
679383
|
+
for (const entrypoint of entrypoints) {
|
|
679384
|
+
const real = realpathSync(entrypoint);
|
|
679385
|
+
for (const packageRoot of [dirname43(real), join129(dirname43(real), "..")]) {
|
|
679386
|
+
const candidate = join129(packageRoot, "assets", "tray", `omnius-online.${extension3}`);
|
|
679387
|
+
if (existsSync117(candidate)) return candidate;
|
|
679388
|
+
}
|
|
679389
|
+
}
|
|
679390
|
+
return assetPath("online");
|
|
679391
|
+
}
|
|
679235
679392
|
function desktopExecQuote(value2) {
|
|
679236
679393
|
return `"${value2.replaceAll("\\", "\\\\").replaceAll('"', '\\"').replaceAll("$", "\\$").replaceAll("`", "\\`")}"`;
|
|
679237
679394
|
}
|
|
@@ -679285,13 +679442,13 @@ start "" /b ${argv.map(windowsCmdQuote).join(" ")}\r
|
|
|
679285
679442
|
throw new Error(`Autostart is not supported on ${platform8}`);
|
|
679286
679443
|
}
|
|
679287
679444
|
function writeTrayAutostart(endpoint) {
|
|
679288
|
-
const launch =
|
|
679445
|
+
const launch = resolveTrayRegistrationCommand();
|
|
679289
679446
|
if (!launch) throw new Error("Could not resolve the installed Omnius CLI entrypoint");
|
|
679290
679447
|
const paths = resolveTrayPaths();
|
|
679291
679448
|
mkdirSync70(dirname43(paths.autostartFile), { recursive: true });
|
|
679292
679449
|
writeFileSync61(
|
|
679293
679450
|
paths.autostartFile,
|
|
679294
|
-
buildAutostartContent(process.platform, launch, endpoint,
|
|
679451
|
+
buildAutostartContent(process.platform, launch, endpoint, registrationIconPath(launch)),
|
|
679295
679452
|
{ encoding: "utf8", mode: process.platform === "win32" ? 448 : 384 }
|
|
679296
679453
|
);
|
|
679297
679454
|
return paths.autostartFile;
|
|
@@ -679313,44 +679470,12 @@ function uninstallTrayAutostart() {
|
|
|
679313
679470
|
unlinkSync23(paths.autostartFile);
|
|
679314
679471
|
return true;
|
|
679315
679472
|
}
|
|
679316
|
-
function runServiceControl(action) {
|
|
679317
|
-
try {
|
|
679318
|
-
if (process.platform === "linux") {
|
|
679319
|
-
const result = spawnSync9("systemctl", ["--user", action, SERVICE_LABEL], {
|
|
679320
|
-
stdio: "ignore",
|
|
679321
|
-
timeout: 1e4
|
|
679322
|
-
});
|
|
679323
|
-
return result.status === 0;
|
|
679324
|
-
}
|
|
679325
|
-
if (process.platform === "darwin" && typeof process.getuid === "function") {
|
|
679326
|
-
const domain = `gui/${process.getuid()}`;
|
|
679327
|
-
const plist = join129(homedir40(), "Library", "LaunchAgents", `${LAUNCHD_LABEL}.plist`);
|
|
679328
|
-
if (action === "stop") {
|
|
679329
|
-
return spawnSync9("launchctl", ["bootout", `${domain}/${LAUNCHD_LABEL}`], { stdio: "ignore", timeout: 1e4 }).status === 0;
|
|
679330
|
-
}
|
|
679331
|
-
if (action === "restart") {
|
|
679332
|
-
return spawnSync9("launchctl", ["kickstart", "-k", `${domain}/${LAUNCHD_LABEL}`], { stdio: "ignore", timeout: 1e4 }).status === 0;
|
|
679333
|
-
}
|
|
679334
|
-
spawnSync9("launchctl", ["bootstrap", domain, plist], { stdio: "ignore", timeout: 1e4 });
|
|
679335
|
-
return spawnSync9("launchctl", ["kickstart", `${domain}/${LAUNCHD_LABEL}`], { stdio: "ignore", timeout: 1e4 }).status === 0;
|
|
679336
|
-
}
|
|
679337
|
-
if (process.platform === "win32") {
|
|
679338
|
-
const args = action === "stop" ? ["/End", "/TN", WINDOWS_TASK_NAME] : ["/Run", "/TN", WINDOWS_TASK_NAME];
|
|
679339
|
-
return spawnSync9("schtasks", args, { stdio: "ignore", timeout: 1e4 }).status === 0;
|
|
679340
|
-
}
|
|
679341
|
-
} catch {
|
|
679342
|
-
}
|
|
679343
|
-
return false;
|
|
679344
|
-
}
|
|
679345
679473
|
async function controlDaemon(action, endpoint) {
|
|
679346
|
-
if (runServiceControl(action)) return true;
|
|
679347
|
-
if (action === "stop") return stopDaemon();
|
|
679348
|
-
if (action === "restart") {
|
|
679349
|
-
stopDaemon();
|
|
679350
|
-
await new Promise((resolve87) => setTimeout(resolve87, 500));
|
|
679351
|
-
}
|
|
679352
679474
|
const port = Number(new URL(endpoint).port);
|
|
679353
|
-
|
|
679475
|
+
if (!Number.isInteger(port) || port <= 0) return false;
|
|
679476
|
+
if (action === "stop") return stopDaemonAtPort(port);
|
|
679477
|
+
if (action === "restart") return restartDaemon(port, getLocalCliVersion());
|
|
679478
|
+
return (await ensureDaemonVersion(getLocalCliVersion(), port)).ok;
|
|
679354
679479
|
}
|
|
679355
679480
|
function openExternal(target) {
|
|
679356
679481
|
try {
|
|
@@ -679372,7 +679497,7 @@ async function loadSysTray() {
|
|
|
679372
679497
|
return constructor;
|
|
679373
679498
|
}
|
|
679374
679499
|
function menuForHealth(health, endpoint, registered, update2) {
|
|
679375
|
-
const healthItem =
|
|
679500
|
+
const healthItem = trayVersionPresentation(health, update2);
|
|
679376
679501
|
const actionItem = {
|
|
679377
679502
|
title: health.kind === "online" ? "Restart daemon" : "Start daemon",
|
|
679378
679503
|
tooltip: health.kind === "online" ? "Restart the Omnius API daemon" : "Start the Omnius API daemon",
|
|
@@ -679394,7 +679519,8 @@ function menuForHealth(health, endpoint, registered, update2) {
|
|
|
679394
679519
|
title: update2.title,
|
|
679395
679520
|
tooltip: update2.tooltip,
|
|
679396
679521
|
enabled: update2.enabled,
|
|
679397
|
-
action:
|
|
679522
|
+
action: update2.action,
|
|
679523
|
+
hidden: Boolean(update2.targetVersion)
|
|
679398
679524
|
};
|
|
679399
679525
|
return {
|
|
679400
679526
|
healthItem,
|
|
@@ -679462,7 +679588,9 @@ async function startTray(explicitEndpoint) {
|
|
|
679462
679588
|
if (!trayRequiresRestart(existing, endpoint, liveHealth)) {
|
|
679463
679589
|
return { ...existing, health: liveHealth };
|
|
679464
679590
|
}
|
|
679465
|
-
await stopTray()
|
|
679591
|
+
if (!await stopTray()) {
|
|
679592
|
+
throw new Error(`Could not stop the existing Omnius indicator on ${existing.endpoint}`);
|
|
679593
|
+
}
|
|
679466
679594
|
}
|
|
679467
679595
|
const support = traySupport();
|
|
679468
679596
|
if (!support.supported) throw new Error(support.reason || "Tray is not supported on this host");
|
|
@@ -679513,16 +679641,49 @@ async function stopTray() {
|
|
|
679513
679641
|
}
|
|
679514
679642
|
return false;
|
|
679515
679643
|
}
|
|
679644
|
+
const state = readProcessState(paths.stateFile);
|
|
679645
|
+
if (state?.pid !== pid || !trayProcessCommandLooksOwned(processCommandLine(pid))) {
|
|
679646
|
+
return false;
|
|
679647
|
+
}
|
|
679516
679648
|
try {
|
|
679517
679649
|
process.kill(pid, "SIGTERM");
|
|
679518
679650
|
} catch {
|
|
679519
679651
|
return false;
|
|
679520
679652
|
}
|
|
679521
679653
|
for (let i2 = 0; i2 < 30; i2++) {
|
|
679522
|
-
if (!isProcessAlive2(pid))
|
|
679654
|
+
if (!isProcessAlive2(pid)) {
|
|
679655
|
+
try {
|
|
679656
|
+
unlinkSync23(paths.pidFile);
|
|
679657
|
+
} catch {
|
|
679658
|
+
}
|
|
679659
|
+
try {
|
|
679660
|
+
unlinkSync23(paths.stateFile);
|
|
679661
|
+
} catch {
|
|
679662
|
+
}
|
|
679663
|
+
return true;
|
|
679664
|
+
}
|
|
679665
|
+
await new Promise((resolve87) => setTimeout(resolve87, 100));
|
|
679666
|
+
}
|
|
679667
|
+
if (!trayProcessCommandLooksOwned(processCommandLine(pid))) return false;
|
|
679668
|
+
try {
|
|
679669
|
+
process.kill(pid, "SIGKILL");
|
|
679670
|
+
} catch {
|
|
679671
|
+
}
|
|
679672
|
+
for (let i2 = 0; i2 < 20; i2++) {
|
|
679673
|
+
if (!isProcessAlive2(pid)) {
|
|
679674
|
+
try {
|
|
679675
|
+
unlinkSync23(paths.pidFile);
|
|
679676
|
+
} catch {
|
|
679677
|
+
}
|
|
679678
|
+
try {
|
|
679679
|
+
unlinkSync23(paths.stateFile);
|
|
679680
|
+
} catch {
|
|
679681
|
+
}
|
|
679682
|
+
return true;
|
|
679683
|
+
}
|
|
679523
679684
|
await new Promise((resolve87) => setTimeout(resolve87, 100));
|
|
679524
679685
|
}
|
|
679525
|
-
return
|
|
679686
|
+
return false;
|
|
679526
679687
|
}
|
|
679527
679688
|
async function runTrayForeground(explicitEndpoint) {
|
|
679528
679689
|
const support = traySupport();
|
|
@@ -679605,8 +679766,6 @@ async function runTrayForeground(explicitEndpoint) {
|
|
|
679605
679766
|
health = next;
|
|
679606
679767
|
state = { ...state, health };
|
|
679607
679768
|
writeProcessState(paths, state);
|
|
679608
|
-
menuState.healthItem.title = health.label;
|
|
679609
|
-
menuState.healthItem.tooltip = health.tooltip;
|
|
679610
679769
|
menuState.actionItem.title = health.kind === "online" ? "Restart daemon" : "Start daemon";
|
|
679611
679770
|
menuState.actionItem.action = health.kind === "online" ? "restart-daemon" : "start-daemon";
|
|
679612
679771
|
menuState.stopItem.enabled = health.kind === "online";
|
|
@@ -679619,6 +679778,14 @@ async function runTrayForeground(explicitEndpoint) {
|
|
|
679619
679778
|
menuState.updateItem.title = updateView.title;
|
|
679620
679779
|
menuState.updateItem.tooltip = updateView.tooltip;
|
|
679621
679780
|
menuState.updateItem.enabled = updateView.enabled;
|
|
679781
|
+
menuState.updateItem.action = updateView.action;
|
|
679782
|
+
menuState.updateItem.hidden = Boolean(updateView.targetVersion);
|
|
679783
|
+
const versionView = trayVersionPresentation(health, updateView);
|
|
679784
|
+
menuState.healthItem.title = versionView.title;
|
|
679785
|
+
menuState.healthItem.tooltip = versionView.tooltip;
|
|
679786
|
+
menuState.healthItem.enabled = versionView.enabled;
|
|
679787
|
+
if (versionView.action) menuState.healthItem.action = versionView.action;
|
|
679788
|
+
else delete menuState.healthItem.action;
|
|
679622
679789
|
const autostartRegistered = existsSync117(paths.autostartFile);
|
|
679623
679790
|
const autostartChanged = menuState.autostartItem.checked !== autostartRegistered;
|
|
679624
679791
|
if (autostartChanged) menuState.autostartItem.checked = autostartRegistered;
|
|
@@ -679676,14 +679843,56 @@ async function runTrayForeground(explicitEndpoint) {
|
|
|
679676
679843
|
trayWasRunning: true
|
|
679677
679844
|
});
|
|
679678
679845
|
updateView = trayUpdatePresentation(currentVersion, targetVersion, readUpdateState());
|
|
679846
|
+
if (updateView.enabled) {
|
|
679847
|
+
updateView = {
|
|
679848
|
+
title: `Updating to v${targetVersion} — starting`,
|
|
679849
|
+
tooltip: "The global npm update transaction is starting",
|
|
679850
|
+
enabled: false,
|
|
679851
|
+
targetVersion,
|
|
679852
|
+
action: "update-omnius"
|
|
679853
|
+
};
|
|
679854
|
+
}
|
|
679855
|
+
const versionView = trayVersionPresentation(health, updateView);
|
|
679856
|
+
menuState.healthItem.title = versionView.title;
|
|
679857
|
+
menuState.healthItem.tooltip = versionView.tooltip;
|
|
679858
|
+
menuState.healthItem.enabled = versionView.enabled;
|
|
679859
|
+
menuState.healthItem.action = "update-omnius";
|
|
679679
679860
|
menuState.updateItem.title = updateView.title;
|
|
679680
679861
|
menuState.updateItem.tooltip = updateView.tooltip;
|
|
679681
679862
|
menuState.updateItem.enabled = false;
|
|
679863
|
+
menuState.updateItem.hidden = true;
|
|
679682
679864
|
} catch (error) {
|
|
679683
|
-
|
|
679684
|
-
menuState.
|
|
679685
|
-
menuState.
|
|
679865
|
+
const message2 = error instanceof Error ? error.message : String(error);
|
|
679866
|
+
menuState.healthItem.title = `Update to v${targetVersion} could not start — retry`;
|
|
679867
|
+
menuState.healthItem.tooltip = message2;
|
|
679868
|
+
menuState.healthItem.enabled = true;
|
|
679869
|
+
menuState.healthItem.action = "update-omnius";
|
|
679686
679870
|
}
|
|
679871
|
+
await tray?.sendAction({ type: "update-item", item: menuState.healthItem });
|
|
679872
|
+
await tray?.sendAction({ type: "update-item", item: menuState.updateItem });
|
|
679873
|
+
break;
|
|
679874
|
+
}
|
|
679875
|
+
case "check-update": {
|
|
679876
|
+
const currentVersion = health.version;
|
|
679877
|
+
if (!currentVersion) break;
|
|
679878
|
+
availableUpdate = await checkForUpdate(currentVersion, true);
|
|
679879
|
+
updateView = trayUpdatePresentation(
|
|
679880
|
+
currentVersion,
|
|
679881
|
+
availableUpdate?.latestVersion,
|
|
679882
|
+
readUpdateState()
|
|
679883
|
+
);
|
|
679884
|
+
menuState.updateItem.title = updateView.title;
|
|
679885
|
+
menuState.updateItem.tooltip = updateView.tooltip;
|
|
679886
|
+
menuState.updateItem.enabled = updateView.enabled;
|
|
679887
|
+
menuState.updateItem.action = updateView.action;
|
|
679888
|
+
menuState.updateItem.hidden = Boolean(updateView.targetVersion);
|
|
679889
|
+
const versionView = trayVersionPresentation(health, updateView);
|
|
679890
|
+
menuState.healthItem.title = versionView.title;
|
|
679891
|
+
menuState.healthItem.tooltip = versionView.tooltip;
|
|
679892
|
+
menuState.healthItem.enabled = versionView.enabled;
|
|
679893
|
+
if (versionView.action) menuState.healthItem.action = versionView.action;
|
|
679894
|
+
else delete menuState.healthItem.action;
|
|
679895
|
+
await tray?.sendAction({ type: "update-item", item: menuState.healthItem });
|
|
679687
679896
|
await tray?.sendAction({ type: "update-item", item: menuState.updateItem });
|
|
679688
679897
|
break;
|
|
679689
679898
|
}
|
|
@@ -679708,7 +679917,7 @@ async function runTrayForeground(explicitEndpoint) {
|
|
|
679708
679917
|
releaseTrayPid(paths);
|
|
679709
679918
|
}
|
|
679710
679919
|
}
|
|
679711
|
-
var DEFAULT_ENDPOINT, TRAY_HELPER_VERSION, POLL_INTERVAL_MS, START_WAIT_MS,
|
|
679920
|
+
var DEFAULT_ENDPOINT, TRAY_HELPER_VERSION, POLL_INTERVAL_MS, START_WAIT_MS, EXPECTED_HELPER_SHA256;
|
|
679712
679921
|
var init_tray = __esm({
|
|
679713
679922
|
"packages/cli/src/tray.ts"() {
|
|
679714
679923
|
init_daemon();
|
|
@@ -679718,9 +679927,6 @@ var init_tray = __esm({
|
|
|
679718
679927
|
TRAY_HELPER_VERSION = "2.1.4";
|
|
679719
679928
|
POLL_INTERVAL_MS = 1e4;
|
|
679720
679929
|
START_WAIT_MS = 5e3;
|
|
679721
|
-
SERVICE_LABEL = "omnius-daemon.service";
|
|
679722
|
-
LAUNCHD_LABEL = "ai.omnius.daemon";
|
|
679723
|
-
WINDOWS_TASK_NAME = "OmniusDaemon";
|
|
679724
679930
|
EXPECTED_HELPER_SHA256 = {
|
|
679725
679931
|
aix: void 0,
|
|
679726
679932
|
android: void 0,
|
|
@@ -679759,6 +679965,26 @@ function output(status, json = false) {
|
|
|
679759
679965
|
`);
|
|
679760
679966
|
else printStatus(status);
|
|
679761
679967
|
}
|
|
679968
|
+
function endpointPort(endpoint) {
|
|
679969
|
+
const port = Number(new URL(endpoint).port);
|
|
679970
|
+
if (!Number.isInteger(port) || port <= 0) {
|
|
679971
|
+
throw new Error(`Invalid tray daemon endpoint: ${endpoint}`);
|
|
679972
|
+
}
|
|
679973
|
+
return port;
|
|
679974
|
+
}
|
|
679975
|
+
async function ensureTrayDaemonOnline(endpoint) {
|
|
679976
|
+
const port = endpointPort(endpoint);
|
|
679977
|
+
const daemon = await ensureDaemonVersion(getLocalCliVersion(), port);
|
|
679978
|
+
if (!daemon.ok) {
|
|
679979
|
+
throw new Error(`Could not reconcile the Omnius daemon on port ${port}`);
|
|
679980
|
+
}
|
|
679981
|
+
for (let attempt = 0; attempt < 10; attempt++) {
|
|
679982
|
+
const health = await pollTrayHealth(endpoint);
|
|
679983
|
+
if (health.kind === "online") return;
|
|
679984
|
+
await new Promise((resolve87) => setTimeout(resolve87, 200));
|
|
679985
|
+
}
|
|
679986
|
+
throw new Error(`Omnius daemon did not become online at ${endpoint}`);
|
|
679987
|
+
}
|
|
679762
679988
|
async function trayCommand(options2) {
|
|
679763
679989
|
const raw = options2.subCommand || "status";
|
|
679764
679990
|
if (!SUBCOMMANDS.has(raw)) {
|
|
@@ -679767,10 +679993,12 @@ async function trayCommand(options2) {
|
|
|
679767
679993
|
const subCommand = raw;
|
|
679768
679994
|
const endpoint = resolveTrayEndpoint(options2.endpoint);
|
|
679769
679995
|
if (subCommand === "run") {
|
|
679996
|
+
await ensureTrayDaemonOnline(endpoint);
|
|
679770
679997
|
await runTrayForeground(endpoint);
|
|
679771
679998
|
return;
|
|
679772
679999
|
}
|
|
679773
680000
|
if (subCommand === "install") {
|
|
680001
|
+
await ensureTrayDaemonOnline(endpoint);
|
|
679774
680002
|
const registrationFile = installTrayAutostart(endpoint);
|
|
679775
680003
|
const status = await startTray(endpoint);
|
|
679776
680004
|
if (!options2.json) process.stdout.write(`Registered tray autostart: ${registrationFile}
|
|
@@ -679787,6 +680015,7 @@ async function trayCommand(options2) {
|
|
|
679787
680015
|
return;
|
|
679788
680016
|
}
|
|
679789
680017
|
if (subCommand === "start") {
|
|
680018
|
+
await ensureTrayDaemonOnline(endpoint);
|
|
679790
680019
|
output(await startTray(endpoint), options2.json);
|
|
679791
680020
|
return;
|
|
679792
680021
|
}
|
|
@@ -679798,7 +680027,11 @@ async function trayCommand(options2) {
|
|
|
679798
680027
|
return;
|
|
679799
680028
|
}
|
|
679800
680029
|
if (subCommand === "restart") {
|
|
679801
|
-
await
|
|
680030
|
+
await ensureTrayDaemonOnline(endpoint);
|
|
680031
|
+
const status = await getTrayStatus(endpoint);
|
|
680032
|
+
if (status.running && !await stopTray()) {
|
|
680033
|
+
throw new Error(`Could not stop the existing Omnius indicator on ${status.endpoint}`);
|
|
680034
|
+
}
|
|
679802
680035
|
output(await startTray(endpoint), options2.json);
|
|
679803
680036
|
return;
|
|
679804
680037
|
}
|
|
@@ -679808,6 +680041,7 @@ var SUBCOMMANDS;
|
|
|
679808
680041
|
var init_tray2 = __esm({
|
|
679809
680042
|
"packages/cli/src/commands/tray.ts"() {
|
|
679810
680043
|
init_tray();
|
|
680044
|
+
init_daemon();
|
|
679811
680045
|
SUBCOMMANDS = /* @__PURE__ */ new Set([
|
|
679812
680046
|
"install",
|
|
679813
680047
|
"uninstall",
|
|
@@ -724624,9 +724858,18 @@ async function runIndicatorCommand(rawAction, operations = DEFAULT_OPERATIONS) {
|
|
|
724624
724858
|
daemon
|
|
724625
724859
|
};
|
|
724626
724860
|
}
|
|
724861
|
+
operations.register?.(endpoint);
|
|
724627
724862
|
const started = await operations.start(endpoint);
|
|
724863
|
+
if (!started.running || !started.ready || started.endpoint !== endpoint || started.error) {
|
|
724864
|
+
return {
|
|
724865
|
+
level: "error",
|
|
724866
|
+
message: `Indicator did not bind to ${endpoint}. ` + formatIndicatorStatus(started, daemon),
|
|
724867
|
+
status: started,
|
|
724868
|
+
daemon
|
|
724869
|
+
};
|
|
724870
|
+
}
|
|
724628
724871
|
const health = await waitForOnlineHealth(endpoint, operations);
|
|
724629
|
-
const status = { ...started,
|
|
724872
|
+
const status = { ...started, health };
|
|
724630
724873
|
const level = status.error ? "error" : status.running && status.ready && health.kind === "online" ? "info" : "error";
|
|
724631
724874
|
return {
|
|
724632
724875
|
level,
|
|
@@ -724648,6 +724891,7 @@ var init_indicator_command = __esm({
|
|
|
724648
724891
|
init_daemon();
|
|
724649
724892
|
DEFAULT_OPERATIONS = {
|
|
724650
724893
|
ensureDaemon: () => ensureDaemonVersion(),
|
|
724894
|
+
register: (endpoint) => installTrayAutostart(endpoint),
|
|
724651
724895
|
start: (endpoint) => startTray(endpoint),
|
|
724652
724896
|
status: () => getTrayStatus(),
|
|
724653
724897
|
health: (endpoint) => pollTrayHealth(endpoint),
|
|
@@ -792695,6 +792939,17 @@ var init_routes_v1 = __esm({
|
|
|
792695
792939
|
});
|
|
792696
792940
|
|
|
792697
792941
|
// packages/cli/src/api/web-ui.ts
|
|
792942
|
+
function renderRouteHeading(options2) {
|
|
792943
|
+
const signals = options2.signals.map((signal) => `<span class="route-heading__signal">${signal}</span>`).join("");
|
|
792944
|
+
return `<header class="route-heading" data-route-heading="${options2.page}">
|
|
792945
|
+
<div class="route-heading__copy">
|
|
792946
|
+
<span class="route-heading__eyebrow">${options2.eyebrow}</span>
|
|
792947
|
+
<h1 class="route-heading__title">${options2.title}</h1>
|
|
792948
|
+
<p class="route-heading__description">${options2.description}</p>
|
|
792949
|
+
</div>
|
|
792950
|
+
<div class="route-heading__signals" aria-label="Visible operational areas">${signals}</div>
|
|
792951
|
+
</header>`;
|
|
792952
|
+
}
|
|
792698
792953
|
function getWebUI() {
|
|
792699
792954
|
return `<!DOCTYPE html>
|
|
792700
792955
|
<html lang="en">
|
|
@@ -794792,6 +795047,303 @@ html { scrollbar-color: var(--nclp-line-strong) var(--nclp-bg-main); }
|
|
|
794792
795047
|
}
|
|
794793
795048
|
*::-webkit-scrollbar-thumb:hover { background: var(--nclp-yellow); }
|
|
794794
795049
|
|
|
795050
|
+
/* Shared route composition. This carries the useful six-track command-center
|
|
795051
|
+
* grammar from the Voryn persona/call-tree surfaces while keeping Omnius's
|
|
795052
|
+
* existing NOCLIP palette, compact density, and 3px geometry authoritative. */
|
|
795053
|
+
[data-route-page] {
|
|
795054
|
+
min-width: 0;
|
|
795055
|
+
background: var(--nclp-bg-main) !important;
|
|
795056
|
+
}
|
|
795057
|
+
|
|
795058
|
+
.omnius-route-shell {
|
|
795059
|
+
padding: 12px 16px !important;
|
|
795060
|
+
}
|
|
795061
|
+
|
|
795062
|
+
.route-heading {
|
|
795063
|
+
display: grid;
|
|
795064
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
795065
|
+
align-items: end;
|
|
795066
|
+
gap: 12px;
|
|
795067
|
+
margin-bottom: 12px;
|
|
795068
|
+
padding-bottom: 10px;
|
|
795069
|
+
border-bottom: 1px solid var(--nclp-line);
|
|
795070
|
+
min-width: 0;
|
|
795071
|
+
}
|
|
795072
|
+
|
|
795073
|
+
.route-heading__copy { min-width: 0; }
|
|
795074
|
+
.route-heading__eyebrow {
|
|
795075
|
+
display: block;
|
|
795076
|
+
margin-bottom: 3px;
|
|
795077
|
+
color: var(--nclp-yellow);
|
|
795078
|
+
font-size: 9px;
|
|
795079
|
+
font-weight: 800;
|
|
795080
|
+
letter-spacing: 0.14em;
|
|
795081
|
+
text-transform: uppercase;
|
|
795082
|
+
}
|
|
795083
|
+
.route-heading__title {
|
|
795084
|
+
margin: 0 !important;
|
|
795085
|
+
color: var(--nclp-text) !important;
|
|
795086
|
+
font-size: 15px !important;
|
|
795087
|
+
letter-spacing: 0.02em !important;
|
|
795088
|
+
text-transform: none !important;
|
|
795089
|
+
}
|
|
795090
|
+
.route-heading__description {
|
|
795091
|
+
max-width: 760px;
|
|
795092
|
+
margin: 4px 0 0;
|
|
795093
|
+
color: var(--nclp-muted);
|
|
795094
|
+
font-size: 11px;
|
|
795095
|
+
line-height: 1.45;
|
|
795096
|
+
}
|
|
795097
|
+
.route-heading__signals {
|
|
795098
|
+
display: flex;
|
|
795099
|
+
flex-wrap: wrap;
|
|
795100
|
+
justify-content: flex-end;
|
|
795101
|
+
gap: 5px;
|
|
795102
|
+
}
|
|
795103
|
+
.route-heading__signal,
|
|
795104
|
+
.observability-chip {
|
|
795105
|
+
border: 1px solid var(--nclp-line);
|
|
795106
|
+
border-radius: var(--radius-sm);
|
|
795107
|
+
background: var(--nclp-bg-secondary);
|
|
795108
|
+
color: var(--nclp-muted);
|
|
795109
|
+
padding: 3px 6px;
|
|
795110
|
+
font-size: 9px;
|
|
795111
|
+
font-weight: 700;
|
|
795112
|
+
letter-spacing: 0.06em;
|
|
795113
|
+
text-transform: uppercase;
|
|
795114
|
+
white-space: nowrap;
|
|
795115
|
+
}
|
|
795116
|
+
|
|
795117
|
+
.observability-grid {
|
|
795118
|
+
display: grid;
|
|
795119
|
+
grid-template-columns: repeat(6, minmax(0, 1fr));
|
|
795120
|
+
gap: 12px;
|
|
795121
|
+
min-width: 0;
|
|
795122
|
+
align-items: start;
|
|
795123
|
+
}
|
|
795124
|
+
.observability-card {
|
|
795125
|
+
position: relative;
|
|
795126
|
+
min-width: 0;
|
|
795127
|
+
overflow: hidden;
|
|
795128
|
+
border: 1px solid var(--nclp-line);
|
|
795129
|
+
border-radius: var(--radius-lg);
|
|
795130
|
+
background: var(--nclp-card);
|
|
795131
|
+
box-shadow: var(--nclp-shadow);
|
|
795132
|
+
}
|
|
795133
|
+
.observability-card::before {
|
|
795134
|
+
content: "";
|
|
795135
|
+
position: absolute;
|
|
795136
|
+
z-index: 2;
|
|
795137
|
+
top: 0;
|
|
795138
|
+
left: 0;
|
|
795139
|
+
width: 38px;
|
|
795140
|
+
height: 2px;
|
|
795141
|
+
background: var(--nclp-yellow);
|
|
795142
|
+
pointer-events: none;
|
|
795143
|
+
}
|
|
795144
|
+
.observability-card__header {
|
|
795145
|
+
display: grid;
|
|
795146
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
795147
|
+
align-items: center;
|
|
795148
|
+
gap: 10px;
|
|
795149
|
+
min-width: 0;
|
|
795150
|
+
padding: 10px 12px 8px;
|
|
795151
|
+
border-bottom: 1px solid var(--nclp-line);
|
|
795152
|
+
}
|
|
795153
|
+
.observability-card__title {
|
|
795154
|
+
min-width: 0;
|
|
795155
|
+
margin: 0 !important;
|
|
795156
|
+
color: var(--nclp-text) !important;
|
|
795157
|
+
font-size: 10px !important;
|
|
795158
|
+
letter-spacing: 0.1em !important;
|
|
795159
|
+
text-transform: uppercase !important;
|
|
795160
|
+
}
|
|
795161
|
+
.observability-card__meta {
|
|
795162
|
+
overflow: hidden;
|
|
795163
|
+
color: var(--nclp-quiet);
|
|
795164
|
+
font-size: 9px;
|
|
795165
|
+
text-overflow: ellipsis;
|
|
795166
|
+
white-space: nowrap;
|
|
795167
|
+
}
|
|
795168
|
+
.observability-card__body {
|
|
795169
|
+
min-width: 0;
|
|
795170
|
+
padding: 11px 12px 12px;
|
|
795171
|
+
}
|
|
795172
|
+
.observability-card__body--flush { padding: 0; }
|
|
795173
|
+
.observability-span-2 { grid-column: span 2; }
|
|
795174
|
+
.observability-span-3 { grid-column: span 3; }
|
|
795175
|
+
.observability-span-4 { grid-column: span 4; }
|
|
795176
|
+
.observability-span-6 { grid-column: 1 / -1; }
|
|
795177
|
+
|
|
795178
|
+
.observability-facts {
|
|
795179
|
+
display: grid;
|
|
795180
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
795181
|
+
gap: 7px;
|
|
795182
|
+
min-width: 0;
|
|
795183
|
+
}
|
|
795184
|
+
.observability-facts > * {
|
|
795185
|
+
min-width: 0;
|
|
795186
|
+
margin: 0 !important;
|
|
795187
|
+
}
|
|
795188
|
+
.observability-stack {
|
|
795189
|
+
display: grid;
|
|
795190
|
+
gap: 7px;
|
|
795191
|
+
min-width: 0;
|
|
795192
|
+
}
|
|
795193
|
+
.observability-actions {
|
|
795194
|
+
display: flex;
|
|
795195
|
+
flex-wrap: wrap;
|
|
795196
|
+
align-items: center;
|
|
795197
|
+
gap: 7px;
|
|
795198
|
+
}
|
|
795199
|
+
.observability-actions > :is(input, select, textarea) { min-width: 0; }
|
|
795200
|
+
.observability-table-scroll { overflow-x: auto; overscroll-behavior-inline: contain; }
|
|
795201
|
+
|
|
795202
|
+
.route-workspace {
|
|
795203
|
+
display: flex;
|
|
795204
|
+
flex: 1;
|
|
795205
|
+
min-width: 0;
|
|
795206
|
+
min-height: 0;
|
|
795207
|
+
overflow: hidden;
|
|
795208
|
+
}
|
|
795209
|
+
.route-workspace__main {
|
|
795210
|
+
display: flex;
|
|
795211
|
+
flex: 1;
|
|
795212
|
+
flex-direction: column;
|
|
795213
|
+
min-width: 0;
|
|
795214
|
+
min-height: 0;
|
|
795215
|
+
overflow: hidden;
|
|
795216
|
+
}
|
|
795217
|
+
.route-scroll-region {
|
|
795218
|
+
min-width: 0;
|
|
795219
|
+
min-height: 0;
|
|
795220
|
+
overflow: auto;
|
|
795221
|
+
}
|
|
795222
|
+
.agent-command-body { padding: 11px 12px 12px; }
|
|
795223
|
+
.agent-command-body #agent-params { margin: 0; border: 0; background: transparent; }
|
|
795224
|
+
.agent-task-input { min-height: 120px !important; margin: 0 !important; }
|
|
795225
|
+
.agent-events-surface { min-height: 180px; }
|
|
795226
|
+
|
|
795227
|
+
.workspace-switcher { position: relative; flex: 1; min-width: 0; }
|
|
795228
|
+
.workspace-switcher__trigger {
|
|
795229
|
+
display: grid !important;
|
|
795230
|
+
grid-template-columns: 9px minmax(0, 1fr) 12px;
|
|
795231
|
+
align-items: center;
|
|
795232
|
+
gap: 8px;
|
|
795233
|
+
width: 100%;
|
|
795234
|
+
min-width: 0;
|
|
795235
|
+
padding: 5px 7px !important;
|
|
795236
|
+
text-align: left;
|
|
795237
|
+
}
|
|
795238
|
+
#sidebar-brand {
|
|
795239
|
+
overflow: hidden;
|
|
795240
|
+
color: var(--nclp-text) !important;
|
|
795241
|
+
font-size: 11px !important;
|
|
795242
|
+
letter-spacing: 0.01em !important;
|
|
795243
|
+
text-overflow: ellipsis;
|
|
795244
|
+
text-transform: none !important;
|
|
795245
|
+
white-space: nowrap;
|
|
795246
|
+
}
|
|
795247
|
+
.workspace-switcher__chevron { color: var(--nclp-quiet); transition: transform 0.14s ease; }
|
|
795248
|
+
.workspace-switcher__trigger[aria-expanded="true"] .workspace-switcher__chevron { transform: rotate(180deg); }
|
|
795249
|
+
.workspace-picker {
|
|
795250
|
+
position: absolute;
|
|
795251
|
+
z-index: 80;
|
|
795252
|
+
top: calc(100% + 6px);
|
|
795253
|
+
left: 0;
|
|
795254
|
+
width: min(420px, calc(100vw - 24px));
|
|
795255
|
+
max-height: min(520px, 72vh);
|
|
795256
|
+
overflow: hidden;
|
|
795257
|
+
border: 1px solid var(--nclp-line-strong);
|
|
795258
|
+
border-radius: var(--radius-lg);
|
|
795259
|
+
background: var(--nclp-card);
|
|
795260
|
+
box-shadow: 0 22px 56px rgba(0, 0, 0, 0.48);
|
|
795261
|
+
}
|
|
795262
|
+
.workspace-picker__header { padding: 10px; border-bottom: 1px solid var(--nclp-line); }
|
|
795263
|
+
.workspace-picker__header input { width: 100%; padding: 7px 9px; }
|
|
795264
|
+
.workspace-picker__list {
|
|
795265
|
+
display: grid;
|
|
795266
|
+
gap: 3px;
|
|
795267
|
+
max-height: 42vh;
|
|
795268
|
+
overflow-y: auto;
|
|
795269
|
+
padding: 6px;
|
|
795270
|
+
overscroll-behavior: contain;
|
|
795271
|
+
}
|
|
795272
|
+
.workspace-picker__option {
|
|
795273
|
+
display: grid !important;
|
|
795274
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
795275
|
+
align-items: center;
|
|
795276
|
+
gap: 10px;
|
|
795277
|
+
width: 100%;
|
|
795278
|
+
padding: 8px 9px !important;
|
|
795279
|
+
text-align: left;
|
|
795280
|
+
text-transform: none !important;
|
|
795281
|
+
}
|
|
795282
|
+
.workspace-picker__option[aria-selected="true"] {
|
|
795283
|
+
border-color: var(--nclp-yellow) !important;
|
|
795284
|
+
background: var(--nclp-amber-soft) !important;
|
|
795285
|
+
}
|
|
795286
|
+
.workspace-picker__option.is-keyboard-active {
|
|
795287
|
+
border-color: var(--nclp-yellow) !important;
|
|
795288
|
+
color: var(--nclp-yellow) !important;
|
|
795289
|
+
}
|
|
795290
|
+
.workspace-picker__name,
|
|
795291
|
+
.workspace-picker__path { display: block; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
795292
|
+
.workspace-picker__name { color: var(--nclp-text); font-size: 11px; font-weight: 700; }
|
|
795293
|
+
.workspace-picker__path { margin-top: 2px; color: var(--nclp-quiet); font-size: 9px; font-weight: 500; }
|
|
795294
|
+
.workspace-picker__check { color: var(--nclp-yellow); }
|
|
795295
|
+
.workspace-picker__empty { padding: 18px 10px; color: var(--nclp-quiet); font-size: 10px; text-align: center; }
|
|
795296
|
+
.workspace-picker__footer { display: flex; justify-content: flex-end; padding: 7px 9px; border-top: 1px solid var(--nclp-line); }
|
|
795297
|
+
#omnius-sidebar[data-collapsed="true"] .workspace-switcher { display: none; }
|
|
795298
|
+
|
|
795299
|
+
/* Generate keeps its purpose-built rails, but uses the same six-track span
|
|
795300
|
+
* proportions as the overview grids: 1/6 controls, 3/6 work, 2/6 signal. */
|
|
795301
|
+
.hud-workbench[data-layout="generate-workbench"] {
|
|
795302
|
+
grid-template-columns: repeat(6, minmax(0, 1fr));
|
|
795303
|
+
}
|
|
795304
|
+
.hud-workbench[data-layout="generate-workbench"] > #gen-mode-rail { grid-column: span 1; }
|
|
795305
|
+
.hud-workbench[data-layout="generate-workbench"] > main { grid-column: span 3; min-width: 0; }
|
|
795306
|
+
.hud-workbench[data-layout="generate-workbench"] > .hud-side-output { grid-column: span 2; min-width: 0; }
|
|
795307
|
+
|
|
795308
|
+
@media (max-width: 1180px) {
|
|
795309
|
+
.hud-workbench[data-layout="generate-workbench"] { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
795310
|
+
.hud-workbench[data-layout="generate-workbench"] > #gen-mode-rail,
|
|
795311
|
+
.hud-workbench[data-layout="generate-workbench"] > main { grid-column: span 1; }
|
|
795312
|
+
.hud-workbench[data-layout="generate-workbench"] > .hud-side-output { grid-column: 1 / -1; }
|
|
795313
|
+
}
|
|
795314
|
+
|
|
795315
|
+
@media (max-width: 820px) {
|
|
795316
|
+
.hud-workbench[data-layout="generate-workbench"] { grid-template-columns: minmax(0, 1fr); }
|
|
795317
|
+
.hud-workbench[data-layout="generate-workbench"] > #gen-mode-rail,
|
|
795318
|
+
.hud-workbench[data-layout="generate-workbench"] > main,
|
|
795319
|
+
.hud-workbench[data-layout="generate-workbench"] > .hud-side-output { grid-column: 1; }
|
|
795320
|
+
}
|
|
795321
|
+
|
|
795322
|
+
@media (max-width: 920px) {
|
|
795323
|
+
.observability-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
795324
|
+
.observability-span-2,
|
|
795325
|
+
.observability-span-3 { grid-column: span 1; }
|
|
795326
|
+
.observability-span-4,
|
|
795327
|
+
.observability-span-6 { grid-column: 1 / -1; }
|
|
795328
|
+
.observability-facts { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
795329
|
+
}
|
|
795330
|
+
|
|
795331
|
+
@media (max-width: 760px) {
|
|
795332
|
+
.omnius-route-shell { padding: 10px !important; }
|
|
795333
|
+
.route-heading { grid-template-columns: 1fr; align-items: start; }
|
|
795334
|
+
.route-heading__signals { justify-content: flex-start; }
|
|
795335
|
+
.observability-grid { grid-template-columns: minmax(0, 1fr); gap: 10px; }
|
|
795336
|
+
.observability-span-2,
|
|
795337
|
+
.observability-span-3,
|
|
795338
|
+
.observability-span-4,
|
|
795339
|
+
.observability-span-6 { grid-column: 1; }
|
|
795340
|
+
.observability-facts { grid-template-columns: minmax(0, 1fr); }
|
|
795341
|
+
.observability-card__header { grid-template-columns: minmax(0, 1fr); }
|
|
795342
|
+
.observability-card__meta { white-space: normal; }
|
|
795343
|
+
.observability-actions > :is(button, input, select) { max-width: 100%; }
|
|
795344
|
+
.workspace-picker { position: fixed; top: 54px; right: 12px; left: 12px; width: auto; }
|
|
795345
|
+
}
|
|
795346
|
+
|
|
794795
795347
|
@media (prefers-reduced-motion: reduce) {
|
|
794796
795348
|
*, *::before, *::after {
|
|
794797
795349
|
animation-duration: 0.01ms !important;
|
|
@@ -794820,8 +795372,22 @@ html { scrollbar-color: var(--nclp-line-strong) var(--nclp-bg-main); }
|
|
|
794820
795372
|
<button id="sidebar-toggle" onclick="toggleSidebar()" title="Toggle sidebar (Cmd/Ctrl+B)" style="background:transparent;border:none;color:var(--color-fg-muted);padding:4px;border-radius:var(--radius-sm);cursor:pointer;display:flex;align-items:center;justify-content:center;width:28px;height:28px">
|
|
794821
795373
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M9 3v18"/></svg>
|
|
794822
795374
|
</button>
|
|
794823
|
-
<
|
|
794824
|
-
|
|
795375
|
+
<div class="workspace-switcher">
|
|
795376
|
+
<button id="workspace-switcher-trigger" class="workspace-switcher__trigger" type="button" aria-haspopup="dialog" aria-expanded="false" aria-controls="workspace-picker" onclick="toggleWorkspacePicker(event)" title="Switch workspace">
|
|
795377
|
+
<span id="sidebar-brand-dot" title="Connection status" style="width:9px;height:9px;border-radius:999px;background:#f59e0b;box-shadow:0 0 0 2px rgba(245,158,11,.16);flex:0 0 auto"></span>
|
|
795378
|
+
<span id="sidebar-brand">No project</span>
|
|
795379
|
+
<svg class="workspace-switcher__chevron" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" aria-hidden="true"><path d="m6 9 6 6 6-6"/></svg>
|
|
795380
|
+
</button>
|
|
795381
|
+
<section id="workspace-picker" class="workspace-picker" role="dialog" aria-label="Select workspace" style="display:none">
|
|
795382
|
+
<div class="workspace-picker__header">
|
|
795383
|
+
<input id="workspace-picker-search" type="search" placeholder="Filter workspaces..." autocomplete="off" aria-label="Filter workspaces" oninput="filterWorkspacePicker(this.value)" onkeydown="handleWorkspacePickerKeys(event)">
|
|
795384
|
+
</div>
|
|
795385
|
+
<div id="workspace-picker-list" class="workspace-picker__list" role="listbox" aria-label="Available workspaces"></div>
|
|
795386
|
+
<div class="workspace-picker__footer">
|
|
795387
|
+
<button type="button" onclick="closeWorkspacePicker(); switchTab('projects')">Manage workspaces</button>
|
|
795388
|
+
</div>
|
|
795389
|
+
</section>
|
|
795390
|
+
</div>
|
|
794825
795391
|
</div>
|
|
794826
795392
|
|
|
794827
795393
|
<!-- New chat button -->
|
|
@@ -794857,35 +795423,35 @@ html { scrollbar-color: var(--nclp-line-strong) var(--nclp-bg-main); }
|
|
|
794857
795423
|
|
|
794858
795424
|
<!-- Section nav (the legacy tabs migrated into the sidebar footer) -->
|
|
794859
795425
|
<div id="sidebar-nav" class="sb-label" style="border-top:1px solid var(--color-border);padding:8px;display:flex;flex-direction:column;gap:2px">
|
|
794860
|
-
<button class="sb-nav" data-tab="chat"
|
|
795426
|
+
<button class="sb-nav" data-tab="chat" data-route="/chat" aria-controls="chat-container" aria-current="page" onclick="switchTab('chat')" title="Chat">
|
|
794861
795427
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
|
|
794862
795428
|
<span class="sb-label">Chat</span>
|
|
794863
795429
|
</button>
|
|
794864
|
-
<button class="sb-nav" data-tab="agent"
|
|
795430
|
+
<button class="sb-nav" data-tab="agent" data-route="/agent" aria-controls="agent-panel" onclick="switchTab('agent')" title="Agent">
|
|
794865
795431
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2 4 7v10l8 5 8-5V7l-8-5z"/><path d="M12 22V12"/><path d="m4 7 8 5 8-5"/></svg>
|
|
794866
795432
|
<span class="sb-label">Agent</span>
|
|
794867
795433
|
</button>
|
|
794868
|
-
<button class="sb-nav" data-tab="voice"
|
|
795434
|
+
<button class="sb-nav" data-tab="voice" data-route="/voice" aria-controls="voice-panel" onclick="switchTab('voice')" title="Voice">
|
|
794869
795435
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" y1="19" x2="12" y2="23"/><line x1="8" y1="23" x2="16" y2="23"/></svg>
|
|
794870
795436
|
<span class="sb-label">Voice</span>
|
|
794871
795437
|
</button>
|
|
794872
|
-
<button class="sb-nav" data-tab="generate" onclick="switchTab('generate')" title="Generate media">
|
|
795438
|
+
<button class="sb-nav" data-tab="generate" data-route="/generate" aria-controls="generate-panel" onclick="switchTab('generate')" title="Generate media">
|
|
794873
795439
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>
|
|
794874
795440
|
<span class="sb-label">Generate</span>
|
|
794875
795441
|
</button>
|
|
794876
|
-
<button class="sb-nav" data-tab="projects" onclick="switchTab('projects')" title="Projects">
|
|
795442
|
+
<button class="sb-nav" data-tab="projects" data-route="/projects" aria-controls="projects-panel" onclick="switchTab('projects')" title="Projects">
|
|
794877
795443
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></svg>
|
|
794878
795444
|
<span class="sb-label">Projects</span>
|
|
794879
795445
|
</button>
|
|
794880
|
-
<button class="sb-nav" data-tab="jobs"
|
|
795446
|
+
<button class="sb-nav" data-tab="jobs" data-route="/dashboard" aria-controls="jobs-panel" onclick="switchTab('jobs')" title="Dashboard">
|
|
794881
795447
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="9"/><rect x="14" y="3" width="7" height="5"/><rect x="14" y="12" width="7" height="9"/><rect x="3" y="16" width="7" height="5"/></svg>
|
|
794882
795448
|
<span class="sb-label">Dashboard</span>
|
|
794883
795449
|
</button>
|
|
794884
|
-
<button class="sb-nav" data-tab="activity" onclick="switchTab('activity')" title="Activity">
|
|
795450
|
+
<button class="sb-nav" data-tab="activity" data-route="/activity" aria-controls="activity-panel" onclick="switchTab('activity')" title="Activity">
|
|
794885
795451
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg>
|
|
794886
795452
|
<span class="sb-label">Activity</span>
|
|
794887
795453
|
</button>
|
|
794888
|
-
<button class="sb-nav" data-tab="config"
|
|
795454
|
+
<button class="sb-nav" data-tab="config" data-route="/settings" aria-controls="config-panel" onclick="switchTab('config')" title="Settings">
|
|
794889
795455
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
|
794890
795456
|
<span class="sb-label">Settings</span>
|
|
794891
795457
|
</button>
|
|
@@ -794911,8 +795477,7 @@ html { scrollbar-color: var(--nclp-line-strong) var(--nclp-bg-main); }
|
|
|
794911
795477
|
<div id="omnius-main" style="flex:1;display:flex;flex-direction:column;min-width:0;overflow:hidden;background:var(--color-bg)">
|
|
794912
795478
|
|
|
794913
795479
|
<div id="header">
|
|
794914
|
-
<span class="
|
|
794915
|
-
<span class="status" id="status">connecting...</span>
|
|
795480
|
+
<span class="status" id="status" aria-live="polite" style="display:none">connecting...</span>
|
|
794916
795481
|
<span id="update-btn" style="display:none;background:#0f3f3d;border:1px solid var(--color-brand);color:var(--color-brand);padding:2px 8px;border-radius:3px;font-family:inherit;font-size:0.6rem;cursor:pointer" onclick="doUpdate()">update</span>
|
|
794917
795482
|
<select id="model-select"><option>loading...</option></select>
|
|
794918
795483
|
<button class="key-btn" id="files-btn" onclick="toggleFilesForActiveTab()" title="Toggle workspace sidebar (chat or agent depending on active tab)">files</button>
|
|
@@ -794954,7 +795519,9 @@ html { scrollbar-color: var(--nclp-line-strong) var(--nclp-bg-main); }
|
|
|
794954
795519
|
<span id="token-counter" style="font-size:0.6rem;color:var(--color-fg-faint);display:flex;flex-flow:column;justify-content:center;padding:0 6px">0 tokens</span>
|
|
794955
795520
|
</div>
|
|
794956
795521
|
</div>
|
|
794957
|
-
<div id="chat-container" style="display:flex;flex:1;overflow:hidden">
|
|
795522
|
+
<div id="chat-container" class="omnius-route-shell" data-route-page="chat" data-route-tab="chat" style="display:flex;flex:1;overflow:hidden;flex-direction:column">
|
|
795523
|
+
${renderRouteHeading({ page: "chat", eyebrow: "Conversation", title: "Chat workspace", description: "Keep the active session, execution plan, workspace context, and conversation visible as one operational surface.", signals: ["session", "plan", "live context"] })}
|
|
795524
|
+
<div class="route-workspace" data-layout="chat-split">
|
|
794958
795525
|
<div id="workspace-sidebar" style="display:none;width:250px;background:var(--color-bg-elevated);border-right:1px solid var(--color-bg-input);overflow-y:auto;padding:8px;flex-shrink:0;font-size:0.7rem">
|
|
794959
795526
|
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px">
|
|
794960
795527
|
<span style="color:var(--color-brand);font-size:0.7rem;font-weight:bold">Workspace</span>
|
|
@@ -794964,7 +795531,7 @@ html { scrollbar-color: var(--nclp-line-strong) var(--nclp-bg-main); }
|
|
|
794964
795531
|
<div id="workspace-tree" style="color:var(--color-fg)"></div>
|
|
794965
795532
|
</div>
|
|
794966
795533
|
<!-- Vertical column: session topbar + checklist + conversation -->
|
|
794967
|
-
<div
|
|
795534
|
+
<div class="route-workspace__main">
|
|
794968
795535
|
<!-- WO-TASK-02 — Chat session topbar (separate storage from agent runs) -->
|
|
794969
795536
|
<div class="session-topbar" id="chat-session-topbar">
|
|
794970
795537
|
<span class="topbar-label">chat session</span>
|
|
@@ -794981,8 +795548,10 @@ html { scrollbar-color: var(--nclp-line-strong) var(--nclp-bg-main); }
|
|
|
794981
795548
|
</div>
|
|
794982
795549
|
<div id="conversation" style="flex:1;overflow-y:auto;padding:12px 16px;display:flex;flex-direction:column;gap:4px;min-height:0"></div>
|
|
794983
795550
|
</div>
|
|
795551
|
+
</div>
|
|
794984
795552
|
</div>
|
|
794985
|
-
<div id="agent-panel" style="display:none;flex:1;overflow:hidden;flex-direction:column">
|
|
795553
|
+
<div id="agent-panel" class="omnius-route-shell" data-route-page="agent" data-route-tab="agent" style="display:none;flex:1;overflow:hidden;flex-direction:column">
|
|
795554
|
+
${renderRouteHeading({ page: "agent", eyebrow: "Execution", title: "Agent command center", description: "Define the task contract, inspect the run configuration, and follow the event stream without losing workspace context.", signals: ["task contract", "run profile", "event stream"] })}
|
|
794986
795555
|
<!-- WO-TASK-02 — Agent run topbar (separate storage from chat sessions) -->
|
|
794987
795556
|
<div class="session-topbar" id="agent-session-topbar">
|
|
794988
795557
|
<span class="topbar-label">agent run</span>
|
|
@@ -794994,7 +795563,7 @@ html { scrollbar-color: var(--nclp-line-strong) var(--nclp-bg-main); }
|
|
|
794994
795563
|
<button onclick="refreshAgentRunSelect()" title="Refresh the run list">⟳</button>
|
|
794995
795564
|
</div>
|
|
794996
795565
|
<!-- Body row: agent workspace sidebar (isolated from chat) | scrollable form -->
|
|
794997
|
-
<div
|
|
795566
|
+
<div class="route-workspace" data-layout="agent-form">
|
|
794998
795567
|
<div id="agent-workspace-sidebar" style="display:none;width:250px;background:var(--color-bg-elevated);border-right:1px solid var(--color-bg-input);overflow-y:auto;padding:8px;flex-shrink:0;font-size:0.7rem">
|
|
794999
795568
|
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px">
|
|
795000
795569
|
<span style="color:var(--color-brand);font-size:0.7rem;font-weight:bold">Agent Workspace</span>
|
|
@@ -795003,10 +795572,12 @@ html { scrollbar-color: var(--nclp-line-strong) var(--nclp-bg-main); }
|
|
|
795003
795572
|
<div id="agent-workspace-cwd" style="color:var(--color-fg-faint);font-size:0.6rem;margin-bottom:8px;word-break:break-all"></div>
|
|
795004
795573
|
<div id="agent-workspace-tree" style="color:var(--color-fg)"></div>
|
|
795005
795574
|
</div>
|
|
795006
|
-
<div style="flex:1;
|
|
795575
|
+
<div class="route-scroll-region" style="flex:1;padding:0 0 4px;">
|
|
795576
|
+
<div class="observability-grid">
|
|
795007
795577
|
<!-- Parameter form: all the dials for POST /v1/run -->
|
|
795008
|
-
<
|
|
795009
|
-
<
|
|
795578
|
+
<section class="observability-card observability-span-4" data-module="agent-parameters">
|
|
795579
|
+
<header class="observability-card__header"><h2 class="observability-card__title">Run configuration</h2><span class="observability-card__meta">POST /v1/run</span></header>
|
|
795580
|
+
<div id="agent-params" class="observability-card__body agent-command-body" style="font-size:0.68rem">
|
|
795010
795581
|
<div style="display:grid;grid-template-columns:120px 1fr;gap:6px 12px;align-items:center">
|
|
795011
795582
|
<label style="color:var(--color-fg-muted)">Working dir</label>
|
|
795012
795583
|
<div style="display:flex;gap:4px;align-items:center">
|
|
@@ -795037,36 +795608,75 @@ html { scrollbar-color: var(--nclp-line-strong) var(--nclp-bg-main); }
|
|
|
795037
795608
|
<label style="color:var(--color-fg-muted)">Isolate workspace</label>
|
|
795038
795609
|
<div><input type="checkbox" id="agent-isolate" style="accent-color:var(--color-brand)"> <span style="color:var(--color-fg-faint);font-size:0.6rem">fresh temp dir per run</span></div>
|
|
795039
795610
|
</div>
|
|
795040
|
-
|
|
795611
|
+
</div>
|
|
795612
|
+
</section>
|
|
795041
795613
|
|
|
795042
|
-
<
|
|
795043
|
-
|
|
795614
|
+
<section class="observability-card observability-span-2" data-module="agent-task-contract">
|
|
795615
|
+
<header class="observability-card__header"><h2 class="observability-card__title">Task contract</h2><span class="observability-card__meta">persona + controls</span></header>
|
|
795616
|
+
<div class="observability-card__body observability-stack">
|
|
795617
|
+
<textarea id="agent-task" class="agent-task-input" placeholder="Describe the task for the agent..." style="width:100%;background:var(--color-bg-input);border:1px solid var(--color-border);border-radius:3px;padding:8px 12px;color:var(--color-fg);font-family:inherit;font-size:0.82rem;resize:vertical;outline:none"></textarea>
|
|
795618
|
+
<div class="observability-actions">
|
|
795044
795619
|
<select id="agent-profile" style="background:var(--color-bg-input);border:1px solid var(--color-border);color:var(--color-fg);padding:4px 8px;border-radius:3px;font-family:inherit;font-size:0.7rem"><option value="">no profile</option></select>
|
|
795045
795620
|
<button onclick="submitAgentTask()" id="agent-submit" style="background:var(--color-bg-input);border:1px solid var(--color-border);color:var(--color-brand);padding:6px 16px;border-radius:3px;font-family:inherit;font-size:0.75rem;cursor:pointer">run task</button>
|
|
795046
795621
|
<button onclick="abortAgentTask()" id="agent-abort" style="display:none;background:var(--color-bg-input);border:1px solid var(--color-error);color:var(--color-error);padding:6px 16px;border-radius:3px;font-family:inherit;font-size:0.75rem;cursor:pointer">abort</button>
|
|
795047
795622
|
</div>
|
|
795048
|
-
|
|
795623
|
+
</div>
|
|
795624
|
+
</section>
|
|
795625
|
+
|
|
795626
|
+
<section class="observability-card observability-span-6" data-module="agent-event-stream">
|
|
795627
|
+
<header class="observability-card__header"><h2 class="observability-card__title">Event stream</h2><span class="observability-card__meta">tools · stdout · completion</span></header>
|
|
795628
|
+
<div id="agent-events" class="observability-card__body agent-events-surface" style="font-size:0.78rem;line-height:1.5"></div>
|
|
795629
|
+
</section>
|
|
795630
|
+
</div>
|
|
795049
795631
|
</div><!-- /scrollable agent body -->
|
|
795050
795632
|
</div><!-- /agent body row (sidebar | form) -->
|
|
795051
795633
|
</div>
|
|
795052
|
-
<div id="jobs-panel" style="display:none;flex:1;overflow-y:auto
|
|
795053
|
-
|
|
795054
|
-
<div
|
|
795055
|
-
|
|
795056
|
-
|
|
795057
|
-
|
|
795058
|
-
|
|
795059
|
-
|
|
795634
|
+
<div id="jobs-panel" class="omnius-route-shell" data-route-page="dashboard" data-route-tab="jobs" style="display:none;flex:1;overflow-y:auto">
|
|
795635
|
+
${renderRouteHeading({ page: "dashboard", eyebrow: "Operations", title: "System dashboard", description: "Health, active execution, scheduled automation, services, usage, and job history share one scan-friendly command grid.", signals: ["health", "daemons", "usage", "history"] })}
|
|
795636
|
+
<div class="observability-grid" data-layout="dashboard-metrics">
|
|
795637
|
+
<section class="observability-card observability-span-6" data-module="runtime-health">
|
|
795638
|
+
<header class="observability-card__header"><h2 class="observability-card__title">Runtime health</h2><span class="observability-card__meta">live daemon telemetry</span></header>
|
|
795639
|
+
<div id="dashboard-health" class="observability-card__body observability-facts"></div>
|
|
795640
|
+
</section>
|
|
795641
|
+
<section class="observability-card observability-span-2" data-module="active-processes">
|
|
795642
|
+
<header class="observability-card__header"><h2 class="observability-card__title">Active processes</h2><span class="observability-card__meta">agent runs</span></header>
|
|
795643
|
+
<div id="dashboard-daemons" class="observability-card__body"></div>
|
|
795644
|
+
</section>
|
|
795645
|
+
<section class="observability-card observability-span-4" data-module="scheduled-tasks">
|
|
795646
|
+
<header class="observability-card__header"><h2 class="observability-card__title">Scheduled tasks</h2><span class="observability-card__meta">automation control</span></header>
|
|
795647
|
+
<div id="dashboard-scheduled" class="observability-card__body"></div>
|
|
795648
|
+
</section>
|
|
795649
|
+
<section class="observability-card observability-span-3" data-module="services">
|
|
795650
|
+
<header class="observability-card__header"><h2 class="observability-card__title">Services</h2><span class="observability-card__meta">systemd --user</span></header>
|
|
795651
|
+
<div id="dashboard-services" class="observability-card__body"></div>
|
|
795652
|
+
</section>
|
|
795653
|
+
<section class="observability-card observability-span-3" data-module="token-usage">
|
|
795654
|
+
<header class="observability-card__header"><h2 class="observability-card__title">Token usage</h2><span class="observability-card__meta">persistent by provider</span></header>
|
|
795655
|
+
<div id="dashboard-usage" class="observability-card__body"></div>
|
|
795656
|
+
</section>
|
|
795657
|
+
<section class="observability-card observability-span-6" data-module="job-history">
|
|
795658
|
+
<header class="observability-card__header"><h2 class="observability-card__title">Job history</h2><span class="observability-card__meta">latest 20 runs</span></header>
|
|
795659
|
+
<div class="observability-card__body observability-table-scroll"><div id="jobs-list" style="font-size:0.78rem"></div></div>
|
|
795660
|
+
</section>
|
|
795661
|
+
</div>
|
|
795060
795662
|
</div>
|
|
795061
|
-
<div id="config-panel" style="display:none;flex:1;overflow-y:auto
|
|
795062
|
-
|
|
795063
|
-
<div
|
|
795064
|
-
<
|
|
795065
|
-
|
|
795663
|
+
<div id="config-panel" class="omnius-route-shell" data-route-page="settings" data-route-tab="config" style="display:none;flex:1;overflow-y:auto">
|
|
795664
|
+
${renderRouteHeading({ page: "settings", eyebrow: "Control plane", title: "Settings", description: "Inspect daemon configuration, choose models and providers, manage profiles, and export session data from one modular surface.", signals: ["runtime", "models", "providers", "profiles"] })}
|
|
795665
|
+
<div class="observability-grid" data-layout="config-form">
|
|
795666
|
+
<section class="observability-card observability-span-2" data-module="server-configuration">
|
|
795667
|
+
<header class="observability-card__header"><h2 class="observability-card__title">Server configuration</h2><span class="observability-card__meta">read only</span></header>
|
|
795668
|
+
<div id="config-content" class="observability-card__body" style="font-size:0.78rem"></div>
|
|
795669
|
+
</section>
|
|
795670
|
+
<section class="observability-card observability-span-4" data-module="active-model">
|
|
795671
|
+
<header class="observability-card__header"><h2 class="observability-card__title">Model</h2><span class="observability-card__meta">active inference target</span></header>
|
|
795672
|
+
<div class="observability-card__body observability-actions">
|
|
795066
795673
|
<select id="config-model-select" style="background:var(--color-bg-input);border:1px solid var(--color-border);color:var(--color-fg);padding:4px 8px;border-radius:3px;font-family:inherit;font-size:0.7rem;flex:1"></select>
|
|
795067
795674
|
<button onclick="switchModel()" style="background:var(--color-bg-input);border:1px solid var(--color-border);color:var(--color-brand);padding:4px 12px;border-radius:3px;font-family:inherit;font-size:0.7rem;cursor:pointer">switch</button>
|
|
795068
|
-
|
|
795069
|
-
|
|
795675
|
+
</div>
|
|
795676
|
+
</section>
|
|
795677
|
+
<section class="observability-card observability-span-6" data-module="inference-provider">
|
|
795678
|
+
<header class="observability-card__header"><h2 class="observability-card__title">Inference provider</h2><span class="observability-card__meta">connection + authentication</span></header>
|
|
795679
|
+
<div class="observability-card__body">
|
|
795070
795680
|
<div id="config-endpoint" style="font-size:0.78rem;color:var(--color-fg-muted);margin-bottom:8px"></div>
|
|
795071
795681
|
<div id="config-recent-providers-section" style="display:none;margin-bottom:12px">
|
|
795072
795682
|
<div style="font-size:0.66rem;color:var(--color-fg-muted);margin-bottom:6px;font-weight:500">Previously connected</div>
|
|
@@ -795082,48 +795692,71 @@ html { scrollbar-color: var(--nclp-line-strong) var(--nclp-bg-main); }
|
|
|
795082
795692
|
</select>
|
|
795083
795693
|
<button type="submit" style="background:var(--color-bg-input);border:1px solid var(--color-border);color:var(--color-brand);padding:4px 12px;border-radius:3px;font-family:inherit;font-size:0.7rem;cursor:pointer">set</button>
|
|
795084
795694
|
</form>
|
|
795085
|
-
|
|
795086
|
-
|
|
795087
|
-
<
|
|
795088
|
-
|
|
795695
|
+
</div>
|
|
795696
|
+
</section>
|
|
795697
|
+
<section class="observability-card observability-span-3" data-module="agent-profiles">
|
|
795698
|
+
<header class="observability-card__header"><h2 class="observability-card__title">Profiles</h2><span class="observability-card__meta">agent personas</span></header>
|
|
795699
|
+
<div id="config-profiles" class="observability-card__body" style="font-size:0.78rem"></div>
|
|
795700
|
+
</section>
|
|
795701
|
+
<section class="observability-card observability-span-3" data-module="conversation-export">
|
|
795702
|
+
<header class="observability-card__header"><h2 class="observability-card__title">Export conversation</h2><span class="observability-card__meta">portable session record</span></header>
|
|
795703
|
+
<div class="observability-card__body observability-actions">
|
|
795089
795704
|
<button onclick="exportChat('md')" style="background:var(--color-bg-input);border:1px solid var(--color-border);color:var(--color-brand);padding:4px 12px;border-radius:3px;font-family:inherit;font-size:0.7rem;cursor:pointer">markdown</button>
|
|
795090
795705
|
<button onclick="exportChat('json')" style="background:var(--color-bg-input);border:1px solid var(--color-border);color:var(--color-brand);padding:4px 12px;border-radius:3px;font-family:inherit;font-size:0.7rem;cursor:pointer">JSON</button>
|
|
795091
795706
|
</div>
|
|
795707
|
+
</section>
|
|
795708
|
+
</div>
|
|
795092
795709
|
</div>
|
|
795093
|
-
<div id="activity-panel" style="display:none;flex:1;overflow-y:auto
|
|
795094
|
-
|
|
795095
|
-
<div
|
|
795710
|
+
<div id="activity-panel" class="omnius-route-shell" data-route-page="activity" data-route-tab="activity" style="display:none;flex:1;overflow-y:auto">
|
|
795711
|
+
${renderRouteHeading({ page: "activity", eyebrow: "Audit", title: "Recent activity", description: "Review request outcomes, routes, latency, and actor context as a continuous operational record.", signals: ["status", "latency", "actor"] })}
|
|
795712
|
+
<div class="observability-grid" data-layout="activity-ledger">
|
|
795713
|
+
<section class="observability-card observability-span-6" data-module="audit-log">
|
|
795714
|
+
<header class="observability-card__header"><h2 class="observability-card__title">Audit log</h2><span class="observability-card__meta">latest 50 records</span></header>
|
|
795715
|
+
<div id="activity-feed" class="observability-card__body" style="font-size:0.72rem"></div>
|
|
795716
|
+
</section>
|
|
795717
|
+
</div>
|
|
795096
795718
|
</div>
|
|
795097
|
-
<div id="projects-panel" style="display:none;flex:1;overflow-y:auto
|
|
795098
|
-
|
|
795099
|
-
|
|
795100
|
-
|
|
795719
|
+
<div id="projects-panel" class="omnius-route-shell" data-route-page="projects" data-route-tab="projects" style="display:none;flex:1;overflow-y:auto">
|
|
795720
|
+
${renderRouteHeading({ page: "projects", eyebrow: "Workspace registry", title: "Projects", description: "Find, register, and activate workspaces while keeping the current execution context unambiguous.", signals: ["active context", "registry", "discovery"] })}
|
|
795721
|
+
<div class="observability-grid" data-layout="project-toolbar">
|
|
795722
|
+
<section class="observability-card observability-span-6" data-module="workspace-tools">
|
|
795723
|
+
<header class="observability-card__header"><h2 class="observability-card__title">Workspace tools</h2><span class="observability-card__meta">filter · scan · register</span></header>
|
|
795724
|
+
<div class="observability-card__body observability-actions">
|
|
795101
795725
|
<input id="project-search" type="text" placeholder="Filter projects…" style="background:var(--color-bg);border:1px solid var(--color-border);color:var(--color-fg);padding:3px 8px;border-radius:3px;font-size:0.65rem;width:140px;font-family:inherit" oninput="filterProjects()">
|
|
795102
795726
|
<button id="scan-btn" onclick="scanProjects()" style="background:var(--color-bg);border:1px solid var(--color-border);color:var(--color-fg-subtle);padding:3px 10px;border-radius:3px;font-size:0.65rem;cursor:pointer;font-family:inherit">🔍 Scan</button>
|
|
795103
795727
|
<button id="add-project-btn" onclick="showAddProject()" style="background:var(--color-bg);border:1px solid var(--color-border);color:var(--color-fg-subtle);padding:3px 10px;border-radius:3px;font-size:0.65rem;cursor:pointer;font-family:inherit">+ Add</button>
|
|
795104
795728
|
</div>
|
|
795105
|
-
</
|
|
795106
|
-
<
|
|
795107
|
-
|
|
795729
|
+
</section>
|
|
795730
|
+
<section class="observability-card observability-span-2" data-module="current-workspace">
|
|
795731
|
+
<header class="observability-card__header"><h2 class="observability-card__title">Current workspace</h2><span class="observability-card__meta">active context</span></header>
|
|
795732
|
+
<div id="projects-current" class="observability-card__body" style="font-size:0.72rem;color:var(--color-fg)">
|
|
795108
795733
|
<span style="color:var(--color-fg-subtle)">current:</span> <span id="projects-current-name" style="color:var(--color-brand)">(none)</span>
|
|
795109
795734
|
<span id="projects-current-root" style="color:var(--color-fg-subtle);margin-left:8px"></span>
|
|
795110
795735
|
</div>
|
|
795111
|
-
|
|
795112
|
-
<
|
|
795113
|
-
<
|
|
795736
|
+
</section>
|
|
795737
|
+
<section class="observability-card observability-span-4" data-module="workspace-registry">
|
|
795738
|
+
<header class="observability-card__header"><h2 class="observability-card__title">Registered workspaces</h2><span class="observability-card__meta">select to activate</span></header>
|
|
795739
|
+
<div id="projects-list" class="observability-card__body" style="font-size:0.72rem"></div>
|
|
795740
|
+
</section>
|
|
795741
|
+
<section id="add-project-form" class="observability-card observability-span-6" data-module="register-workspace" style="display:none">
|
|
795742
|
+
<header class="observability-card__header"><h2 class="observability-card__title">Add project</h2><span class="observability-card__meta">absolute filesystem path</span></header>
|
|
795743
|
+
<div class="observability-card__body">
|
|
795114
795744
|
<input id="add-project-path" type="text" placeholder="/absolute/path/to/project" style="background:var(--color-bg-input);border:1px solid var(--color-border);color:var(--color-fg);padding:4px 8px;border-radius:3px;font-size:0.65rem;width:100%;margin-bottom:6px;font-family:inherit">
|
|
795115
|
-
<div
|
|
795745
|
+
<div class="observability-actions">
|
|
795116
795746
|
<button onclick="addProject()" style="background:var(--color-brand);border:none;color:var(--color-accent-fg);padding:4px 12px;border-radius:3px;font-size:0.65rem;cursor:pointer;font-family:inherit">Register</button>
|
|
795117
795747
|
<button onclick="hideAddProject()" style="background:var(--color-bg);border:1px solid var(--color-border);color:var(--color-fg-subtle);padding:4px 12px;border-radius:3px;font-size:0.65rem;cursor:pointer;font-family:inherit">Cancel</button>
|
|
795118
795748
|
</div>
|
|
795749
|
+
</div>
|
|
795750
|
+
</section>
|
|
795119
795751
|
</div>
|
|
795120
795752
|
</div>
|
|
795121
795753
|
|
|
795122
795754
|
<!-- Generate tab — global media generation (image/video/audio/music) backed by
|
|
795123
795755
|
the unified ~/.omnius model store. Loads models from /v1/media/models,
|
|
795124
795756
|
generates via /v1/media/<kind>, and browses the global gallery. -->
|
|
795125
|
-
<div id="generate-panel" style="display:none;flex:1;overflow-y:auto
|
|
795126
|
-
|
|
795757
|
+
<div id="generate-panel" class="omnius-route-shell" data-route-page="generate" data-route-tab="generate" style="display:none;flex:1;overflow-y:auto">
|
|
795758
|
+
${renderRouteHeading({ page: "generate", eyebrow: "Media operations", title: "Generate", description: "Move between synthesis, perception, model storage, evidence output, and the media archive without leaving the workbench.", signals: ["synthesis", "analysis", "signal", "archive"] })}
|
|
795759
|
+
<div class="hud-workbench" data-layout="generate-workbench">
|
|
795127
795760
|
<aside class="hud-panel hud-panel--slant hud-rail" id="gen-mode-rail">
|
|
795128
795761
|
<div class="hud-panel__chrome" aria-hidden="true"></div>
|
|
795129
795762
|
<header class="hud-panel__header">
|
|
@@ -795292,9 +795925,12 @@ html { scrollbar-color: var(--nclp-line-strong) var(--nclp-bg-main); }
|
|
|
795292
795925
|
</div>
|
|
795293
795926
|
|
|
795294
795927
|
<!-- Voice tab — voicechat toggle + clone management. AudioWorklet drives mic capture; WebAudio handles TTS playback. -->
|
|
795295
|
-
<div id="voice-panel" style="display:none;flex:1;overflow-y:auto
|
|
795296
|
-
|
|
795297
|
-
|
|
795928
|
+
<div id="voice-panel" class="omnius-route-shell" data-route-page="voice" data-route-tab="voice" style="display:none;flex:1;overflow-y:auto">
|
|
795929
|
+
${renderRouteHeading({ page: "voice", eyebrow: "Realtime audio", title: "Voice", description: "Control the live voice session, active model, model-specific options, transcript, and speech test from one observable surface.", signals: ["session", "model", "transcript", "TTS"] })}
|
|
795930
|
+
<div class="observability-grid" data-layout="voice-controls">
|
|
795931
|
+
<section class="observability-card observability-span-4" data-module="voice-session">
|
|
795932
|
+
<header class="observability-card__header"><h2 class="observability-card__title">Voice session</h2><span class="observability-card__meta">mic ↔ ASR ↔ agent ↔ TTS</span></header>
|
|
795933
|
+
<div class="observability-card__body">
|
|
795298
795934
|
<div style="font-size:0.65rem;color:var(--color-fg-subtle);margin-bottom:8px">
|
|
795299
795935
|
Live mic ↔ ASR ↔ agent ↔ TTS over WebSocket. Audio routes over the same origin
|
|
795300
795936
|
as this page — works on localhost or over a forwarded public IP. Mic permission
|
|
@@ -795306,26 +795942,29 @@ html { scrollbar-color: var(--nclp-line-strong) var(--nclp-bg-main); }
|
|
|
795306
795942
|
<span id="voice-mic-pill" style="display:none;background:var(--color-bg);border-left:2px solid var(--color-success);padding:4px 10px;font-size:0.6rem;color:var(--color-success)">● mic active</span>
|
|
795307
795943
|
</div>
|
|
795308
795944
|
<div id="voice-transcript-pane" style="display:none;background:var(--color-bg);border:1px solid var(--color-bg-input);border-radius:3px;padding:10px;margin-top:6px;min-height:80px;max-height:280px;overflow-y:auto;font-size:0.7rem"></div>
|
|
795309
|
-
|
|
795945
|
+
</div>
|
|
795946
|
+
</section>
|
|
795310
795947
|
|
|
795311
|
-
<
|
|
795312
|
-
<
|
|
795313
|
-
<div
|
|
795948
|
+
<section class="observability-card observability-span-2" data-module="voice-model">
|
|
795949
|
+
<header class="observability-card__header"><h2 class="observability-card__title">Voice model</h2><span class="observability-card__meta">active engine</span></header>
|
|
795950
|
+
<div class="observability-card__body observability-actions">
|
|
795314
795951
|
<select id="voice-model-select" onchange="renderVoiceModelOptions()" style="background:var(--color-bg);border:1px solid var(--color-border);color:var(--color-fg);padding:5px 10px;border-radius:3px;font-family:inherit;font-size:0.7rem;flex:1"></select>
|
|
795315
795952
|
<button onclick="switchVoiceModel()" style="background:var(--color-bg-input);border:1px solid var(--color-border);color:var(--color-brand);padding:5px 12px;border-radius:3px;font-family:inherit;font-size:0.7rem;cursor:pointer">switch</button>
|
|
795316
795953
|
</div>
|
|
795317
|
-
</
|
|
795954
|
+
</section>
|
|
795318
795955
|
|
|
795319
|
-
<
|
|
795320
|
-
<
|
|
795321
|
-
|
|
795956
|
+
<section id="voice-model-options-panel" class="observability-card observability-span-3" data-module="voice-model-options">
|
|
795957
|
+
<header class="observability-card__header"><h2 class="observability-card__title">Model options</h2><span class="observability-card__meta">engine capabilities</span></header>
|
|
795958
|
+
<div id="voice-model-options-content" class="observability-card__body" style="font-size:0.7rem;color:var(--color-fg-muted)"></div>
|
|
795959
|
+
</section>
|
|
795322
795960
|
|
|
795323
|
-
<
|
|
795324
|
-
<
|
|
795325
|
-
<div
|
|
795961
|
+
<section class="observability-card observability-span-3" data-module="voice-tts-test">
|
|
795962
|
+
<header class="observability-card__header"><h2 class="observability-card__title">Speech test</h2><span class="observability-card__meta">test active TTS</span></header>
|
|
795963
|
+
<div class="observability-card__body observability-actions">
|
|
795326
795964
|
<input type="text" id="voice-test-text" placeholder="Type and the voice model speaks it..." style="flex:1;background:var(--color-bg);border:1px solid var(--color-border);color:var(--color-fg);padding:6px 10px;border-radius:3px;font-family:inherit;font-size:0.7rem">
|
|
795327
795965
|
<button onclick="testTTS()" style="background:var(--color-bg-input);border:1px solid var(--color-border);color:var(--color-brand);padding:5px 14px;border-radius:3px;font-family:inherit;font-size:0.7rem;cursor:pointer">speak</button>
|
|
795328
795966
|
</div>
|
|
795967
|
+
</section>
|
|
795329
795968
|
</div>
|
|
795330
795969
|
</div>
|
|
795331
795970
|
|
|
@@ -795398,7 +796037,7 @@ html { scrollbar-color: var(--nclp-line-strong) var(--nclp-bg-main); }
|
|
|
795398
796037
|
the mic button on the input row. The panel hits /v1/voice/start
|
|
795399
796038
|
to warm the engines on first open, /v1/voice/tts to synthesize
|
|
795400
796039
|
arbitrary text, and /v1/voice/transcribe with mic blob bytes. -->
|
|
795401
|
-
<div id="voice-panel" style="display:none;padding:8px 10px;background:var(--color-bg-elevated);border-top:1px solid var(--color-border);font-size:0.78rem">
|
|
796040
|
+
<div id="voice-mini-panel" style="display:none;padding:8px 10px;background:var(--color-bg-elevated);border-top:1px solid var(--color-border);font-size:0.78rem">
|
|
795402
796041
|
<div style="display:flex;gap:6px;align-items:center;margin-bottom:6px">
|
|
795403
796042
|
<span id="voice-state-badge" style="padding:2px 8px;border-radius:10px;background:var(--color-bg-input);border:1px solid var(--color-border);font-size:0.7rem;color:var(--color-fg-muted)">idle</span>
|
|
795404
796043
|
<button type="button" onclick="warmVoice()" style="font-size:0.74rem">warm</button>
|
|
@@ -795420,7 +796059,7 @@ html { scrollbar-color: var(--nclp-line-strong) var(--nclp-bg-main); }
|
|
|
795420
796059
|
<div id="input-row">
|
|
795421
796060
|
<span id="system-prompt-toggle" onclick="toggleSystemPrompt()" style="display:none">sys</span>
|
|
795422
796061
|
<textarea id="input-area" placeholder="Type a message..." rows="1"></textarea>
|
|
795423
|
-
<button id="voice-toggle-btn" type="button" onclick="toggleVoicePanel()" title="open voice panel" style="background:var(--color-bg-input);border:1px solid var(--color-border);color:var(--color-fg-muted);padding:10px 12px;border-radius:3px;font-family:inherit;font-size:0.75rem;cursor:pointer;flex-shrink:0">🎙</button>
|
|
796062
|
+
<button id="voice-input-toggle-btn" type="button" onclick="toggleVoicePanel()" title="open voice panel" style="background:var(--color-bg-input);border:1px solid var(--color-border);color:var(--color-fg-muted);padding:10px 12px;border-radius:3px;font-family:inherit;font-size:0.75rem;cursor:pointer;flex-shrink:0">🎙</button>
|
|
795424
796063
|
<button id="send-btn" onclick="sendMessage()">send</button>
|
|
795425
796064
|
<button id="stop-btn" onclick="stopChat()" style="display:none;background:var(--color-bg-input);border:1px solid var(--color-error);color:var(--color-error);padding:10px 16px;border-radius:3px;font-family:inherit;font-size:0.75rem;cursor:pointer;flex-shrink:0">stop</button>
|
|
795426
796065
|
<!-- WO-CHAT-CHECKIN — teal accent button that takes the place of stop
|
|
@@ -795559,6 +796198,7 @@ html { scrollbar-color: var(--nclp-line-strong) var(--nclp-bg-main); }
|
|
|
795559
796198
|
</div>
|
|
795560
796199
|
|
|
795561
796200
|
<script>
|
|
796201
|
+
const webUiRouteSpecs = ${JSON.stringify(WEB_UI_ROUTE_SPECS)};
|
|
795562
796202
|
// ════════════════════════════════════════════════════════════════════
|
|
795563
796203
|
// Store layer — Svelte-writable shape, vanilla JS.
|
|
795564
796204
|
// ════════════════════════════════════════════════════════════════════
|
|
@@ -795819,18 +796459,8 @@ const $activeTab = writable('chat');
|
|
|
795819
796459
|
|
|
795820
796460
|
function parseInitialGuiRoute() {
|
|
795821
796461
|
const path = (location.pathname || '/').replace(/\\/+$/, '') || '/';
|
|
795822
|
-
const
|
|
795823
|
-
|
|
795824
|
-
'/agent': 'agent',
|
|
795825
|
-
'/voice': 'voice',
|
|
795826
|
-
'/projects': 'projects',
|
|
795827
|
-
'/dashboard': 'jobs',
|
|
795828
|
-
'/jobs': 'jobs',
|
|
795829
|
-
'/activity': 'activity',
|
|
795830
|
-
'/settings': 'config',
|
|
795831
|
-
'/config': 'config',
|
|
795832
|
-
};
|
|
795833
|
-
const tab = pathToTab[path] || null;
|
|
796462
|
+
const matched = webUiRouteSpecs.find(route => route.path === path || route.aliases.includes(path));
|
|
796463
|
+
const tab = matched?.tab || null;
|
|
795834
796464
|
let chatSession = null;
|
|
795835
796465
|
if (tab === 'chat') {
|
|
795836
796466
|
const qs = location.search.replace(/^\\?/, '').trim();
|
|
@@ -796029,36 +796659,18 @@ function _projectDisplayName(proj) {
|
|
|
796029
796659
|
if (!proj) return 'No project';
|
|
796030
796660
|
return proj.name || _projectPathLeaf(proj.root) || proj.root || 'Project';
|
|
796031
796661
|
}
|
|
796032
|
-
function _endpointDisplayName(endpoint) {
|
|
796033
|
-
if (!endpoint) return '';
|
|
796034
|
-
try { return new URL(endpoint).host || endpoint; } catch { return String(endpoint); }
|
|
796035
|
-
}
|
|
796036
796662
|
function renderProjectChrome() {
|
|
796037
796663
|
const proj = $currentProject.get ? $currentProject.get() : null;
|
|
796038
|
-
const cfg = $config.get ? ($config.get() || {}) : {};
|
|
796039
796664
|
const root = proj?.root || '';
|
|
796040
796665
|
const name = _projectDisplayName(proj);
|
|
796041
|
-
const headerName = document.getElementById('header-project-name') || document.querySelector('#header .accent');
|
|
796042
|
-
if (headerName) {
|
|
796043
|
-
headerName.textContent = name;
|
|
796044
|
-
headerName.title = root || name;
|
|
796045
|
-
}
|
|
796046
796666
|
const brand = document.getElementById('sidebar-brand');
|
|
796047
796667
|
if (brand) {
|
|
796048
796668
|
brand.textContent = name;
|
|
796049
796669
|
brand.title = root || name;
|
|
796050
796670
|
}
|
|
796051
796671
|
if (statusEl) {
|
|
796052
|
-
|
|
796053
|
-
|
|
796054
|
-
const endpoint = endpointObj.url || cfg.backendUrl || '';
|
|
796055
|
-
const endpointType = endpointObj.backendType || cfg.backendType || '';
|
|
796056
|
-
const endpointLabel = endpoint ? ((endpointType ? endpointType + ' ' : '') + _endpointDisplayName(endpoint)) : '';
|
|
796057
|
-
const bits = [String(_connectionState || 'connecting')];
|
|
796058
|
-
if (selected) bits.push(selected);
|
|
796059
|
-
if (endpointLabel) bits.push(endpointLabel);
|
|
796060
|
-
statusEl.textContent = bits.join(' · ');
|
|
796061
|
-
statusEl.title = [root, endpoint].filter(Boolean).join('\\n');
|
|
796672
|
+
statusEl.textContent = String(_connectionState || 'connecting');
|
|
796673
|
+
statusEl.title = root || name;
|
|
796062
796674
|
statusEl.className = /connected|ready|online/i.test(_connectionState || '') ? 'status live' : 'status';
|
|
796063
796675
|
}
|
|
796064
796676
|
try { _syncSidebarStatus(); } catch {}
|
|
@@ -796067,6 +796679,171 @@ function setConnectionState(state) {
|
|
|
796067
796679
|
_connectionState = state || 'connecting';
|
|
796068
796680
|
renderProjectChrome();
|
|
796069
796681
|
}
|
|
796682
|
+
|
|
796683
|
+
// Accessible workspace switcher based on the compact search/listbox pattern
|
|
796684
|
+
// used by Voryn. Project state still flows through Omnius's central store.
|
|
796685
|
+
let workspacePickerProjects = [];
|
|
796686
|
+
let workspacePickerIndex = -1;
|
|
796687
|
+
function workspacePickerElements() {
|
|
796688
|
+
return {
|
|
796689
|
+
trigger: document.getElementById('workspace-switcher-trigger'),
|
|
796690
|
+
picker: document.getElementById('workspace-picker'),
|
|
796691
|
+
search: document.getElementById('workspace-picker-search'),
|
|
796692
|
+
list: document.getElementById('workspace-picker-list'),
|
|
796693
|
+
};
|
|
796694
|
+
}
|
|
796695
|
+
function isWorkspacePickerOpen() {
|
|
796696
|
+
const { picker } = workspacePickerElements();
|
|
796697
|
+
return !!picker && picker.style.display !== 'none';
|
|
796698
|
+
}
|
|
796699
|
+
async function openWorkspacePicker() {
|
|
796700
|
+
const { trigger, picker, search } = workspacePickerElements();
|
|
796701
|
+
if (!trigger || !picker || !search) return;
|
|
796702
|
+
picker.style.display = 'block';
|
|
796703
|
+
trigger.setAttribute('aria-expanded', 'true');
|
|
796704
|
+
workspacePickerIndex = -1;
|
|
796705
|
+
await loadWorkspacePicker();
|
|
796706
|
+
requestAnimationFrame(() => { try { search.focus(); search.select(); } catch {} });
|
|
796707
|
+
}
|
|
796708
|
+
function closeWorkspacePicker(restoreFocus) {
|
|
796709
|
+
const { trigger, picker, search } = workspacePickerElements();
|
|
796710
|
+
if (!trigger || !picker) return;
|
|
796711
|
+
picker.style.display = 'none';
|
|
796712
|
+
trigger.setAttribute('aria-expanded', 'false');
|
|
796713
|
+
workspacePickerIndex = -1;
|
|
796714
|
+
if (search) search.value = '';
|
|
796715
|
+
if (restoreFocus) { try { trigger.focus(); } catch {} }
|
|
796716
|
+
}
|
|
796717
|
+
function toggleWorkspacePicker(event) {
|
|
796718
|
+
if (event) event.stopPropagation();
|
|
796719
|
+
if (isWorkspacePickerOpen()) closeWorkspacePicker(false);
|
|
796720
|
+
else openWorkspacePicker();
|
|
796721
|
+
}
|
|
796722
|
+
function workspaceAgo(ts) {
|
|
796723
|
+
if (!ts) return '';
|
|
796724
|
+
const seconds = Math.max(0, Math.floor((Date.now() - Number(ts)) / 1000));
|
|
796725
|
+
if (seconds < 60) return seconds + 's';
|
|
796726
|
+
if (seconds < 3600) return Math.floor(seconds / 60) + 'm';
|
|
796727
|
+
if (seconds < 86400) return Math.floor(seconds / 3600) + 'h';
|
|
796728
|
+
return Math.floor(seconds / 86400) + 'd';
|
|
796729
|
+
}
|
|
796730
|
+
function renderWorkspacePicker(query) {
|
|
796731
|
+
const { list } = workspacePickerElements();
|
|
796732
|
+
if (!list) return;
|
|
796733
|
+
const currentRoot = $currentProject.get?.()?.root || '';
|
|
796734
|
+
const needle = String(query || '').trim().toLowerCase();
|
|
796735
|
+
const matches = workspacePickerProjects.filter(project => {
|
|
796736
|
+
const haystack = (_projectDisplayName(project) + ' ' + (project.root || '')).toLowerCase();
|
|
796737
|
+
return !needle || haystack.includes(needle);
|
|
796738
|
+
});
|
|
796739
|
+
list.replaceChildren();
|
|
796740
|
+
if (!matches.length) {
|
|
796741
|
+
const empty = document.createElement('div');
|
|
796742
|
+
empty.className = 'workspace-picker__empty';
|
|
796743
|
+
empty.textContent = needle ? 'No matching workspaces' : 'No registered workspaces';
|
|
796744
|
+
list.appendChild(empty);
|
|
796745
|
+
return;
|
|
796746
|
+
}
|
|
796747
|
+
for (const project of matches) {
|
|
796748
|
+
const active = project.root === currentRoot;
|
|
796749
|
+
const option = document.createElement('button');
|
|
796750
|
+
option.type = 'button';
|
|
796751
|
+
option.className = 'workspace-picker__option';
|
|
796752
|
+
option.setAttribute('role', 'option');
|
|
796753
|
+
option.setAttribute('aria-selected', active ? 'true' : 'false');
|
|
796754
|
+
option.dataset.root = project.root || '';
|
|
796755
|
+
const copy = document.createElement('span');
|
|
796756
|
+
copy.style.minWidth = '0';
|
|
796757
|
+
const name = document.createElement('span');
|
|
796758
|
+
name.className = 'workspace-picker__name';
|
|
796759
|
+
name.textContent = _projectDisplayName(project);
|
|
796760
|
+
const path = document.createElement('span');
|
|
796761
|
+
path.className = 'workspace-picker__path';
|
|
796762
|
+
const age = workspaceAgo(project.lastSeen);
|
|
796763
|
+
path.textContent = (project.root || '') + (age ? ' · seen ' + age + ' ago' : '');
|
|
796764
|
+
const mark = document.createElement('span');
|
|
796765
|
+
mark.className = 'workspace-picker__check';
|
|
796766
|
+
mark.textContent = active ? 'active' : 'switch';
|
|
796767
|
+
copy.append(name, path);
|
|
796768
|
+
option.append(copy, mark);
|
|
796769
|
+
option.addEventListener('click', () => switchProjectRoot(project.root, option));
|
|
796770
|
+
list.appendChild(option);
|
|
796771
|
+
}
|
|
796772
|
+
}
|
|
796773
|
+
async function loadWorkspacePicker() {
|
|
796774
|
+
const { list, search } = workspacePickerElements();
|
|
796775
|
+
if (!list) return;
|
|
796776
|
+
list.innerHTML = '<div class="workspace-picker__empty">Loading workspaces...</div>';
|
|
796777
|
+
try {
|
|
796778
|
+
const response = await fetch('/v1/projects', { headers: headers() });
|
|
796779
|
+
if (!response.ok) throw new Error('HTTP ' + response.status);
|
|
796780
|
+
const data = await response.json();
|
|
796781
|
+
workspacePickerProjects = Array.isArray(data.projects) ? data.projects : [];
|
|
796782
|
+
const current = data.current && typeof data.current.root === 'string' ? data.current : null;
|
|
796783
|
+
if ($currentProject.get?.()?.root !== current?.root) $currentProject.set(current);
|
|
796784
|
+
renderWorkspacePicker(search?.value || '');
|
|
796785
|
+
} catch (error) {
|
|
796786
|
+
list.innerHTML = '<div class="workspace-picker__empty">Unable to load workspaces</div>';
|
|
796787
|
+
}
|
|
796788
|
+
}
|
|
796789
|
+
function filterWorkspacePicker(value) {
|
|
796790
|
+
workspacePickerIndex = -1;
|
|
796791
|
+
renderWorkspacePicker(value);
|
|
796792
|
+
}
|
|
796793
|
+
function handleWorkspacePickerKeys(event) {
|
|
796794
|
+
const { list } = workspacePickerElements();
|
|
796795
|
+
if (!list) return;
|
|
796796
|
+
const options = Array.from(list.querySelectorAll('.workspace-picker__option'));
|
|
796797
|
+
if (event.key === 'Escape') {
|
|
796798
|
+
event.preventDefault();
|
|
796799
|
+
closeWorkspacePicker(true);
|
|
796800
|
+
return;
|
|
796801
|
+
}
|
|
796802
|
+
if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
|
|
796803
|
+
event.preventDefault();
|
|
796804
|
+
const delta = event.key === 'ArrowDown' ? 1 : -1;
|
|
796805
|
+
workspacePickerIndex = Math.max(0, Math.min(options.length - 1, workspacePickerIndex + delta));
|
|
796806
|
+
options.forEach((option, index) => option.classList.toggle('is-keyboard-active', index === workspacePickerIndex));
|
|
796807
|
+
options[workspacePickerIndex]?.scrollIntoView({ block: 'nearest' });
|
|
796808
|
+
return;
|
|
796809
|
+
}
|
|
796810
|
+
if (event.key === 'Enter' && options.length) {
|
|
796811
|
+
event.preventDefault();
|
|
796812
|
+
options[Math.max(0, workspacePickerIndex)]?.click();
|
|
796813
|
+
}
|
|
796814
|
+
}
|
|
796815
|
+
async function switchProjectRoot(root, pendingElement) {
|
|
796816
|
+
if (!root) return false;
|
|
796817
|
+
if (pendingElement) pendingElement.disabled = true;
|
|
796818
|
+
try {
|
|
796819
|
+
const response = await fetch('/v1/projects/switch', {
|
|
796820
|
+
method: 'POST',
|
|
796821
|
+
headers: { 'Content-Type': 'application/json', ...headers() },
|
|
796822
|
+
body: JSON.stringify({ root }),
|
|
796823
|
+
});
|
|
796824
|
+
if (!response.ok) {
|
|
796825
|
+
const error = await response.json().catch(() => ({ message: 'HTTP ' + response.status }));
|
|
796826
|
+
throw new Error(error.message || error.error || 'unknown error');
|
|
796827
|
+
}
|
|
796828
|
+
closeWorkspacePicker(true);
|
|
796829
|
+
await loadProjects();
|
|
796830
|
+
return true;
|
|
796831
|
+
} catch (error) {
|
|
796832
|
+
alert('Switch failed: ' + (error && error.message ? error.message : String(error)));
|
|
796833
|
+
if (pendingElement) pendingElement.disabled = false;
|
|
796834
|
+
return false;
|
|
796835
|
+
}
|
|
796836
|
+
}
|
|
796837
|
+
document.addEventListener('pointerdown', (event) => {
|
|
796838
|
+
if (isWorkspacePickerOpen() && !event.target.closest('.workspace-switcher')) closeWorkspacePicker(false);
|
|
796839
|
+
});
|
|
796840
|
+
document.addEventListener('keydown', (event) => {
|
|
796841
|
+
if (event.key === 'Escape' && isWorkspacePickerOpen()) closeWorkspacePicker(true);
|
|
796842
|
+
});
|
|
796843
|
+
window.toggleWorkspacePicker = toggleWorkspacePicker;
|
|
796844
|
+
window.closeWorkspacePicker = closeWorkspacePicker;
|
|
796845
|
+
window.filterWorkspacePicker = filterWorkspacePicker;
|
|
796846
|
+
window.handleWorkspacePickerKeys = handleWorkspacePickerKeys;
|
|
796070
796847
|
let streaming = false;
|
|
796071
796848
|
let messages = [];
|
|
796072
796849
|
let chatAbortController = null; // for stop button
|
|
@@ -797791,7 +798568,7 @@ async function _refreshVoiceState() {
|
|
|
797791
798568
|
}
|
|
797792
798569
|
|
|
797793
798570
|
function toggleVoicePanel() {
|
|
797794
|
-
const p = document.getElementById('voice-panel');
|
|
798571
|
+
const p = document.getElementById('voice-mini-panel');
|
|
797795
798572
|
if (!p) return;
|
|
797796
798573
|
if (p.style.display === 'none') {
|
|
797797
798574
|
p.style.display = 'block';
|
|
@@ -797801,7 +798578,7 @@ function toggleVoicePanel() {
|
|
|
797801
798578
|
}
|
|
797802
798579
|
}
|
|
797803
798580
|
function closeVoicePanel() {
|
|
797804
|
-
const p = document.getElementById('voice-panel');
|
|
798581
|
+
const p = document.getElementById('voice-mini-panel');
|
|
797805
798582
|
if (p) p.style.display = 'none';
|
|
797806
798583
|
}
|
|
797807
798584
|
window.toggleVoicePanel = toggleVoicePanel;
|
|
@@ -798081,36 +798858,14 @@ window.closeRemoteConnection = closeRemoteConnection;
|
|
|
798081
798858
|
})();
|
|
798082
798859
|
|
|
798083
798860
|
// Tab switching + browser route sync
|
|
798084
|
-
const allPanels =
|
|
798861
|
+
const allPanels = webUiRouteSpecs.map(route => route.panelId);
|
|
798085
798862
|
function routePathForTab(tab) {
|
|
798086
|
-
|
|
798087
|
-
chat: '/chat',
|
|
798088
|
-
agent: '/agent',
|
|
798089
|
-
voice: '/voice',
|
|
798090
|
-
generate: '/generate',
|
|
798091
|
-
projects: '/projects',
|
|
798092
|
-
jobs: '/dashboard',
|
|
798093
|
-
activity: '/activity',
|
|
798094
|
-
config: '/settings',
|
|
798095
|
-
};
|
|
798096
|
-
return map[tab] || '/chat';
|
|
798863
|
+
return webUiRouteSpecs.find(route => route.tab === tab)?.path || '/chat';
|
|
798097
798864
|
}
|
|
798098
798865
|
function tabForRoutePath(pathname) {
|
|
798099
798866
|
const path = (pathname || '/').replace(/\\/+$/, '') || '/';
|
|
798100
|
-
const
|
|
798101
|
-
|
|
798102
|
-
'/chat': 'chat',
|
|
798103
|
-
'/agent': 'agent',
|
|
798104
|
-
'/voice': 'voice',
|
|
798105
|
-
'/generate': 'generate',
|
|
798106
|
-
'/projects': 'projects',
|
|
798107
|
-
'/dashboard': 'jobs',
|
|
798108
|
-
'/jobs': 'jobs',
|
|
798109
|
-
'/activity': 'activity',
|
|
798110
|
-
'/settings': 'config',
|
|
798111
|
-
'/config': 'config',
|
|
798112
|
-
};
|
|
798113
|
-
return map[path] || 'chat';
|
|
798867
|
+
const matched = webUiRouteSpecs.find(route => route.path === path || route.aliases.includes(path));
|
|
798868
|
+
return matched?.tab || 'chat';
|
|
798114
798869
|
}
|
|
798115
798870
|
function chatSessionFromRouteSearch(search) {
|
|
798116
798871
|
const qs = String(search || '').replace(/^\\?/, '').trim();
|
|
@@ -798132,13 +798887,19 @@ function syncRouteForTab(tab, replace) {
|
|
|
798132
798887
|
}
|
|
798133
798888
|
function switchTab(tab, opts) {
|
|
798134
798889
|
opts = opts || {};
|
|
798135
|
-
const
|
|
798136
|
-
allPanels.forEach(id => {
|
|
798137
|
-
|
|
798890
|
+
const route = webUiRouteSpecs.find(candidate => candidate.tab === tab) || webUiRouteSpecs[0];
|
|
798891
|
+
allPanels.forEach(id => {
|
|
798892
|
+
const el = document.getElementById(id);
|
|
798893
|
+
if (!el) return;
|
|
798894
|
+
el.style.display = 'none';
|
|
798895
|
+
el.setAttribute('aria-hidden', 'true');
|
|
798896
|
+
});
|
|
798897
|
+
const panel = document.getElementById(route.panelId);
|
|
798138
798898
|
if (panel) {
|
|
798139
798899
|
// chat AND agent are flex containers (vertical column with sidebar/topbar);
|
|
798140
798900
|
// other panels are simple block scrollers.
|
|
798141
798901
|
panel.style.display = (tab === 'chat' || tab === 'agent') ? 'flex' : 'block';
|
|
798902
|
+
panel.setAttribute('aria-hidden', 'false');
|
|
798142
798903
|
}
|
|
798143
798904
|
document.getElementById('footer').style.display = tab === 'chat' ? 'flex' : 'none';
|
|
798144
798905
|
document.querySelectorAll('.tab').forEach(t => { t.style.borderBottomColor = 'transparent'; t.style.color = 'var(--color-fg-faint)'; });
|
|
@@ -798146,7 +798907,10 @@ function switchTab(tab, opts) {
|
|
|
798146
798907
|
if (active) { active.style.borderBottomColor = 'var(--color-brand)'; active.style.color = 'var(--color-brand)'; }
|
|
798147
798908
|
// OWUI-2: mirror active state on the sidebar nav
|
|
798148
798909
|
document.querySelectorAll('#omnius-sidebar .sb-nav').forEach(b => {
|
|
798149
|
-
|
|
798910
|
+
const isActive = b.getAttribute('data-tab') === tab;
|
|
798911
|
+
b.classList.toggle('active', isActive);
|
|
798912
|
+
if (isActive) b.setAttribute('aria-current', 'page');
|
|
798913
|
+
else b.removeAttribute('aria-current');
|
|
798150
798914
|
});
|
|
798151
798915
|
if ($activeTab.get && $activeTab.get() !== tab) $activeTab.set(tab);
|
|
798152
798916
|
if (!opts.fromRoute) syncRouteForTab(tab, !!opts.replaceRoute);
|
|
@@ -798169,6 +798933,9 @@ function switchTab(tab, opts) {
|
|
|
798169
798933
|
}
|
|
798170
798934
|
}
|
|
798171
798935
|
window.switchTab = switchTab;
|
|
798936
|
+
// Route activation must not depend on a registered/current project. Apply it
|
|
798937
|
+
// immediately, then project preference hydration may fill route-local state.
|
|
798938
|
+
if (initialGuiRoute.tab) switchTab(initialGuiRoute.tab, { fromRoute: true });
|
|
798172
798939
|
|
|
798173
798940
|
// ════════════════════════════════════════════════════════════
|
|
798174
798941
|
// Generate tab — global media generation (image/video/audio/music)
|
|
@@ -798785,27 +799552,8 @@ async function loadProjects() {
|
|
|
798785
799552
|
const root = decodeURIComponent(el.getAttribute('data-root') || '');
|
|
798786
799553
|
if (!root) return;
|
|
798787
799554
|
el.style.opacity = '0.5';
|
|
798788
|
-
|
|
798789
|
-
|
|
798790
|
-
method: 'POST',
|
|
798791
|
-
headers: { 'Content-Type': 'application/json', ...headers() },
|
|
798792
|
-
body: JSON.stringify({ root }),
|
|
798793
|
-
});
|
|
798794
|
-
if (!r.ok) {
|
|
798795
|
-
const err = await r.json().catch(() => ({ message: 'HTTP ' + r.status }));
|
|
798796
|
-
alert('Switch failed: ' + (err.message || err.error || 'unknown'));
|
|
798797
|
-
el.style.opacity = '1';
|
|
798798
|
-
return;
|
|
798799
|
-
}
|
|
798800
|
-
// Successful switch: re-fetch /v1/projects which updates
|
|
798801
|
-
// $currentProject; the subscription cascade refreshes models,
|
|
798802
|
-
// config, sessions, agent runs, and restores the chat session
|
|
798803
|
-
// for the new project. No more brittle ad-hoc fan-out.
|
|
798804
|
-
await loadProjects();
|
|
798805
|
-
} catch (e) {
|
|
798806
|
-
alert('Switch failed: ' + (e && e.message ? e.message : String(e)));
|
|
798807
|
-
el.style.opacity = '1';
|
|
798808
|
-
}
|
|
799555
|
+
const switched = await switchProjectRoot(root, el);
|
|
799556
|
+
if (!switched) el.style.opacity = '1';
|
|
798809
799557
|
});
|
|
798810
799558
|
});
|
|
798811
799559
|
} catch (e) {
|
|
@@ -799169,8 +799917,7 @@ async function loadDaemons() {
|
|
|
799169
799917
|
el.innerHTML = '<div style="background:var(--color-bg-elevated);border:1px solid var(--color-bg-input);border-radius:3px;padding:8px 12px;color:var(--color-fg-faint);font-size:0.7rem">No active processes</div>';
|
|
799170
799918
|
return;
|
|
799171
799919
|
}
|
|
799172
|
-
el.innerHTML =
|
|
799173
|
-
d.runs.map(j =>
|
|
799920
|
+
el.innerHTML = d.runs.map(j =>
|
|
799174
799921
|
'<div style="background:var(--color-bg-elevated);border-left:2px solid var(--color-brand);padding:6px 10px;margin:4px 0;font-size:0.72rem">' +
|
|
799175
799922
|
'<span style="color:var(--color-brand)">' + (j.id||'').slice(0,12) + '</span> ' +
|
|
799176
799923
|
'<span style="color:var(--color-success)">running</span> ' +
|
|
@@ -799207,7 +799954,7 @@ async function loadScheduled() {
|
|
|
799207
799954
|
+ '</div>';
|
|
799208
799955
|
return row;
|
|
799209
799956
|
}).join('');
|
|
799210
|
-
el.innerHTML =
|
|
799957
|
+
el.innerHTML = rows
|
|
799211
799958
|
+ '<div style="margin-top:6px;display:flex;gap:8px">'
|
|
799212
799959
|
+ '<button onclick="disableAllScheduled()" title="Disable all scheduled tasks" style="background:var(--color-bg-input);border:1px solid var(--color-error);color:var(--color-error);padding:3px 8px;border-radius:3px;font-size:0.65rem;cursor:pointer">disable all</button>'
|
|
799213
799960
|
+ '<button onclick="enableAllScheduled()" title="Enable all scheduled tasks" style="background:var(--color-bg-input);border:1px solid #2a3a2a;color:var(--color-success);padding:3px 8px;border-radius:3px;font-size:0.65rem;cursor:pointer">enable all</button>'
|
|
@@ -799369,7 +800116,7 @@ async function loadServices() {
|
|
|
799369
800116
|
+ '<div style="margin-top:4px;display:flex;gap:8px">' + stopBtn + disBtn + '</div>'
|
|
799370
800117
|
+ '</div>';
|
|
799371
800118
|
}).join('');
|
|
799372
|
-
el.innerHTML =
|
|
800119
|
+
el.innerHTML = rows;
|
|
799373
800120
|
} catch {}
|
|
799374
800121
|
}
|
|
799375
800122
|
|
|
@@ -799558,7 +800305,6 @@ async function loadDashboard() {
|
|
|
799558
800305
|
const totalIn = d.persistent?.totalIn || d.totalTokensIn || 0;
|
|
799559
800306
|
const totalOut = d.persistent?.totalOut || d.totalTokensOut || 0;
|
|
799560
800307
|
document.getElementById('dashboard-usage').innerHTML =
|
|
799561
|
-
'<h3 style="color:var(--color-brand);font-size:0.7rem;margin-bottom:8px">Token Usage by Provider (persistent)</h3>' +
|
|
799562
800308
|
'<div style="display:flex;gap:12px;flex-wrap:wrap;margin-bottom:8px">' +
|
|
799563
800309
|
'<div style="background:var(--color-bg-elevated);border:1px solid var(--color-bg-input);border-radius:3px;padding:8px 12px;flex:1">' +
|
|
799564
800310
|
'<div style="color:var(--color-fg-faint);font-size:0.6rem">TOTAL IN</div>' +
|
|
@@ -802524,6 +803270,7 @@ function toggleSidebar() {
|
|
|
802524
803270
|
const sb = document.getElementById('omnius-sidebar');
|
|
802525
803271
|
if (!sb) return;
|
|
802526
803272
|
const next = sb.getAttribute('data-collapsed') !== 'true';
|
|
803273
|
+
if (next) closeWorkspacePicker(false);
|
|
802527
803274
|
sb.setAttribute('data-collapsed', String(next));
|
|
802528
803275
|
_setLS(SIDEBAR_KEYS.collapsed, next ? '1' : '0');
|
|
802529
803276
|
}
|
|
@@ -803965,8 +804712,22 @@ input.focus();
|
|
|
803965
804712
|
</body>
|
|
803966
804713
|
</html>`;
|
|
803967
804714
|
}
|
|
804715
|
+
var WEB_UI_ROUTE_SPECS, WEB_UI_ROUTE_PATHS;
|
|
803968
804716
|
var init_web_ui = __esm({
|
|
803969
804717
|
"packages/cli/src/api/web-ui.ts"() {
|
|
804718
|
+
WEB_UI_ROUTE_SPECS = Object.freeze([
|
|
804719
|
+
{ tab: "chat", page: "chat", path: "/chat", aliases: ["/"], panelId: "chat-container" },
|
|
804720
|
+
{ tab: "agent", page: "agent", path: "/agent", aliases: [], panelId: "agent-panel" },
|
|
804721
|
+
{ tab: "voice", page: "voice", path: "/voice", aliases: [], panelId: "voice-panel" },
|
|
804722
|
+
{ tab: "generate", page: "generate", path: "/generate", aliases: [], panelId: "generate-panel" },
|
|
804723
|
+
{ tab: "projects", page: "projects", path: "/projects", aliases: [], panelId: "projects-panel" },
|
|
804724
|
+
{ tab: "jobs", page: "dashboard", path: "/dashboard", aliases: ["/jobs"], panelId: "jobs-panel" },
|
|
804725
|
+
{ tab: "activity", page: "activity", path: "/activity", aliases: [], panelId: "activity-panel" },
|
|
804726
|
+
{ tab: "config", page: "settings", path: "/settings", aliases: ["/config"], panelId: "config-panel" }
|
|
804727
|
+
]);
|
|
804728
|
+
WEB_UI_ROUTE_PATHS = Object.freeze(
|
|
804729
|
+
WEB_UI_ROUTE_SPECS.flatMap((route) => [route.path, ...route.aliases])
|
|
804730
|
+
);
|
|
803970
804731
|
}
|
|
803971
804732
|
});
|
|
803972
804733
|
|
|
@@ -811291,19 +812052,7 @@ async function handleRequest(req3, res, ollamaUrl, verbose, runtimeDefaults = {}
|
|
|
811291
812052
|
res.end(svg);
|
|
811292
812053
|
return;
|
|
811293
812054
|
}
|
|
811294
|
-
const guiRoutes =
|
|
811295
|
-
"/",
|
|
811296
|
-
"/chat",
|
|
811297
|
-
"/agent",
|
|
811298
|
-
"/voice",
|
|
811299
|
-
"/generate",
|
|
811300
|
-
"/projects",
|
|
811301
|
-
"/dashboard",
|
|
811302
|
-
"/jobs",
|
|
811303
|
-
"/activity",
|
|
811304
|
-
"/settings",
|
|
811305
|
-
"/config"
|
|
811306
|
-
]);
|
|
812055
|
+
const guiRoutes = new Set(WEB_UI_ROUTE_PATHS);
|
|
811307
812056
|
if (guiRoutes.has(pathname) && method === "GET" && req3.headers.accept?.includes("text/html")) {
|
|
811308
812057
|
const htmlHeaders = {
|
|
811309
812058
|
"Content-Type": "text/html; charset=utf-8",
|