Files
pi_harness/packages/orchestrator/src/supervisor.ts
T
Cristina Poncela Cubeiro 8bc92fc90b feat: rpc bridge
2026-06-18 13:32:17 +02:00

112 lines
2.9 KiB
TypeScript

import { randomUUID } from "node:crypto";
import {
type AgentSessionRuntime,
type CreateAgentSessionRuntimeFactory,
createAgentSessionFromServices,
createAgentSessionRuntime,
createAgentSessionServices,
getAgentDir,
type RpcCommand,
type RpcResponse,
SessionManager,
} from "@earendil-works/pi-coding-agent";
import { handleRpcCommand } from "./rpc-bridge.ts";
import { getInstance, loadInstances, removeInstance, upsertInstance } from "./storage.ts";
import type { InstanceRecord } from "./types.ts";
interface LiveInstance {
runtime: AgentSessionRuntime;
record: InstanceRecord;
}
function cloneInstance(record: InstanceRecord): InstanceRecord {
return { ...record };
}
async function createRuntime(cwd: string): Promise<AgentSessionRuntime> {
const agentDir = getAgentDir();
const sessionManager = SessionManager.inMemory(cwd);
const runtimeFactory: CreateAgentSessionRuntimeFactory = async ({
cwd,
agentDir,
sessionManager,
sessionStartEvent,
}) => {
const services = await createAgentSessionServices({ cwd, agentDir });
const created = await createAgentSessionFromServices({
services,
sessionManager,
sessionStartEvent,
});
return {
...created,
services,
diagnostics: services.diagnostics,
};
};
return createAgentSessionRuntime(runtimeFactory, {
cwd,
agentDir,
sessionManager,
});
}
export class OrchestratorSupervisor {
private readonly liveInstances = new Map<string, LiveInstance>();
listInstances(): InstanceRecord[] {
return loadInstances().map(cloneInstance);
}
getInstance(instanceId: string): InstanceRecord | undefined {
const live = this.liveInstances.get(instanceId);
if (live) {
return cloneInstance(live.record);
}
const stored = getInstance(instanceId);
return stored ? cloneInstance(stored) : undefined;
}
async spawnInstance(options: { cwd: string; label?: string }): Promise<InstanceRecord> {
const runtime = await createRuntime(options.cwd);
const now = new Date().toISOString();
const record: InstanceRecord = {
id: randomUUID(),
status: "online",
cwd: options.cwd,
createdAt: now,
lastSeenAt: now,
label: options.label,
sessionId: runtime.session.sessionId,
};
this.liveInstances.set(record.id, { runtime, record });
upsertInstance(record);
return cloneInstance(record);
}
async stopInstance(instanceId: string): Promise<InstanceRecord | undefined> {
const live = this.liveInstances.get(instanceId);
if (!live) {
return undefined;
}
await live.runtime.dispose();
this.liveInstances.delete(instanceId);
removeInstance(instanceId);
return cloneInstance(live.record);
}
async handleRpc(instanceId: string, command: RpcCommand): Promise<RpcResponse | undefined> {
const live = this.liveInstances.get(instanceId);
if (!live) {
return undefined;
}
return handleRpcCommand(live.runtime, command);
}
}
export const supervisor = new OrchestratorSupervisor();