openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
162 lines (161 loc) • 7.35 kB
JavaScript
import "./operator-scopes-DsrPoRc4.mjs";
import { hr as validatePortalOpenParams, mr as validatePortalListParams, ni as validateSessionPortalListParams, pr as validatePortalCloseParams, ri as validateSessionPortalOpenParams, ti as validateSessionPortalCloseParams } from "./src-l2dWvEsN.mjs";
import { t as ErrorCodes } from "./gateway-error-details-DIefe7Zy.mjs";
import { f as errorShape } from "./error-codes-Dh56LniQ.mjs";
import { t as captureSessionPortalTarget } from "./session-portal-target-D54R2k-a.mjs";
import { r as defineValidatedGatewayMethod } from "./validation-Bu27N0nv.mjs";
import { n as resolveSessionEnvironmentCaller } from "./environments.session-BSQglBkO.mjs";
import { n as createPortalOperations, r as redactPortalSummary } from "./portal-service-DiyVo96I.mjs";
//#region src/gateway/server-methods/portals.session.ts
function sessionPortalOwner(options, environmentId) {
const access = options.sessionAccessAuthority;
const environments = options.context.workerEnvironmentService;
const service = options.context.portalService;
if (!access || !environments || !service) throw new Error("Session preview authority or transport is unavailable");
if (access.sandboxRequired) throw new Error("This conversation requires sandbox isolation; attached machine previews are unavailable");
const target = captureSessionPortalTarget(environments, {
...access.target,
sessionLifecycleRevision: access.target.lifecycleRevision
}, environmentId);
const assertInvocationCurrent = () => {
access.assertCurrent();
target.assertCurrent();
};
assertInvocationCurrent();
const { binding } = target;
const resourceOwnerKey = JSON.stringify([
binding.agentId,
binding.sessionKey,
binding.sessionId,
binding.sessionLifecycleRevision ?? null,
binding.generation
]);
return createPortalOperations(service, {
...binding,
resourceOwnerKey,
assertCurrent: assertInvocationCurrent,
ownershipError: "Portal does not belong to this conversation's attached environment",
async prepareTarget(remotePort) {
await target.touch();
assertInvocationCurrent();
const session = access.retainSession();
try {
session.assertCurrent();
const connection = await environments.openNodePortal({
environmentId: binding.environmentId,
ownerEpoch: binding.ownerEpoch,
remotePort
});
return {
assertCurrent: () => session.assertCurrent(),
ownerSignal: AbortSignal.any([session.signal, target.signal]),
connect: () => connection.connect(() => {
session.assertCurrent();
target.assertCurrent();
}, target.touch),
close: async () => {
try {
await connection.close();
} finally {
session.release();
}
}
};
} catch (error) {
session.release();
throw error;
}
}
}, () => broadcastPortalChange(options.context, service));
}
function sessionPortalError(error) {
return errorShape(ErrorCodes.INVALID_REQUEST, error instanceof Error ? error.message : "Session preview request failed");
}
function broadcastPortalChange(context, service) {
context.broadcast("portal.changed", { portals: service.list().map(redactPortalSummary) }, { dropIfSlow: true });
}
const sessionPortalHandlers = {
"portal.session.list": defineValidatedGatewayMethod("portal.session.list", validateSessionPortalListParams, (options) => {
const portals = sessionPortalOwner(options, options.params.environmentId);
options.respond(true, portals.list());
}, sessionPortalError),
"portal.session.open": defineValidatedGatewayMethod("portal.session.open", validateSessionPortalOpenParams, async (options) => {
const portals = sessionPortalOwner(options, options.params.environmentId);
options.respond(true, await portals.open(options.params));
}, sessionPortalError),
"portal.session.close": defineValidatedGatewayMethod("portal.session.close", validateSessionPortalCloseParams, async (options) => {
const portals = sessionPortalOwner(options, options.params.environmentId);
options.respond(true, await portals.close(options.params.id));
}, sessionPortalError)
};
//#endregion
//#region src/gateway/server-methods/portals.ts
function requirePortalService(context, respond) {
const service = context.portalService;
if (!service) respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "portals unavailable"));
return service;
}
function portalOperations(options, service, environmentId) {
if (!environmentId) return createPortalOperations(service);
const { context } = options;
const environments = context.workerEnvironmentService;
if (!environments) throw new Error("Conversation environments are unavailable");
const caller = resolveSessionEnvironmentCaller(options);
const binding = environments.findSessionAttachment(caller.identity);
if (!binding || binding.environmentId !== environmentId) throw new Error("Portal environment is not attached to this conversation");
const assertCurrent = () => {
caller.assertCurrent();
environments.assertSessionAttachment(binding);
};
assertCurrent();
return createPortalOperations(service, {
...binding,
assertCurrent,
ownershipError: "Portal does not belong to the attached environment",
async prepareTarget(remotePort) {
await environments.touchSessionAttachment(binding);
assertCurrent();
const connection = await environments.openNodePortal({
environmentId: binding.environmentId,
ownerEpoch: binding.ownerEpoch,
remotePort
});
return {
...connection,
connect: () => connection.connect(() => environments.assertSessionAttachment(binding), () => environments.touchSessionAttachment(binding))
};
}
});
}
async function mutatePortal(options, environmentId, mutate) {
const service = requirePortalService(options.context, options.respond);
if (!service) return;
try {
const result = await mutate(portalOperations(options, service, environmentId));
broadcastPortalChange(options.context, service);
options.respond(true, result, void 0);
} catch (error) {
options.respond(false, void 0, errorShape(ErrorCodes.UNAVAILABLE, error instanceof Error ? error.message : String(error)));
}
}
const portalHandlers = {
...sessionPortalHandlers,
"portal.list": defineValidatedGatewayMethod("portal.list", validatePortalListParams, (options) => {
const { params, respond, context, client } = options;
const service = requirePortalService(context, respond);
if (!service) return;
const scopes = Array.isArray(client?.connect?.scopes) ? client.connect.scopes : [];
let portals;
try {
portals = portalOperations(options, service, params.environmentId).list().portals;
} catch (error) {
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, String(error)));
return;
}
respond(true, { portals: scopes.includes("operator.write") || scopes.includes("operator.admin") ? portals : portals.map(redactPortalSummary) }, void 0);
}),
"portal.open": defineValidatedGatewayMethod("portal.open", validatePortalOpenParams, (options) => mutatePortal(options, options.params.environmentId, (portals) => portals.open(options.params))),
"portal.close": defineValidatedGatewayMethod("portal.close", validatePortalCloseParams, (options) => mutatePortal(options, options.params.environmentId, (portals) => portals.close(options.params.id)))
};
//#endregion
export { portalHandlers };