import * as routes from "./routes.js"; import type { ApiError, Branches, CreateSessionRequest, DaemonInfo, DirListing, GitRoot, NameBatch, Projects, RecentDirs, Session, Uuid, } from "./types.js"; async function jsonOrError(res: Response): Promise { 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 { return jsonOrError(await fetch(routes.SESSIONS, { credentials: "same-origin" })); } export async function createSession(req: CreateSessionRequest): Promise { return jsonOrError( await fetch(routes.SESSIONS, { method: "POST", credentials: "same-origin", headers: { "content-type": "application/json" }, body: JSON.stringify(req), }), ); } export async function killSession(id: Uuid): Promise { await jsonOrError( await fetch(routes.session(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 { const url = routes.withPathQuery(routes.DIRS, 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 { return jsonOrError(await fetch(routes.INFO, { credentials: "same-origin" })); } /** Recently-used working dirs, newest-first and deduped by dir (#26). */ export async function recentDirs(): Promise { return jsonOrError(await fetch(routes.RECENT_DIRS, { credentials: "same-origin" })); } /** Every discovered git repository / worktree, newest-activity-first (#54). * Drives the spawn form's project picker. */ export async function projects(): Promise { return jsonOrError(await fetch(routes.PROJECTS, { 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 { const url = routes.withPathQuery(routes.GITROOT, path); return jsonOrError(await fetch(url, { credentials: "same-origin" })); } /** A batch of fresh session handles for the spawn form's instant reroll (#53). * Rerolls consume the batch locally; only exhaustion goes back to the daemon. */ export async function fetchNames(count: number): Promise { const url = `${routes.NAMES}?${routes.COUNT_QUERY}=${encodeURIComponent(count)}`; return jsonOrError(await fetch(url, { credentials: "same-origin" })); } /** One repository's local branches, with worktree occupancy (#55). Empty for * a path outside any repo. Drives the spawn form's existing-branch picker. */ export async function fetchBranches(path: string): Promise { const url = routes.withPathQuery(routes.BRANCHES, path); return jsonOrError(await fetch(url, { credentials: "same-origin" })); } export { eventsWsUrl } from "./routes.js";