fix: render registry workspaces for harness

This commit is contained in:
2026-08-05 16:03:45 +02:00
parent ece9cfda50
commit bd798b1c96
21 changed files with 659 additions and 69 deletions
+19 -5
View File
@@ -77,6 +77,12 @@ export function sessionRoutes(
return typeof runner.withPrincipal === "function" ? runner.withPrincipal(principal) : runner;
};
const optionsWithRuntimeConfig = (runner: any, workspaceConfigPath: string | undefined, options: any) => (
workspaceConfigPath && typeof runner.acquireWorkspaceRuntime === "function"
? { ...options, runtimeConfig: runner.acquireWorkspaceRuntime(workspaceConfigPath) }
: options
);
const maintenanceReply = (reply: any) => reply.code(503).send({
code: "maintenance",
error: "Session admission is temporarily paused for maintenance. Try again shortly.",
@@ -413,9 +419,11 @@ export function sessionRoutes(
principal,
question: b.question,
};
let runtimeOptions = options;
let rt: ReturnType<PiProcessManager["createFor"]> | undefined;
try {
rt = d.mgr.createFor(id, options);
runtimeOptions = optionsWithRuntimeConfig(runner, workspaceConfigPath, options);
rt = d.mgr.createFor(id, runtimeOptions);
bindRuntime(id, rt, runner, workspaceConfigPath);
} catch (error) {
if (rt) d.mgr.teardownIfCurrent(id, rt);
@@ -430,9 +438,9 @@ export function sessionRoutes(
}
info(id, "Session created");
bootstrap(
id, rt, runner, workspaceConfigPath, d.mgr.configure(rt, options),
id, rt, runner, workspaceConfigPath, d.mgr.configure(rt, runtimeOptions),
runner.searchPack(b.question, id, workspaceConfigPath),
() => d.mgr.start(id, rt, options),
() => d.mgr.start(id, rt, runtimeOptions),
);
return { id };
} finally {
@@ -560,6 +568,7 @@ export function sessionRoutes(
principal,
mode: "resume" as const,
};
let runtimeOptions = options;
// Reopening is validation, not the transport commit point. Keep the old hub intact if
// persistence cannot be reopened.
@@ -589,7 +598,8 @@ export function sessionRoutes(
if (boundRuntimes.get(id) === current) boundRuntimes.delete(id);
d.mgr.teardownIfCurrent(id, current);
}
rt = d.mgr.createFor(id, options);
runtimeOptions = optionsWithRuntimeConfig(runner, workspaceConfigPath, options);
rt = d.mgr.createFor(id, runtimeOptions);
bindRuntime(id, rt, runner, workspaceConfigPath);
} catch {
// A created-but-unbound runtime is not usable. The old hub remains attached because
@@ -605,7 +615,11 @@ export function sessionRoutes(
// immediately before the first event produced by the new Resume.
d.hub.clear(id);
info(id, "Resuming session");
bootstrap(id, rt, runner, workspaceConfigPath, d.mgr.configure(rt, options), null, () => d.mgr.start(id, rt, options));
bootstrap(
id, rt, runner, workspaceConfigPath,
d.mgr.configure(rt, runtimeOptions), null,
() => d.mgr.start(id, rt, runtimeOptions),
);
return reply.code(200).send({ id, alreadyActive: false });
});
});
+40 -11
View File
@@ -3,22 +3,49 @@ import type { ThtRunner } from "../tht/tht-runner.js";
import { getPrincipal } from "../auth/auth.js";
import type { PrincipalContext } from "../auth/principal.js";
import type { Settings } from "../settings/settings-store.js";
import type { WorkspaceRegistry } from "../workspaces/registry.js";
export function sqlRoutes(app: FastifyInstance, deps: {
tht: ThtRunner; getSettings: (principal: PrincipalContext) => Promise<Settings>;
workspaceRegistry: WorkspaceRegistry;
}): void {
const runnerFor = (principal: PrincipalContext): any => {
const runner = deps.tht as any;
return typeof runner.withPrincipal === "function" ? runner.withPrincipal(principal) : runner;
};
const authorize = async (principal: PrincipalContext, id: string, workspace?: string) => {
try {
const runner = runnerFor(principal);
if (typeof runner.sessionShow !== "function") return {};
return await runner.sessionShow(id, workspace);
const isNotFound = (error: unknown) => /not found|non trovata|inesistente|404/i.test(
error instanceof Error ? error.message : String(error),
);
const locate = async (principal: PrincipalContext, id: string, legacyWorkspace?: string) => {
const runner = runnerFor(principal);
if (typeof runner.sessionShow !== "function") return { manifest: {}, workspace: legacyWorkspace };
const registry = deps.workspaceRegistry as Partial<WorkspaceRegistry>;
const revisions = typeof registry.listRetainedSnapshots === "function"
? await registry.listRetainedSnapshots.call(deps.workspaceRegistry)
: await deps.workspaceRegistry.list();
for (const revision of revisions) {
if (revision.state !== "operational") continue;
try {
const manifest = await runner.sessionShow(id, revision.snapshotPath);
if (!manifest) continue;
const saved = manifest as { workspace_id?: string; workspace_revision?: string };
if (saved.workspace_id && saved.workspace_revision) {
const pinned = await deps.workspaceRegistry.readPinned(saved.workspace_id, saved.workspace_revision);
return {
manifest,
workspace: pinned.workspaceConfigPath ?? (pinned as any).revision?.snapshotPath,
};
}
return { manifest, workspace: revision.snapshotPath };
} catch (error) {
if (!isNotFound(error)) throw error;
}
}
catch (error) {
if (/not found|non trovata|inesistente|404/i.test(error instanceof Error ? error.message : String(error))) return undefined;
try {
const manifest = await runner.sessionShow(id, legacyWorkspace);
return manifest ? { manifest, workspace: legacyWorkspace } : undefined;
} catch (error) {
if (isNotFound(error)) return undefined;
throw error;
}
};
@@ -30,8 +57,9 @@ export function sqlRoutes(app: FastifyInstance, deps: {
try {
principal = getPrincipal(req);
const settings = await deps.getSettings(principal);
workspace = settings.workspace;
if (!await authorize(principal, id, settings.workspace)) return reply.code(404).send({ error: "session not found" });
const located = await locate(principal, id, settings.workspace);
if (!located) return reply.code(404).send({ error: "session not found" });
workspace = located.workspace;
} catch {
return reply.code(503).send({ error: "session storage is unavailable" });
}
@@ -49,8 +77,9 @@ export function sqlRoutes(app: FastifyInstance, deps: {
try {
principal = getPrincipal(req);
const settings = await deps.getSettings(principal);
workspace = settings.workspace;
if (!await authorize(principal, id, settings.workspace)) return reply.code(404).send({ error: "session not found" });
const located = await locate(principal, id, settings.workspace);
if (!located) return reply.code(404).send({ error: "session not found" });
workspace = located.workspace;
} catch {
return reply.code(503).send({ error: "session storage is unavailable" });
}