feat: add session subagent cli control
This commit is contained in:
+50
-2
@@ -1,10 +1,10 @@
|
||||
import type { Server } from "node:http";
|
||||
import { createServer } from "node:http";
|
||||
import type { AddressInfo } from "node:net";
|
||||
import type { AgentRunStore, ListQueueTasksInput } from "./store.js";
|
||||
import type { AgentRunStore, ListQueueTasksInput, ListSessionsInput, SessionEventPageInput } from "./store.js";
|
||||
import { openAgentRunStoreFromEnv } from "./store.js";
|
||||
import { AgentRunError, errorToJson } from "../common/errors.js";
|
||||
import { asRecord, validateCreateCommand, validateCreateQueueTask, validateCreateRun, validateQueueTaskState } from "../common/validation.js";
|
||||
import { asRecord, validateBackendProfile, validateCreateCommand, validateCreateQueueTask, validateCreateRun, validateQueueTaskState, validateSessionListState } from "../common/validation.js";
|
||||
import type { ApiErrorBody, ApiOkBody, JsonRecord, JsonValue, RunEvent } from "../common/types.js";
|
||||
import { createKubernetesRunnerJob } from "./kubernetes-runner-job.js";
|
||||
import { dispatchQueueTask, refreshQueueTaskFromCore } from "./queue-dispatch.js";
|
||||
@@ -69,6 +69,54 @@ async function route({ method, url, body, store, sourceCommit, runnerJobDefaults
|
||||
return { serviceId: "agentrun-mgr", live: true, ready, database, sourceCommit, secretRefs: { databaseUrl: database.adapter === "postgres" ? "redacted" : "not-used", valuesPrinted: false } };
|
||||
}
|
||||
if (method === "GET" && path === "/api/v1/backends") return { items: await store.backends() as unknown as JsonValue };
|
||||
if (method === "GET" && path === "/api/v1/sessions") {
|
||||
const input: ListSessionsInput = { limit: integerQuery(url, "limit", 50) };
|
||||
const state = url.searchParams.get("state");
|
||||
const backendProfile = url.searchParams.get("backendProfile") ?? url.searchParams.get("profile");
|
||||
const readerId = url.searchParams.get("readerId");
|
||||
const cursor = url.searchParams.get("cursor");
|
||||
if (state) input.state = validateSessionListState(state);
|
||||
if (backendProfile) input.backendProfile = validateBackendProfile(backendProfile);
|
||||
if (readerId) input.readerId = readerId;
|
||||
if (cursor) input.cursor = cursor;
|
||||
return await store.listSessions(input) as unknown as JsonValue;
|
||||
}
|
||||
const sessionMatch = path.match(/^\/api\/v1\/sessions\/([^/]+)$/u);
|
||||
if (method === "GET" && sessionMatch) return await store.getSessionSummary(sessionMatch[1] ?? "", url.searchParams.get("readerId")) as unknown as JsonValue;
|
||||
const sessionTraceMatch = path.match(/^\/api\/v1\/sessions\/([^/]+)\/trace$/u);
|
||||
if (method === "GET" && sessionTraceMatch) {
|
||||
const input: SessionEventPageInput = { afterSeq: integerQuery(url, "afterSeq", 0), limit: integerQuery(url, "limit", 100) };
|
||||
const runId = url.searchParams.get("runId");
|
||||
if (runId) input.runId = runId;
|
||||
return await store.listSessionTrace(sessionTraceMatch[1] ?? "", input) as unknown as JsonValue;
|
||||
}
|
||||
const sessionOutputMatch = path.match(/^\/api\/v1\/sessions\/([^/]+)\/output$/u);
|
||||
if (method === "GET" && sessionOutputMatch) {
|
||||
const input: SessionEventPageInput = { afterSeq: integerQuery(url, "afterSeq", 0), limit: integerQuery(url, "limit", 100) };
|
||||
const runId = url.searchParams.get("runId");
|
||||
if (runId) input.runId = runId;
|
||||
return await store.listSessionOutput(sessionOutputMatch[1] ?? "", input) as unknown as JsonValue;
|
||||
}
|
||||
const sessionReadMatch = path.match(/^\/api\/v1\/sessions\/([^/]+)\/read$/u);
|
||||
if (method === "POST" && sessionReadMatch) {
|
||||
const record = body === null ? {} : asRecord(body, "read");
|
||||
const readerId = typeof record.readerId === "string" && record.readerId.trim().length > 0 ? record.readerId.trim() : "cli";
|
||||
return await store.markSessionRead(sessionReadMatch[1] ?? "", readerId) as unknown as JsonValue;
|
||||
}
|
||||
const sessionControlMatch = path.match(/^\/api\/v1\/sessions\/([^/]+)\/control$/u);
|
||||
if (method === "POST" && sessionControlMatch) {
|
||||
const record = asRecord(body ?? {}, "sessionControl");
|
||||
const action = typeof record.action === "string" ? record.action : "";
|
||||
const session = await store.getSessionSummary(sessionControlMatch[1] ?? "", typeof record.readerId === "string" ? record.readerId : null);
|
||||
if (action === "read") return await store.markSessionRead(session.sessionId, typeof record.readerId === "string" && record.readerId.trim().length > 0 ? record.readerId.trim() : "cli") as unknown as JsonValue;
|
||||
if (action === "cancel") {
|
||||
const reason = typeof record.reason === "string" && record.reason.trim().length > 0 ? record.reason.trim() : undefined;
|
||||
if (session.activeCommandId) return await store.cancelCommand(session.activeCommandId, reason) as unknown as JsonValue;
|
||||
if (session.activeRunId) return await store.cancelRun(session.activeRunId, reason) as unknown as JsonValue;
|
||||
throw new AgentRunError("schema-invalid", `session ${session.sessionId} has no active run or command`, { httpStatus: 409 });
|
||||
}
|
||||
throw new AgentRunError("schema-invalid", `session control action ${action} is not supported`, { httpStatus: 400 });
|
||||
}
|
||||
if (method === "POST" && path === "/api/v1/queue/tasks") return await store.createQueueTask(validateCreateQueueTask(body)) as unknown as JsonValue;
|
||||
if (method === "GET" && path === "/api/v1/queue/tasks") {
|
||||
const state = url.searchParams.get("state");
|
||||
|
||||
Reference in New Issue
Block a user