UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

327 lines (326 loc) • 14.2 kB
import { l as normalizeOptionalString } from "./string-coerce-6kR-86XC.mjs"; import { c as isRecord } from "./record-coerce-TIsquoVR.mjs"; import { r as truncateUtf16Safe } from "./utf16-slice-D_ngcYKd.mjs"; import { b as redactToolPayloadText } from "./redact-BEIE6NCT.mjs"; import "./utils-DUAwk2p5.mjs"; import { o as getActiveSecretsRuntimeConfigSnapshot } from "./runtime-state-DAdPNuC3.mjs"; import { t as ErrorCodes } from "./gateway-error-details-DIefe7Zy.mjs"; import { f as errorShape } from "./error-codes-DI20vDEB.mjs"; import { n as resolveRequestedSessionAgentId } from "./session-request-agent-D8Xj-iBG.mjs"; import { N as GitHubIdentityError, c as prepareGitHubReadIdentity, p as resolveConfiguredGitHubToolIdentity } from "./github-tool-identity-BrGsBuH4.mjs"; import { c as getSubagentSessionListReadSnapshotIdentity, v as prepareSubagentSessionListReadCache } from "./subagent-registry-state-BfMfyRYx.mjs"; import { n as getSessionRowProjection } from "./session-row-projection-access-CUCrJD4M.mjs"; import { r as requestCurrentGitHubOAuthRefresh } from "./github-oauth-lifecycle-DWRW4csa.mjs"; import { N as buildGatewaySessionRow } from "./session-row-prepared-read-CsgaLEXi.mjs"; import { b as createSessionListEntryFilter } from "./session-sharing-KNQSWoOJ.mjs"; import "./session-utils-DrY1lc9W.mjs"; import { n as gitHubPublicApi } from "./github-public-api-D6N-IT-v.mjs"; import { a as loadSessionEntriesForTarget } from "./sessions-shared-B4EdIFDR.mjs"; import { t as resolveAgentIdOrRespondError } from "./agent-id-shared-CCm6C9lW.mjs"; import { a as resolveControlUiSessionPrTarget, i as prepareControlUiSessionPrRead, n as parseControlUiSessionPullRequestsSubscribeParams, r as withControlUiSessionPrSource } from "./control-ui-session-pr-subscriptions--FLgDreo.mjs"; //#region src/gateway/server-methods/control-ui.ts var GitHubReadRequestInactiveError = class extends Error { constructor() { super("GitHub request is no longer active. Try again."); } }; async function prepareControlUiGitHubIdentity({ context, client, signal, hasCurrentClientAuthority }, agentId) { const config = context.getRuntimeConfig(); const configuredIdentity = () => { const current = context.getRuntimeConfig(); return resolveConfiguredGitHubToolIdentity({ config: current, agentId, scope: "agent" }) ?? resolveConfiguredGitHubToolIdentity({ config: current, agentId, scope: "system" }); }; const assertActive = () => { if (signal?.aborted || (hasCurrentClientAuthority ? !hasCurrentClientAuthority() : client?.connId && !context.getClientConnIds?.((current) => current === client).has(client.connId))) throw new GitHubReadRequestInactiveError(); }; assertActive(); const identity = configuredIdentity() ? await prepareGitHubReadIdentity({ config, sourceConfig: getActiveSecretsRuntimeConfigSnapshot()?.sourceConfig ?? config, agentId, getCurrentConfig: () => context.getRuntimeConfig(), assertActive, refresh: () => requestCurrentGitHubOAuthRefresh(agentId) }) : void 0; return { identity, assertSelected: identity?.assertSelected ?? (() => { assertActive(); if (configuredIdentity()) throw new GitHubIdentityError("changed"); }) }; } function createGitHubReadHandler(method, parseTarget, load) { return async (options) => { const { params, respond, context } = options; const target = parseTarget(params); if (!target || params.refresh !== void 0 && typeof params.refresh !== "boolean") { respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid ${method} params`)); return; } const resolved = resolveAgentIdOrRespondError({ rawAgentId: params.agentId, respond, cfg: context.getRuntimeConfig(), normalize: normalizeOptionalString }); if (!resolved) return; try { const { identity, assertSelected } = await prepareControlUiGitHubIdentity(options, resolved.agentId); assertSelected(); const result = params.refresh === true ? await load(target, identity, void 0, true) : await load(target, identity); assertSelected(); respond(true, result, void 0); } catch (error) { const { message, ...details } = error instanceof GitHubReadRequestInactiveError ? { message: error.message, retryable: true } : error instanceof GitHubIdentityError ? { message: error.message, retryable: error.reason !== "unavailable" } : gitHubPublicApi.formatControlUiGitHubPreviewError(error); respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, message, details)); } }; } const SESSION_PREVIEW_TEXT_MAX_CHARS = 200; function boundedPreviewText(value, maxChars = SESSION_PREVIEW_TEXT_MAX_CHARS) { const trimmed = value?.trim(); return trimmed ? truncateUtf16Safe(trimmed, maxChars) : void 0; } function parseSessionPreviewKey(params) { if (!isRecord(params) || Object.keys(params).some((key) => key !== "sessionKey")) return null; const sessionKey = typeof params.sessionKey === "string" ? params.sessionKey.trim() : ""; return sessionKey && sessionKey.length <= 512 ? sessionKey : null; } function projectSessionPreview(source) { if (!source) return { status: "unavailable" }; const lastMessagePreview = boundedPreviewText(source.lastMessagePreview ? redactToolPayloadText(source.lastMessagePreview) : void 0); const title = boundedPreviewText(source.title); const derivedTitle = boundedPreviewText(source.derivedTitle); const kind = boundedPreviewText(source.kind, 64); const channel = boundedPreviewText(source.channel, 80); return { status: "ok", sessionKey: source.sessionKey, agentId: source.agentId, ...title ? { title } : {}, ...derivedTitle ? { derivedTitle } : {}, ...kind ? { kind } : {}, ...channel ? { channel } : {}, ...typeof source.updatedAt === "number" && Number.isFinite(source.updatedAt) ? { updatedAt: source.updatedAt } : {}, ...lastMessagePreview ? { lastMessagePreview } : {}, ...typeof source.archived === "boolean" ? { archived: source.archived } : {} }; } function loadControlUiSessionPreview(sessionKey, context, client) { const cfg = context.getRuntimeConfig(); const requestedAgent = resolveRequestedSessionAgentId(cfg, sessionKey); if (!requestedAgent.ok) return null; const { target, storePath, store, entry } = loadSessionEntriesForTarget({ key: sessionKey, cfg, ...requestedAgent.agentId ? { agentId: requestedAgent.agentId } : {} }); if (!entry) return null; const entryFilter = createSessionListEntryFilter({ client, cfg }); if (entryFilter && !entryFilter(target.canonicalKey, entry)) return null; const row = buildGatewaySessionRow({ cfg, agentId: target.agentId, storePath, store, key: target.canonicalKey, entry, includeDerivedTitles: true, includeLastMessage: true, skipTranscriptUsageFallback: true }); return { sessionKey: row.key, agentId: target.agentId, title: row.displayName, derivedTitle: row.derivedTitle, kind: row.kind, channel: row.channel, updatedAt: row.updatedAt, lastMessagePreview: row.lastMessagePreview, archived: row.archived }; } function parseCheckDetailsParams(params) { if (Object.keys(params).some((key) => ![ "sessionKey", "owner", "repo", "number", "headSha" ].includes(key))) return null; const sessionKey = typeof params.sessionKey === "string" ? params.sessionKey.trim() : ""; const headSha = typeof params.headSha === "string" ? params.headSha : ""; const target = gitHubPublicApi.parseControlUiGitHubPreviewTarget({ ...params, kind: "pull" }); return target && sessionKey && sessionKey.length <= 512 && /^[0-9a-f]{40}$/i.test(headSha) ? { sessionKey, owner: target.owner, repo: target.repo, number: target.number, headSha: headSha.toLowerCase() } : null; } function resolveCheckDetailsSession(sessionKey, context, client) { const cfg = context.getRuntimeConfig(); const requested = resolveRequestedSessionAgentId(cfg, sessionKey); if (!requested.ok) return null; const { target, entry, storePath } = loadSessionEntriesForTarget({ key: sessionKey, cfg, agentId: requested.agentId }); const entryFilter = createSessionListEntryFilter({ client, cfg }); if (!entry?.sessionId || entryFilter && !entryFilter(target.canonicalKey, entry)) return null; return resolveControlUiSessionPrTarget({ cfg, agentId: target.agentId, canonicalKey: target.canonicalKey, storePath, readSource: target.readSource, entry }) ?? null; } const loadSessionCheckDetails = async (params, deps) => { deps.assertCurrent(); const { loadControlUiSessionPullRequestChecks } = await import("./control-ui-session-pr-check-details-DUN8g_c3.mjs"); const { loadControlUiSessionPullRequests } = await import("./control-ui-session-prs-CRuZ7HPC.mjs"); return loadControlUiSessionPullRequestChecks(params, { ...deps, loadPullRequests: (request, options) => loadControlUiSessionPullRequests(request, { ...options, read: deps.read }) }); }; function createControlUiHandlers(loadGitHubPreview = (...args) => gitHubPublicApi.loadControlUiGitHubPreview(...args), loadSessionPreview = loadControlUiSessionPreview, loadChecks = loadSessionCheckDetails) { return { "controlUi.linkPreview": async ({ params, context, respond, signal }) => { const isEnabled = () => context.getRuntimeConfig().gateway?.controlUi?.automaticallyFetchFavicons !== false; if (!isEnabled()) { respond(true, {}, void 0); return; } const { parseControlUiLinkPreviewUrl, loadControlUiLinkPreview } = await import("./control-ui-link-preview-zFIPwA5p.mjs"); const url = Object.keys(params).every((key) => key === "url") ? parseControlUiLinkPreviewUrl(params.url) : null; if (!url) { respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "invalid controlUi.linkPreview params")); return; } const preview = await loadControlUiLinkPreview(url, isEnabled); respond(true, !signal?.aborted && isEnabled() ? preview : {}, void 0); }, "controlUi.githubPreview": createGitHubReadHandler("controlUi.githubPreview", (params) => gitHubPublicApi.parseControlUiGitHubPreviewTarget(params), loadGitHubPreview), "controlUi.githubDetail": createGitHubReadHandler("controlUi.githubDetail", (params) => gitHubPublicApi.parseGitHubTarget(params), (...args) => gitHubPublicApi.loadGitHubDetail(...args)), "controlUi.sessionPreview": async ({ params, client, context, respond, signal }) => { const sessionKey = parseSessionPreviewKey(params); if (!sessionKey) { respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "invalid controlUi.sessionPreview params")); return; } try { while (!getSubagentSessionListReadSnapshotIdentity()) await prepareSubagentSessionListReadCache(); signal?.throwIfAborted(); respond(true, projectSessionPreview(loadSessionPreview(sessionKey, context, client)), void 0); } catch { respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, "Session preview unavailable")); } }, "controlUi.sessionPullRequests.checks": async ({ params, client, context, respond, signal }) => { const parsed = parseCheckDetailsParams(params); if (!parsed) { respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "invalid controlUi.sessionPullRequests.checks params")); return; } try { const reader = client ? await prepareControlUiSessionPrRead({ client, sessionKey: parsed.sessionKey, getRuntimeConfig: context.getRuntimeConfig, getSessionRowProjection: () => getSessionRowProjection(context), isCurrentClient: () => !client.connId || context.getClientConnIds?.((candidate) => candidate === client).has(client.connId) === true }) : void 0; const currentBinding = async () => { if (!client) return resolveCheckDetailsSession(parsed.sessionKey, context, client); return await reader?.() ?? null; }; const binding = await currentBinding(); if (!binding) throw new gitHubPublicApi.ControlUiGitHubError(404, "Session CI details unavailable"); const assertCurrent = () => { let identityCurrent = false; try { binding.assertCurrent?.(); identityCurrent = client ? true : resolveCheckDetailsSession(parsed.sessionKey, context, client)?.identity === binding.identity; } catch {} if (signal?.aborted || !identityCurrent) throw new gitHubPublicApi.ControlUiGitHubError(409, "Session changed; reopen CI details"); }; await withControlUiSessionPrSource(binding.readSource, async (assertSourceCurrent, sourceIdentity) => { const assertReadCurrent = () => { assertSourceCurrent(); assertCurrent(); }; const result = await loadChecks({ ...parsed, agentId: binding.params.agentId }, { sessionScope: binding.identity, assertCurrent: assertReadCurrent, read: { target: binding, sourceIdentity, assertCurrent: assertReadCurrent } }); assertReadCurrent(); respond(true, result, void 0); }); } catch (error) { const message = error instanceof gitHubPublicApi.ControlUiGitHubError && (error.statusCode === 404 || error.statusCode === 409) ? error.message : gitHubPublicApi.formatControlUiGitHubPreviewError(error).message; respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, message)); } }, "controlUi.sessionPullRequests.subscribe": async ({ params, client, context, respond }) => { const parsed = parseControlUiSessionPullRequestsSubscribeParams(params); if (!parsed) { respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "invalid controlUi.sessionPullRequests.subscribe params")); return; } const connId = client?.connId?.trim(); const subscriptions = context.controlUiSessionPullRequests; if (!connId || !subscriptions) { respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, "session pull request subscriptions unavailable")); return; } await new Promise((resolve) => { subscriptions.replace(connId, parsed.sessionKeys, parsed.refreshSessionKeys.length > 0 ? new Set(parsed.refreshSessionKeys) : void 0, resolve).catch(() => {}); }); respond(true, { subscribed: parsed.sessionKeys.length > 0 }, void 0); } }; } const controlUiHandlers = createControlUiHandlers(); //#endregion export { controlUiHandlers };