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.
81 lines
2.6 KiB
TypeScript
81 lines
2.6 KiB
TypeScript
import type {
|
|
ApiError,
|
|
CreateSessionRequest,
|
|
DaemonInfo,
|
|
DirListing,
|
|
GitRoot,
|
|
RecentDirs,
|
|
Session,
|
|
Uuid,
|
|
} from "./types.js";
|
|
|
|
async function jsonOrError<T>(res: Response): Promise<T> {
|
|
if (res.ok) {
|
|
if (res.status === 204) return undefined as unknown as T;
|
|
return (await res.json()) as T;
|
|
}
|
|
let detail = "";
|
|
try {
|
|
const body = (await res.json()) as ApiError;
|
|
detail = `${body.error.code}: ${body.error.message}`;
|
|
} catch {
|
|
detail = await res.text();
|
|
}
|
|
throw new Error(`${res.status} ${res.statusText}${detail ? ` — ${detail}` : ""}`);
|
|
}
|
|
|
|
export async function listSessions(): Promise<Session[]> {
|
|
return jsonOrError(await fetch("/api/sessions", { credentials: "same-origin" }));
|
|
}
|
|
|
|
export async function createSession(req: CreateSessionRequest): Promise<Session> {
|
|
return jsonOrError(
|
|
await fetch("/api/sessions", {
|
|
method: "POST",
|
|
credentials: "same-origin",
|
|
headers: { "content-type": "application/json" },
|
|
body: JSON.stringify(req),
|
|
}),
|
|
);
|
|
}
|
|
|
|
export async function killSession(id: Uuid): Promise<void> {
|
|
await jsonOrError(
|
|
await fetch(`/api/sessions/${encodeURIComponent(id)}`, {
|
|
method: "DELETE",
|
|
credentials: "same-origin",
|
|
}),
|
|
);
|
|
}
|
|
|
|
/** List one directory's immediate subdirectories (NAP-329). `path` may be empty,
|
|
* `~`, relative, or absolute — the daemon resolves it against its home dir. */
|
|
export async function listDir(path: string): Promise<DirListing> {
|
|
const url = `/api/dirs?path=${encodeURIComponent(path)}`;
|
|
return jsonOrError(await fetch(url, { credentials: "same-origin" }));
|
|
}
|
|
|
|
/** Identity + spawn presets of the daemon this web app is connected to (#26). */
|
|
export async function getDaemonInfo(): Promise<DaemonInfo> {
|
|
return jsonOrError(await fetch("/api/info", { credentials: "same-origin" }));
|
|
}
|
|
|
|
/** Recently-used working dirs, newest-first and deduped by dir (#26). Drives the
|
|
* spawn form's recents picker. */
|
|
export async function recentDirs(): Promise<RecentDirs> {
|
|
return jsonOrError(await fetch("/api/recent-dirs", { credentials: "same-origin" }));
|
|
}
|
|
|
|
/** Probe the enclosing git repo of `path` (#26). `root` is absent when the path
|
|
* isn't inside a repo. `path` may be `~`/relative/absolute; the daemon resolves
|
|
* it against its home dir. */
|
|
export async function gitRoot(path: string): Promise<GitRoot> {
|
|
const url = `/api/gitroot?path=${encodeURIComponent(path)}`;
|
|
return jsonOrError(await fetch(url, { credentials: "same-origin" }));
|
|
}
|
|
|
|
export function eventsWsUrl(): string {
|
|
const proto = location.protocol === "https:" ? "wss:" : "ws:";
|
|
return `${proto}//${location.host}/api/events`;
|
|
}
|