105 lines
3.5 KiB
JavaScript
105 lines
3.5 KiB
JavaScript
import { readFileSync } from "node:fs";
|
|
import { fileURLToPath } from "node:url";
|
|
import path from "node:path";
|
|
|
|
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
|
|
const portalSource = readFileSync(
|
|
path.resolve(scriptDir, "../src/pages/instances/InstancePortalPage.tsx"),
|
|
"utf8",
|
|
);
|
|
const desktopAccessHookSource = readFileSync(
|
|
path.resolve(scriptDir, "../src/hooks/useInstanceDesktopAccess.ts"),
|
|
"utf8",
|
|
);
|
|
|
|
function assert(condition, message) {
|
|
if (!condition) {
|
|
throw new Error(message);
|
|
}
|
|
}
|
|
|
|
assert(
|
|
portalSource.includes("WorkspaceFileManager"),
|
|
"Portal must reuse the instance detail workspace file manager.",
|
|
);
|
|
|
|
assert(
|
|
portalSource.includes('selectedInstance.instance_mode === "pro"'),
|
|
"Portal must branch rendering by instance_mode so Lite instances are not shown as Pro.",
|
|
);
|
|
|
|
assert(
|
|
portalSource.includes('initialPath="/config"'),
|
|
"Portal Pro workspace file manager must match detail page config-root behavior.",
|
|
);
|
|
|
|
assert(
|
|
portalSource.includes('data-portal-mode={isProPortal ? "pro" : "lite"}'),
|
|
"Portal must expose separate Pro and Lite layout markers.",
|
|
);
|
|
|
|
assert(
|
|
portalSource.includes("xl:grid-rows-[minmax(0,1fr)]") &&
|
|
portalSource.includes("xl:overflow-hidden") &&
|
|
!portalSource.includes("content-start overflow-y-auto") &&
|
|
!portalSource.includes("aspect-video"),
|
|
"Portal Pro desktop layout must stretch to fill the available height without leaving a lower blank area.",
|
|
);
|
|
|
|
assert(
|
|
portalSource.includes("canShowRuntimeOverlay") &&
|
|
portalSource.includes("isProPortal"),
|
|
"Portal runtime controls must be limited to Pro layout.",
|
|
);
|
|
|
|
assert(
|
|
!portalSource.includes("__openclaw__/canvas"),
|
|
"Portal Pro desktop instances must use the backend proxy entry instead of the OpenClaw gateway canvas route.",
|
|
);
|
|
|
|
assert(
|
|
portalSource.includes("portalEmbedUrl") &&
|
|
portalSource.includes("portalEmbedUrlForInstance") &&
|
|
portalSource.includes("preparedPortalFrame.embedUrl === portalEmbedUrl") &&
|
|
portalSource.includes("src={portalFrameSrc}"),
|
|
"Portal iframe must use a mode-aware embed URL instead of the raw access URL.",
|
|
);
|
|
|
|
assert(
|
|
portalSource.includes("instanceIdFromProxyUrl") &&
|
|
portalSource.includes("embedUrlInstanceId !== instance.id"),
|
|
"Portal iframe must not reuse an access URL from a previously selected instance.",
|
|
);
|
|
|
|
assert(
|
|
portalSource.includes("setShouldConnect(false)") &&
|
|
portalSource.includes("instance.id !== selectedId"),
|
|
"Portal must reset the connection intent before switching to another instance.",
|
|
);
|
|
|
|
assert(
|
|
portalSource.includes("pendingConnectInstanceIdRef") &&
|
|
portalSource.includes("pendingConnectInstanceIdRef.current === nextInstanceId") &&
|
|
portalSource.includes("shouldConnect && portalEmbedUrl"),
|
|
"Portal access generation must target the currently selected instance after switching.",
|
|
);
|
|
|
|
assert(
|
|
portalSource.includes('selectedInstanceStatus !== "running"') &&
|
|
portalSource.includes("requestAccess();"),
|
|
"Portal must auto-connect running instances when switching in the portal.",
|
|
);
|
|
|
|
assert(
|
|
/modeLabel\(\s*instance\.instance_mode\s*\)/.test(portalSource) &&
|
|
/modeClass\(\s*instance\.instance_mode\s*,?\s*\)/.test(portalSource),
|
|
"Portal left instance list must display the Lite/Pro instance type.",
|
|
);
|
|
|
|
assert(
|
|
desktopAccessHookSource.includes("refreshAccess({ forceReload: true })"),
|
|
"Desktop access hook must force a fresh access URL when connecting a selected instance.",
|
|
);
|
|
|
|
console.log("Instance portal mode layout contract is valid.");
|