refactor(maestro): flatten web UI to a single host

Session tree nests by working directory only; the daemon chips in the
header become a session tally, and the spawn form loses its host picker.
API calls move from /api/daemons/:id/* to /api/dirs, /api/recent-dirs,
/api/gitroot, and /api/info.
This commit is contained in:
Miguel Palhas
2026-08-22 10:14:10 +01:00
parent 6790f41652
commit 1bb464be4b
14 changed files with 155 additions and 467 deletions
+10 -16
View File
@@ -36,12 +36,11 @@ export interface AttachOptions {
onClosed?: (sessionId: Uuid) => void;
}
/** What the header shows about the session (name / command / host). Optional —
* falls back to a short session id when the session isn't known yet. */
/** What the header shows about the session (name / command). Optional — falls
* back to a short session id when the session isn't known yet. */
export interface AttachInfo {
name: string;
command: string;
daemon: string;
}
function esc(s: string): string {
@@ -53,10 +52,9 @@ function esc(s: string): string {
}
/**
* Same-origin broker URL. The browser loads from `maestro-web`, which
* Same-origin attach URL. The browser loads from `maestro-web`, which
* reverse-proxies this WebSocket to the co-located daemon's `/api/attach/:id`
* broker endpoint (the daemon short-circuits to the local PTY when it owns the
* session, or brokers to the owning peer — NAP-247/NAP-327).
* endpoint.
*/
function brokerStreamUrl(sessionId: Uuid): string {
const proto = location.protocol === "https:" ? "wss:" : "ws:";
@@ -70,18 +68,15 @@ export function createAttachConnection(
): AttachConnection {
const el = document.createElement("div");
el.className = "attach";
// Useful identity over a bare UUID: name · command @ host. The full id stays
// in the tooltip for when you really need it.
// Useful identity over a bare UUID: name · command. The full id stays in the
// tooltip for when you really need it.
const titleHtml = info
? `<span class="at-name">${esc(info.name)}</span>` +
(info.command && info.command !== info.name
? `<span class="at-cmd">${esc(info.command)}</span>`
: "") +
(info.daemon ? `<span class="at-host">${esc(info.daemon)}</span>` : "")
: "")
: `session <span class="sid">${esc(sessionId.slice(0, 8))}</span>`;
const titleAttr = info
? `${info.name} · ${info.command} @ ${info.daemon} (${sessionId})`
: sessionId;
const titleAttr = info ? `${info.name} · ${info.command} (${sessionId})` : sessionId;
el.innerHTML = `
<header class="attach-header">
<a class="btn btn-ghost back" href="#/" aria-label="Back to catalog" title="Back to catalog">←</a>
@@ -317,9 +312,8 @@ export function createAttachConnection(
};
ws.onclose = (ev) => {
// The web proxy relays a broker/daemon rejection (no shared perimeter, peer
// down, unknown session) as an application close code (4000+) carrying a
// human reason. Show it in the pane — both the header and the terminal body
// The web proxy relays a daemon rejection (unknown session) as an
// application close code (4000+) carrying a human reason. Show it in the pane — both the header and the terminal body
// — and KEEP the pane up so the user can read why, instead of letting it
// flash and vanish (#39). Clean session-ends and ordinary drops keep the
// auto-close behavior below.