evOo-local/packages/adapters/src/computer-support.ts
cynarAI 6c7eed7580 Initial clean evOo-local from f00577b
Fresh history cut from cynarAI/evoo antigravity-handoff/project-timeline
(Voice First + project timeline). Brand docs/titles as evOo; keep @rakazo/*
packages as tech debt. Excludes node_modules, caches, secrets, voice blobs.
2026-09-20 17:15:16 +02:00

142 lines
4.7 KiB
TypeScript

import { createHash } from "node:crypto";
import path from "node:path";
import type { ComputerAction, ComputerObservation, ComputerRef } from "@rakazo/adapter-kit";
import type { ComputerMode } from "@rakazo/contracts";
export function toComputerRef(computer: {
homeKey: string;
kind: string;
providerRef: string | null;
}): ComputerRef {
if (!computer.providerRef) throw new Error("computer provider reference is missing");
return {
id: computer.providerRef,
botId: computer.homeKey,
kind: computer.kind as ComputerRef["kind"],
providerRef: computer.providerRef,
};
}
const EMPTY_PNG = Uint8Array.from(
Buffer.from(
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=",
"base64",
),
);
export function computerObservation(
image: Uint8Array,
input: Omit<ComputerObservation, "frameId" | "capturedAt" | "image">,
): ComputerObservation {
return {
...input,
image,
frameId: createHash("sha256").update(image).digest("hex"),
capturedAt: new Date().toISOString(),
};
}
export function placeholderObservation(label = "ready"): ComputerObservation {
return computerObservation(EMPTY_PNG, {
mimeType: "image/png",
width: 1,
height: 1,
activeWindow: { id: "placeholder", title: label },
});
}
export function applyPlaceholderAction(box: { screen: string }, action: ComputerAction): void {
if (action.kind === "clipboard") box.screen = action.text;
else if (action.kind === "open") box.screen = `opened ${action.path}`;
else if (action.kind === "launch") box.screen = `launched ${action.application}`;
else box.screen = action.kind;
}
export function boundedComputerActions(actions: readonly ComputerAction[]): ComputerAction[] {
if (actions.length > 24) throw new Error("computer action batch exceeds 24 actions");
return [...actions];
}
export function clampRounded(value: number, min: number, max: number): number {
return Math.min(Math.max(Math.round(value), min), max);
}
export function normalizeWorkspacePath(value: string): string {
const normalized = value.replace(/\\/g, "/").replace(/^\/+/, "");
const rawSegments = normalized.split("/").filter(Boolean);
if (rawSegments.some((segment) => segment === "..")) {
throw new Error("Path escapes the computer workspace");
}
const segments = rawSegments.filter((segment) => segment !== ".");
return segments.join("/");
}
export function workspacePath(root: string, relative: string): string {
const normalized = normalizeWorkspacePath(relative);
return normalized ? path.posix.join(root, normalized) : root;
}
export function teamBotWorkspaceDirectory(botId: string): string {
return path.posix.join("bots", normalizeWorkspacePath(botId));
}
export function resolveBotWorkspacePath(
scope: ComputerMode,
botId: string,
requestedPath: string,
): string {
if (scope !== "team") return requestedPath;
const explicitRootPath = stripVirtualWorkspaceRoot(requestedPath);
const normalized = normalizeWorkspacePath(explicitRootPath ?? requestedPath);
if (explicitRootPath !== null || isTeamRootPath(normalized)) return normalized;
return path.posix.join(teamBotWorkspaceDirectory(botId), normalized);
}
export function resolveBotWorkspaceCwd(
scope: ComputerMode,
botId: string,
requestedCwd: string | undefined,
): string | undefined {
if (scope !== "team") return requestedCwd;
if (!requestedCwd || requestedCwd === ".") return teamBotWorkspaceDirectory(botId);
if (requestedCwd.startsWith("/")) return requestedCwd;
return resolveBotWorkspacePath(scope, botId, requestedCwd);
}
export function displayBotWorkspacePath(
scope: ComputerMode,
botId: string,
requestedPath: string,
storedPath: string,
): string {
if (scope !== "team" || !isBotRelativeRequest(requestedPath)) return storedPath;
const normalized = normalizeWorkspacePath(storedPath);
const botDirectory = teamBotWorkspaceDirectory(botId);
if (normalized === botDirectory) return "";
return normalized.startsWith(`${botDirectory}/`)
? normalized.slice(botDirectory.length + 1)
: normalized;
}
function isBotRelativeRequest(value: string): boolean {
if (stripVirtualWorkspaceRoot(value) !== null) return false;
return !isTeamRootPath(normalizeWorkspacePath(value));
}
function isTeamRootPath(value: string): boolean {
return (
value === "shared" ||
value.startsWith("shared/") ||
value === "bots" ||
value.startsWith("bots/")
);
}
function stripVirtualWorkspaceRoot(value: string): string | null {
const portable = value.replace(/\\/g, "/");
if (portable === "/") return "";
return portable.startsWith("/") ? portable.slice(1) : null;
}
export { shellQuote } from "@rakazo/core/node/desktop-runtime";