diff --git a/scripts/src/cli.ts b/scripts/src/cli.ts index 052cdd0..9ffc077 100644 --- a/scripts/src/cli.ts +++ b/scripts/src/cli.ts @@ -40,6 +40,9 @@ async function dispatch(args: ParsedArgs): Promise { if (group === "backends" && command === "list") return client(args).get("/api/v1/backends"); if (group === "secrets" && command === "codex" && id === "render") return renderCodexSecret(args); if (group === "sessions" && command === "ps") return listSessions(args); + if (group === "sessions" && command === "create") return sessionCreate(args, id ?? null); + if (group === "sessions" && command === "storage" && id) return sessionStorageGet(args, id); + if (group === "sessions" && command === "storage" && !id) throw new AgentRunError("schema-invalid", "sessions storage requires a sessionId", { httpStatus: 2 }); if (group === "sessions" && command === "show" && id) return client(args).get(`/api/v1/sessions/${encodeURIComponent(id)}${readerQuery(args)}`); if (group === "sessions" && command === "read" && id) return client(args).post(`/api/v1/sessions/${encodeURIComponent(id)}/read`, { readerId: optionalFlag(args, "reader-id") ?? "cli" }); if (group === "sessions" && command === "trace" && id) return sessionEvents(args, id, "trace"); @@ -47,6 +50,8 @@ async function dispatch(args: ParsedArgs): Promise { if (group === "sessions" && command === "turn") return sessionTurn(args, id ?? null); if (group === "sessions" && command === "steer" && id) return sessionSteer(args, id); if (group === "sessions" && command === "cancel" && id) return sessionCancel(args, id); + const sessionStorageCmd = group === "sessions" && (command === "storage-delete" || (command === "storage" && id && optionalFlag(args, "delete") === "true")); + if (sessionStorageCmd && id) return sessionStorageDelete(args, id); if (group === "queue" && command === "submit") return submitQueueTask(args); if (group === "queue" && command === "list") return listQueueTasks(args); if (group === "queue" && command === "show" && id) return client(args).get(`/api/v1/queue/tasks/${encodeURIComponent(id)}`); @@ -148,11 +153,63 @@ async function sessionEvents(args: ParsedArgs, sessionId: string, kind: "trace" return client(args).get(`/api/v1/sessions/${encodeURIComponent(sessionId)}/${kind}${query ? `?${query}` : ""}`); } +async function sessionCreate(args: ParsedArgs, positionalSessionId: string | null): Promise { + const sessionId = positionalSessionId ?? optionalFlag(args, "session-id") ?? newSessionId(); + const profile = normalizeProfile(optionalFlag(args, "profile") ?? optionalFlag(args, "backend-profile") ?? "codex"); + const tenantId = optionalFlag(args, "tenant-id") ?? "unidesk"; + const projectId = optionalFlag(args, "project-id") ?? "default"; + const providerId = optionalFlag(args, "provider-id") ?? "G14"; + const expiresInDays = Number(optionalFlag(args, "expires-in-days") ?? 30); + const expiresAt = new Date(Date.now() + Math.max(1, expiresInDays) * 24 * 60 * 60 * 1000).toISOString(); + const created = await client(args).post("/api/v1/sessions", { + sessionId, + tenantId, + projectId, + backendProfile: profile, + expiresAt, + }) as { action: string; pvc: { pvcName: string; pvcPhase: string }; session: { sessionId: string; storageKind: string; codexRolloutSubdir: string } }; + const storage = await client(args).get(`/api/v1/sessions/${encodeURIComponent(sessionId)}/storage`) as { pvcName: string; pvcPhase: string; storageSizeBytes: number | null }; + return { + action: created.action, + session: created.session, + pvc: created.pvc, + storage, + pollCommands: { + show: `./scripts/agentrun sessions show ${sessionId} --reader-id cli`, + storage: `./scripts/agentrun sessions storage ${sessionId}`, + trace: `./scripts/agentrun sessions trace ${sessionId} --after-seq 0 --limit 100`, + turn: `./scripts/agentrun sessions turn ${sessionId} --prompt "..."`, + }, + }; +} + +async function sessionStorageGet(args: ParsedArgs, sessionId: string): Promise { + return (await client(args).get(`/api/v1/sessions/${encodeURIComponent(sessionId)}/storage`)) as JsonRecord; +} + +async function sessionStorageDelete(args: ParsedArgs, sessionId: string): Promise { + return (await client(args).delete(`/api/v1/sessions/${encodeURIComponent(sessionId)}/storage`)) as JsonRecord; +} + async function sessionTurn(args: ParsedArgs, positionalSessionId: string | null): Promise { const body = await optionalJsonFile(args); const sessionId = positionalSessionId ?? optionalFlag(args, "session-id") ?? newSessionId(); const requestedProfile = optionalFlag(args, "profile") ?? optionalFlag(args, "backend-profile") ?? (typeof body.backendProfile === "string" ? String(body.backendProfile) : "codex"); const profile = normalizeProfile(requestedProfile); + if (positionalSessionId || optionalFlag(args, "session-id")) { + try { + await client(args).get(`/api/v1/sessions/${encodeURIComponent(sessionId)}/storage`); + } catch (error) { + const expiresInDays = Number(optionalFlag(args, "expires-in-days") ?? 30); + await client(args).post("/api/v1/sessions", { + sessionId, + tenantId: optionalFlag(args, "tenant-id") ?? "unidesk", + projectId: optionalFlag(args, "project-id") ?? "default", + backendProfile: profile, + expiresAt: new Date(Date.now() + Math.max(1, expiresInDays) * 24 * 60 * 60 * 1000).toISOString(), + }); + } + } const prompt = await readPrompt(args); body.tenantId = optionalFlag(args, "tenant-id") ?? stringField(body, "tenantId", "unidesk"); body.projectId = optionalFlag(args, "project-id") ?? stringField(body, "projectId", "default"); @@ -625,6 +682,9 @@ function help(): JsonRecord { "runs result [--command-id ]", "runs cancel [--reason ]", "sessions ps [--state default|running|unread|terminal|idle|all] [--profile codex|deepseek|minimax-m3|M3] [--reader-id ]", + "sessions create [sessionId] [--profile codex|deepseek|minimax-m3|M3] [--expires-in-days ]", + "sessions storage ", + "sessions storage --delete", "sessions show [--reader-id ]", "sessions turn [sessionId] --json-file --prompt-file [--profile minimax-m3|M3] [--runner-json-file ]", "sessions steer --prompt-file ", diff --git a/src/mgr/client.ts b/src/mgr/client.ts index 306dafb..b0f3dd2 100644 --- a/src/mgr/client.ts +++ b/src/mgr/client.ts @@ -16,6 +16,10 @@ export class ManagerClient { return this.request("PATCH", path, body); } + async delete(path: string): Promise { + return this.request("DELETE", path); + } + private async request(method: string, path: string, body?: JsonValue): Promise { const init: RequestInit = { method }; if (body !== undefined) {