4fa4f13558
Co-authored-by: iamlukethedev <iamlukethedev@users.noreply.github.com>
107 lines
3.5 KiB
TypeScript
107 lines
3.5 KiB
TypeScript
import type { AgentState } from "@/features/agents/state/store";
|
|
import type { EventFrame } from "@/lib/gateway/GatewayClient";
|
|
import type { ExecApprovalDecision } from "@/features/agents/approvals/types";
|
|
|
|
type RequestedPayload = {
|
|
id: string;
|
|
request: {
|
|
command: string;
|
|
cwd: string | null;
|
|
host: string | null;
|
|
security: string | null;
|
|
ask: string | null;
|
|
agentId: string | null;
|
|
resolvedPath: string | null;
|
|
sessionKey: string | null;
|
|
};
|
|
createdAtMs: number;
|
|
expiresAtMs: number;
|
|
};
|
|
|
|
type ResolvedPayload = {
|
|
id: string;
|
|
decision: ExecApprovalDecision;
|
|
resolvedBy: string | null;
|
|
ts: number;
|
|
};
|
|
|
|
const asRecord = (value: unknown): Record<string, unknown> | null =>
|
|
value && typeof value === "object" && !Array.isArray(value)
|
|
? (value as Record<string, unknown>)
|
|
: null;
|
|
|
|
const asNonEmptyString = (value: unknown): string | null => {
|
|
if (typeof value !== "string") return null;
|
|
const trimmed = value.trim();
|
|
return trimmed.length > 0 ? trimmed : null;
|
|
};
|
|
|
|
const asOptionalString = (value: unknown): string | null =>
|
|
typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
|
|
const asPositiveTimestamp = (value: unknown): number | null =>
|
|
typeof value === "number" && Number.isFinite(value) && value > 0 ? value : null;
|
|
|
|
export const parseExecApprovalRequested = (event: EventFrame): RequestedPayload | null => {
|
|
if (event.type !== "event" || event.event !== "exec.approval.requested") return null;
|
|
const payload = asRecord(event.payload);
|
|
if (!payload) return null;
|
|
const id = asNonEmptyString(payload.id);
|
|
const request = asRecord(payload.request);
|
|
const createdAtMs = asPositiveTimestamp(payload.createdAtMs);
|
|
const expiresAtMs = asPositiveTimestamp(payload.expiresAtMs);
|
|
if (!id || !request || !createdAtMs || !expiresAtMs) return null;
|
|
const command = asNonEmptyString(request.command);
|
|
if (!command) return null;
|
|
return {
|
|
id,
|
|
request: {
|
|
command,
|
|
cwd: asOptionalString(request.cwd),
|
|
host: asOptionalString(request.host),
|
|
security: asOptionalString(request.security),
|
|
ask: asOptionalString(request.ask),
|
|
agentId: asOptionalString(request.agentId),
|
|
resolvedPath: asOptionalString(request.resolvedPath),
|
|
sessionKey: asOptionalString(request.sessionKey),
|
|
},
|
|
createdAtMs,
|
|
expiresAtMs,
|
|
};
|
|
};
|
|
|
|
export const parseExecApprovalResolved = (event: EventFrame): ResolvedPayload | null => {
|
|
if (event.type !== "event" || event.event !== "exec.approval.resolved") return null;
|
|
const payload = asRecord(event.payload);
|
|
if (!payload) return null;
|
|
const id = asNonEmptyString(payload.id);
|
|
const decisionRaw = asNonEmptyString(payload.decision);
|
|
const ts = asPositiveTimestamp(payload.ts);
|
|
if (!id || !decisionRaw || !ts) return null;
|
|
if (decisionRaw !== "allow-once" && decisionRaw !== "allow-always" && decisionRaw !== "deny") {
|
|
return null;
|
|
}
|
|
return {
|
|
id,
|
|
decision: decisionRaw,
|
|
resolvedBy: asOptionalString(payload.resolvedBy),
|
|
ts,
|
|
};
|
|
};
|
|
|
|
export const resolveExecApprovalAgentId = (params: {
|
|
requested: RequestedPayload;
|
|
agents: AgentState[];
|
|
}): string | null => {
|
|
const requestedAgentId = params.requested.request.agentId;
|
|
if (requestedAgentId) {
|
|
return requestedAgentId;
|
|
}
|
|
const requestedSessionKey = params.requested.request.sessionKey;
|
|
if (!requestedSessionKey) return null;
|
|
const matchedBySession = params.agents.find(
|
|
(agent) => agent.sessionKey.trim() === requestedSessionKey
|
|
);
|
|
return matchedBySession?.agentId ?? null;
|
|
};
|