diff --git a/src/gateway/server-bridge.ts b/src/gateway/server-bridge.ts index b2df674e6..85147d62e 100644 --- a/src/gateway/server-bridge.ts +++ b/src/gateway/server-bridge.ts @@ -80,6 +80,7 @@ import { type SessionsPatchResult, } from "./session-utils.js"; import { applySessionsPatchToStore } from "./sessions-patch.js"; +import { resolveSessionKeyFromResolveParams } from "./sessions-resolve.js"; import { formatForLog } from "./ws-log.js"; export type BridgeHandlersContext = { @@ -314,93 +315,20 @@ export function createBridgeHandlers(ctx: BridgeHandlersContext) { const p = params as SessionsResolveParams; const cfg = loadConfig(); - - const key = typeof p.key === "string" ? p.key.trim() : ""; - const label = typeof p.label === "string" ? p.label.trim() : ""; - const hasKey = key.length > 0; - const hasLabel = label.length > 0; - if (hasKey && hasLabel) { + const resolved = resolveSessionKeyFromResolveParams({ cfg, p }); + if (!resolved.ok) { return { ok: false, error: { - code: ErrorCodes.INVALID_REQUEST, - message: "Provide either key or label (not both)", - }, - }; - } - if (!hasKey && !hasLabel) { - return { - ok: false, - error: { - code: ErrorCodes.INVALID_REQUEST, - message: "Either key or label is required", - }, - }; - } - - if (hasKey) { - const target = resolveGatewaySessionStoreTarget({ cfg, key }); - const store = loadSessionStore(target.storePath); - const existingKey = target.storeKeys.find( - (candidate) => store[candidate], - ); - if (!existingKey) { - return { - ok: false, - error: { - code: ErrorCodes.INVALID_REQUEST, - message: `No session found: ${key}`, - }, - }; - } - return { - ok: true, - payloadJSON: JSON.stringify({ - ok: true, - key: target.canonicalKey, - }), - }; - } - - const { storePath, store } = loadCombinedSessionStoreForGateway(cfg); - const list = listSessionsFromStore({ - cfg, - storePath, - store, - opts: { - includeGlobal: p.includeGlobal === true, - includeUnknown: p.includeUnknown === true, - label, - agentId: p.agentId, - spawnedBy: p.spawnedBy, - limit: 2, - }, - }); - if (list.sessions.length === 0) { - return { - ok: false, - error: { - code: ErrorCodes.INVALID_REQUEST, - message: `No session found with label: ${label}`, - }, - }; - } - if (list.sessions.length > 1) { - const keys = list.sessions.map((s) => s.key).join(", "); - return { - ok: false, - error: { - code: ErrorCodes.INVALID_REQUEST, - message: `Multiple sessions found with label: ${label} (${keys})`, + code: resolved.error.code, + message: resolved.error.message, + details: resolved.error.details, }, }; } return { ok: true, - payloadJSON: JSON.stringify({ - ok: true, - key: list.sessions[0]?.key, - }), + payloadJSON: JSON.stringify({ ok: true, key: resolved.key }), }; } case "sessions.patch": { diff --git a/src/gateway/server-methods/sessions.ts b/src/gateway/server-methods/sessions.ts index ffde2e839..ba95cb1e9 100644 --- a/src/gateway/server-methods/sessions.ts +++ b/src/gateway/server-methods/sessions.ts @@ -35,6 +35,7 @@ import { type SessionsPatchResult, } from "../session-utils.js"; import { applySessionsPatchToStore } from "../sessions-patch.js"; +import { resolveSessionKeyFromResolveParams } from "../sessions-resolve.js"; import type { GatewayRequestHandlers } from "./types.js"; export const sessionsHandlers: GatewayRequestHandlers = { @@ -76,106 +77,12 @@ export const sessionsHandlers: GatewayRequestHandlers = { const p = params as import("../protocol/index.js").SessionsResolveParams; const cfg = loadConfig(); - const key = typeof p.key === "string" ? p.key.trim() : ""; - const label = typeof p.label === "string" ? p.label.trim() : ""; - const hasKey = key.length > 0; - const hasLabel = label.length > 0; - if (hasKey && hasLabel) { - respond( - false, - undefined, - errorShape( - ErrorCodes.INVALID_REQUEST, - "Provide either key or label (not both)", - ), - ); + const resolved = resolveSessionKeyFromResolveParams({ cfg, p }); + if (!resolved.ok) { + respond(false, undefined, resolved.error); return; } - if (!hasKey && !hasLabel) { - respond( - false, - undefined, - errorShape( - ErrorCodes.INVALID_REQUEST, - "Either key or label is required", - ), - ); - return; - } - - if (hasKey) { - if (!key) { - respond( - false, - undefined, - errorShape(ErrorCodes.INVALID_REQUEST, "key required"), - ); - return; - } - const target = resolveGatewaySessionStoreTarget({ cfg, key }); - const store = loadSessionStore(target.storePath); - const existingKey = target.storeKeys.find( - (candidate) => store[candidate], - ); - if (!existingKey) { - respond( - false, - undefined, - errorShape(ErrorCodes.INVALID_REQUEST, `No session found: ${key}`), - ); - return; - } - respond(true, { ok: true, key: target.canonicalKey }, undefined); - return; - } - - if (!label) { - respond( - false, - undefined, - errorShape(ErrorCodes.INVALID_REQUEST, "label required"), - ); - return; - } - - const { storePath, store } = loadCombinedSessionStoreForGateway(cfg); - const list = listSessionsFromStore({ - cfg, - storePath, - store, - opts: { - includeGlobal: p.includeGlobal === true, - includeUnknown: p.includeUnknown === true, - label, - agentId: p.agentId, - spawnedBy: p.spawnedBy, - limit: 2, - }, - }); - if (list.sessions.length === 0) { - respond( - false, - undefined, - errorShape( - ErrorCodes.INVALID_REQUEST, - `No session found with label: ${label}`, - ), - ); - return; - } - if (list.sessions.length > 1) { - const keys = list.sessions.map((s) => s.key).join(", "); - respond( - false, - undefined, - errorShape( - ErrorCodes.INVALID_REQUEST, - `Multiple sessions found with label: ${label} (${keys})`, - ), - ); - return; - } - respond(true, { ok: true, key: list.sessions[0]?.key }, undefined); + respond(true, { ok: true, key: resolved.key }, undefined); }, "sessions.patch": async ({ params, respond, context }) => { if (!validateSessionsPatchParams(params)) { diff --git a/src/gateway/sessions-resolve.ts b/src/gateway/sessions-resolve.ts new file mode 100644 index 000000000..5f6fa05b7 --- /dev/null +++ b/src/gateway/sessions-resolve.ts @@ -0,0 +1,107 @@ +import type { ClawdbotConfig } from "../config/config.js"; +import { loadSessionStore } from "../config/sessions.js"; +import { parseSessionLabel } from "../sessions/session-label.js"; +import { + ErrorCodes, + type ErrorShape, + errorShape, + type SessionsResolveParams, +} from "./protocol/index.js"; +import { + listSessionsFromStore, + loadCombinedSessionStoreForGateway, + resolveGatewaySessionStoreTarget, +} from "./session-utils.js"; + +export type SessionsResolveResult = + | { ok: true; key: string } + | { ok: false; error: ErrorShape }; + +export function resolveSessionKeyFromResolveParams(params: { + cfg: ClawdbotConfig; + p: SessionsResolveParams; +}): SessionsResolveResult { + const { cfg, p } = params; + + const key = typeof p.key === "string" ? p.key.trim() : ""; + const hasKey = key.length > 0; + const hasLabel = typeof p.label === "string" && p.label.trim().length > 0; + if (hasKey && hasLabel) { + return { + ok: false, + error: errorShape( + ErrorCodes.INVALID_REQUEST, + "Provide either key or label (not both)", + ), + }; + } + if (!hasKey && !hasLabel) { + return { + ok: false, + error: errorShape( + ErrorCodes.INVALID_REQUEST, + "Either key or label is required", + ), + }; + } + + if (hasKey) { + const target = resolveGatewaySessionStoreTarget({ cfg, key }); + const store = loadSessionStore(target.storePath); + const existingKey = target.storeKeys.find((candidate) => store[candidate]); + if (!existingKey) { + return { + ok: false, + error: errorShape( + ErrorCodes.INVALID_REQUEST, + `No session found: ${key}`, + ), + }; + } + return { ok: true, key: target.canonicalKey }; + } + + const parsedLabel = parseSessionLabel(p.label); + if (!parsedLabel.ok) { + return { + ok: false, + error: errorShape(ErrorCodes.INVALID_REQUEST, parsedLabel.error), + }; + } + + const { storePath, store } = loadCombinedSessionStoreForGateway(cfg); + const list = listSessionsFromStore({ + cfg, + storePath, + store, + opts: { + includeGlobal: p.includeGlobal === true, + includeUnknown: p.includeUnknown === true, + label: parsedLabel.label, + agentId: p.agentId, + spawnedBy: p.spawnedBy, + limit: 2, + }, + }); + if (list.sessions.length === 0) { + return { + ok: false, + error: errorShape( + ErrorCodes.INVALID_REQUEST, + `No session found with label: ${parsedLabel.label}`, + ), + }; + } + if (list.sessions.length > 1) { + const keys = list.sessions.map((s) => s.key).join(", "); + return { + ok: false, + error: errorShape( + ErrorCodes.INVALID_REQUEST, + `Multiple sessions found with label: ${parsedLabel.label} (${keys})`, + ), + }; + } + + return { ok: true, key: String(list.sessions[0]?.key ?? "") }; +}