claude-code-router/examples/plugins/claude-design-plugin.cjs

8263 lines
278 KiB
JavaScript

"use strict";
const crypto = require("node:crypto");
const fs = require("node:fs");
const http = require("node:http");
const https = require("node:https");
const os = require("node:os");
const pathModule = require("node:path");
const zlib = require("node:zlib");
const DEFAULT_HOST = "claude.ai";
const DEFAULT_GATEWAY_URL = "http://127.0.0.1:3456";
const DEFAULT_GATEWAY_MODEL = "claude-sonnet-4-20250514";
const DEFAULT_UPSTREAM_ORIGIN = "https://claude.ai";
const DEFAULT_DESIGN_ORIGIN = "https://claude.ai";
const DEFAULT_DESIGN_REFERRER = "https://claude.ai/design";
const DEFAULT_FALLBACK_ROUTE_HOSTS = ["claude.com", "www.anthropic.com", "anthropic.com"];
const CLAUDE_APP_ION_DIST_ENV_KEYS = ["CCR_CLAUDE_APP_ION_DIST_DIR", "CLAUDE_APP_ION_DIST_DIR"];
const CLAUDE_APP_PATH_ENV_KEYS = ["CCR_CLAUDE_APP_PATH", "CLAUDE_APP_PATH"];
const CLAUDE_APP_DESIGN_IFRAME_QUERY = "__ccr_design_iframe";
const CLAUDE_APP_DESIGN_PATH_QUERY = "path";
const CLAUDE_APP_DESIGN_SHELL_PATH = "/desktop-design";
const CLAUDE_APP_SPA_ROUTE_PATHS = [
CLAUDE_APP_DESIGN_SHELL_PATH,
"/discover/design",
"/admin-settings/claude-design"
];
const OMELETTE_RPC_PATH_PREFIX = "/design/anthropic.omelette.api.v1alpha.OmeletteService";
const AUTH_ESCAPE_ROUTE_PATHS = ["/login", "/auth", "/oauth"];
const BOOTSTRAP_ROUTE_PATHS = ["/_bootstrap", "/api/bootstrap", "/edge-api/bootstrap"];
const REQUIRED_ROUTE_PATHS = ["/", OMELETTE_RPC_PATH_PREFIX, "/v1/design", ...CLAUDE_APP_SPA_ROUTE_PATHS, ...BOOTSTRAP_ROUTE_PATHS, ...AUTH_ESCAPE_ROUTE_PATHS];
const DEFAULT_ROUTE_PATHS = ["/design", "/v1/design", "/api", "/organizations", "/cdn-cgi", ...BOOTSTRAP_ROUTE_PATHS, ...AUTH_ESCAPE_ROUTE_PATHS];
const FALLBACK_ROUTE_PATHS = ["/app-unavailable-in-region", "/cdn-cgi", "/design", ...AUTH_ESCAPE_ROUTE_PATHS];
const DEFAULT_DIRECT_GATEWAY_ROUTE_PATHS = ["/design", "/v1/design", "/api", "/organizations", "/assets", "/cdn-cgi", "/app-unavailable-in-region", ...BOOTSTRAP_ROUTE_PATHS, ...AUTH_ESCAPE_ROUTE_PATHS];
const DEFAULT_SCRIPT_PATH = "/design/assets/v1/index-C0BEUHEw.js";
const DEFAULT_STYLE_PATH = "/design/assets/v1/index-CqhNJH1o.css";
const DEFAULT_DESIGN_CRITICAL_MODULE_PRELOAD_PATHS = [
"/design/assets/v1/rolldown-runtime-CMxvf4Kt.js",
"/design/assets/v1/preload-helper-7XptfjGJ.js",
"/design/assets/v1/react-BthIFXYf.js",
"/design/assets/v1/client-lite-CK-dmuh6.js",
"/design/assets/v1/useOrg-DmMewMgN.js",
"/design/assets/v1/cn-BvRk9kiK.js",
"/design/assets/v1/cn-D-uX0T7P.js",
"/design/assets/v1/Button-XfKvB69T.js"
];
const DEFAULT_DESIGN_LAZY_MODULE_PRELOAD_PATHS = [
"/design/assets/v1/Button-BtUXY0oi.js",
"/design/assets/v1/DsBrowseModal-hSTModTp.js",
"/design/assets/v1/Form-B3y7m-2_.js",
"/design/assets/v1/FormList-Bv2urBSD.js",
"/design/assets/v1/Kbd-CLyZAmwA.js",
"/design/assets/v1/MetaText-DDKRoRv8.js",
"/design/assets/v1/ModalHeader-Jq2fIJBg.js",
"/design/assets/v1/ProjectsPage-PDmge7e_.js",
"/design/assets/v1/SegmentedControl-oWx7ZWcd.js",
"/design/assets/v1/SpinnerCursorContext-FnwGR7gg.js",
"/design/assets/v1/Switch-C1hJI3Wa.js",
"/design/assets/v1/TextInput-DBxrS72B.js",
"/design/assets/v1/TextLink-CSMbfMRn.js",
"/design/assets/v1/Tooltip-DkOyUUQf.js",
"/design/assets/v1/client-CoyioTSK.js",
"/design/assets/v1/client-event-bus-CUOhmLFi.js",
"/design/assets/v1/completion-BAv5dQBO.js",
"/design/assets/v1/components-CiWVw_5Z.js",
"/design/assets/v1/connectrpc-B0zPEr5l.js",
"/design/assets/v1/data-C1nvXn42.js",
"/design/assets/v1/ds-contract-C8bG_fzg.js",
"/design/assets/v1/ds-manifest-guards-CDOctgob.js",
"/design/assets/v1/home-analytics-EoW6gFrM.js",
"/design/assets/v1/host-BwlQdwMG.js",
"/design/assets/v1/platform-BJ0ekVkb.js",
"/design/assets/v1/registry-DDfaFazS.js",
"/design/assets/v1/useLabelableId-YQk8Dx5K.js",
"/design/assets/v1/useModelSelection-DahB-QBH.js",
"/design/assets/v1/useMutation-ndQNPSAH.js",
"/design/assets/v1/viewer-handle-BbaClWB_.js"
];
const DEFAULT_DESIGN_STYLE_PRELOAD_PATHS = [
"/design/assets/v1/Button-C3hHoRdu.css",
"/design/assets/v1/FormList-DPI5FmbR.css",
"/design/assets/v1/ProjectsPage-CRTuKvXp.css",
"/design/assets/v1/components-DnFFPXN_.css",
"/design/assets/v1/home-analytics-DAgH1hGY.css"
];
const LEGACY_SCRIPT_PATHS = new Set([
"/design/assets/index-DWa5J5J9.js",
"/design/assets/index-DYd5ifc6.js",
"/design/assets/index-BxFzSrWf.js"
]);
const LEGACY_STYLE_PATHS = new Set([
"/design/assets/index-DZOB93ZB.css",
"/design/assets/index-j8_-aIUE.css"
]);
const DEFAULT_EXTERNAL_ASSET_BASE_URLS = ["https://assets-proxy.anthropic.com/claude-ai/v2/assets/v1/"];
const DESIGN_INDEX_ASSET_DISCOVERY_TTL_MS = 5 * 60 * 1000;
const MAX_UPSTREAM_ASSET_REDIRECTS = 5;
const MAX_LOG_BODY_CHARS = 128 * 1024;
const COMMENT_COLLECTION = "comments";
const DESIGN_SYSTEM_COLLECTION = "design_systems";
const EVENT_COLLECTION = "events";
const PROJECT_COLLECTION = "projects";
const SESSION_COLLECTION = "sessions";
const THUMBNAIL_COLLECTION = "thumbnails";
const PROJECT_TYPE_PROJECT = 1;
const PROJECT_TYPE_TEMPLATE = 2;
const PROJECT_TYPE_DESIGN_SYSTEM = 3;
const TRANSPARENT_SVG = `<svg xmlns="http://www.w3.org/2000/svg" width="1" height="1" viewBox="0 0 1 1" aria-hidden="true"></svg>\n`;
const QUESTION_TOOL_NAMES = new Set(["questions", "questions_v2"]);
const CLAUDE_DESIGN_ROUTE_TYPES = new Set(["always", "image", "long-context", "model", "model-prefix", "thinking", "web-search"]);
const CLAUDE_DESIGN_MODEL_ALIASES = new Map([
["claude-haiku-4-5-20251001", DEFAULT_GATEWAY_MODEL],
["claude-opus-4-6", DEFAULT_GATEWAY_MODEL],
["claude-opus-4-7", DEFAULT_GATEWAY_MODEL],
["claude-opus-4-8", DEFAULT_GATEWAY_MODEL],
["claude-sonnet-4-6", DEFAULT_GATEWAY_MODEL]
]);
const FALLBACK_ASSET_CACHE_PREFIX = "fallback:claude-design-v1:";
const FALLBACK_ASSET_CACHE_TTL_MS = 60 * 60 * 1000;
const OMELETTE_PREVIEW_EVAL_BRIDGE_SCRIPT = `(function(){
if (window.__omEvalBridgeInstalled) return;
window.__omEvalBridgeInstalled = true;
window.addEventListener('message', function(event) {
var message = event && event.data;
if (!message || !message.__om_eval || typeof message.code !== 'string') return;
if (event.source !== window.parent) return;
var targetOrigin = event.origin && event.origin !== 'null' ? event.origin : '*';
var reply = function(payload) {
try {
event.source.postMessage(Object.assign({ __om_eval_r: true, id: message.id }, payload), targetOrigin);
} catch (_) {
window.parent.postMessage(Object.assign({ __om_eval_r: true, id: message.id }, payload), '*');
}
};
Promise.resolve()
.then(function() { return (0, eval)(message.code); })
.then(function(value) {
var serialized;
if (value !== undefined) {
try {
serialized = JSON.stringify(value);
} catch (_) {
serialized = JSON.stringify(String(value));
}
}
reply(serialized === undefined ? { ok: true } : { ok: true, v: serialized });
})
.catch(function(error) {
reply({ ok: false, e: error && error.message ? String(error.message) : String(error) });
});
});
})();`;
const DEFAULT_ME = {
accountUuid: "12345678",
organizationUuid: "87654321",
email: "aa@example.com",
displayName: "aa",
orgName: "aa's Organization",
growthbookPayload: "{}",
modelPresets: [
{
id: DEFAULT_GATEWAY_MODEL,
label: "Claude Sonnet 4",
maxTokens: 1000000,
supportsAdaptiveThinking: true,
description: "Most efficient for everyday tasks"
},
{
id: "claude-3-5-haiku-20241022",
label: "Claude Haiku 3.5",
maxTokens: 200000,
description: "Fast for quick answers"
},
{
id: "claude-3-5-sonnet-20241022",
label: "Claude Sonnet 3.5",
maxTokens: 200000,
supportsAdaptiveThinking: true,
overflow: true
},
{
id: "claude-3-opus-20240229",
label: "Claude Opus 3",
maxTokens: 200000,
supportsAdaptiveThinking: true,
overflow: true
}
],
defaultModelId: DEFAULT_GATEWAY_MODEL,
overrideStickyModel: true,
isPersonalOrg: true,
accessLevel: "ACCESS_LEVEL_FULL",
hasOauthTokens: true,
canManageDs: true,
memberships: [
{
uuid: "123456787",
name: "aa's Organization"
}
]
};
module.exports = {
async setup(ctx) {
const options = isRecord(ctx.pluginConfig) ? ctx.pluginConfig : {};
const routeHost = stringValue(options.host) || DEFAULT_HOST;
const configuredRoutePaths = stringArray(options.paths) || DEFAULT_ROUTE_PATHS;
const routePaths = Array.from(new Set([...REQUIRED_ROUTE_PATHS, ...configuredRoutePaths]));
const fallbackRouteHosts = normalizeFallbackRouteHosts(options.fallbackHosts, routeHost);
const upstreamOrigin = stringValue(options.upstreamOrigin) || DEFAULT_UPSTREAM_ORIGIN;
const assetProxy = options.assetProxy !== false;
const configuredAssetDir = stringValue(options.assetDir);
const claudeAppAssetDir = (configuredAssetDir || options.claudeAppAssets === false)
? ""
: resolveClaudeAppIonDistDir(options, ctx.logger);
const assetDir = configuredAssetDir || claudeAppAssetDir;
const usingClaudeAppAssets = Boolean(claudeAppAssetDir) ||
Boolean(configuredAssetDir && isClaudeAppIonDistDir(expandHomePath(configuredAssetDir)));
const assetSource = usingClaudeAppAssets ? "claude-app" : configuredAssetDir ? "configured" : "none";
const assetPassthrough = usingClaudeAppAssets
? false
: options.assetPassthrough === true ||
(options.assetPassthrough !== false && !localAssetDirExists(assetDir));
if (usingClaudeAppAssets && options.assetPassthrough === true) {
ctx.logger.info("Claude Design disabled assetPassthrough because Claude app ion-dist assets must be served locally.");
}
const configuredScriptPath = normalizePath(stringValue(options.scriptPath) || DEFAULT_SCRIPT_PATH);
const configuredStylePath = normalizePath(stringValue(options.stylePath) || DEFAULT_STYLE_PATH);
const scriptPath = shouldKeepCurrentScriptPath(configuredScriptPath) ? configuredScriptPath : DEFAULT_SCRIPT_PATH;
const stylePath = shouldKeepCurrentStylePath(configuredStylePath) ? configuredStylePath : DEFAULT_STYLE_PATH;
const assetAutoUpdate = options.assetAutoUpdate !== false;
const autoAnswerQuestions = options.autoAnswerQuestions !== false;
const gatewayUrl = stringValue(options.gatewayUrl) || DEFAULT_GATEWAY_URL;
const gatewayApiKey = stringValue(options.gatewayApiKey) || configuredGatewayApiKey(ctx.config);
const gatewayConfigPath = stringValue(options.gatewayConfigPath) ||
stringValue(ctx.config?.gateway?.generatedConfigFile) ||
defaultClaudeDesignGatewayConfigPath();
const gatewayConfig = loadClaudeDesignGatewayConfig(gatewayConfigPath, ctx.logger);
const modelSourceConfig = claudeDesignModelSourceConfig(ctx.config, gatewayConfig);
const configuredDefaultGatewayModel = normalizeRouteTarget(
stringValue(options.defaultGatewayModel) ||
stringValue(options.gatewayModel)
);
const defaultGatewayModel = configuredDefaultGatewayModel ||
normalizeRouteTarget(stringValue(ctx.config?.Router?.default)) ||
DEFAULT_GATEWAY_MODEL;
const frontendDefaultModel = normalizeRouteTarget(
stringValue(options.frontendDefaultModel) ||
stringValue(options.defaultModelId) ||
configuredDefaultGatewayModel
) || DEFAULT_GATEWAY_MODEL;
const availableProviderNames = claudeDesignProviderSelectorNames(modelSourceConfig);
const gatewayModelPresets = claudeDesignGatewayModelPresets(modelSourceConfig, frontendDefaultModel);
const routing = normalizeClaudeDesignRouting(options.routing, options);
const upstreamOrigins = normalizeUpstreamOrigins(options.upstreamOrigins, upstreamOrigin);
const me = normalizeMe(options.me, frontendDefaultModel, gatewayModelPresets);
const browserAppUrl = claudeDesignBrowserAppUrl(routeHost, usingClaudeAppAssets);
ctx.registerApp({
description: usingClaudeAppAssets
? "Open Claude Design UI through the Claude App shell and CCR gateway."
: "Open Claude Design through the CCR browser proxy.",
id: "claude-design",
name: "Claude Design",
url: browserAppUrl
});
const store = await ctx.openSqliteStore({
filename: "claude-design.sqlite",
migrate(database) {
database.run(`
CREATE TABLE IF NOT EXISTS claude_design_requests (
id INTEGER PRIMARY KEY AUTOINCREMENT,
created_at TEXT NOT NULL,
method TEXT NOT NULL,
path TEXT NOT NULL,
search TEXT NOT NULL DEFAULT '',
request_headers TEXT NOT NULL DEFAULT '{}',
request_body TEXT NOT NULL DEFAULT '',
response_status INTEGER NOT NULL,
response_body TEXT NOT NULL DEFAULT ''
);
CREATE INDEX IF NOT EXISTS claude_design_requests_created_at_idx
ON claude_design_requests(created_at);
CREATE INDEX IF NOT EXISTS claude_design_requests_path_idx
ON claude_design_requests(method, path);
CREATE TABLE IF NOT EXISTS claude_design_assets (
path TEXT PRIMARY KEY,
upstream_url TEXT NOT NULL,
content_type TEXT NOT NULL,
body_base64 TEXT NOT NULL,
fetched_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS claude_design_responses (
method TEXT NOT NULL,
path TEXT NOT NULL,
status INTEGER NOT NULL DEFAULT 200,
headers_json TEXT NOT NULL DEFAULT '{}',
body TEXT NOT NULL DEFAULT '{}',
updated_at TEXT NOT NULL,
PRIMARY KEY (method, path)
);
CREATE TABLE IF NOT EXISTS claude_design_items (
collection TEXT NOT NULL,
uuid TEXT NOT NULL,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
title TEXT NOT NULL,
model TEXT NOT NULL,
data_json TEXT NOT NULL DEFAULT '{}',
messages_json TEXT NOT NULL DEFAULT '[]',
PRIMARY KEY (collection, uuid)
);
CREATE INDEX IF NOT EXISTS claude_design_items_updated_at_idx
ON claude_design_items(collection, updated_at);
CREATE TABLE IF NOT EXISTS claude_design_files (
project_id TEXT NOT NULL,
path TEXT NOT NULL,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
content_type TEXT NOT NULL DEFAULT 'text/plain',
body_base64 TEXT NOT NULL DEFAULT '',
version INTEGER NOT NULL DEFAULT 1,
PRIMARY KEY (project_id, path)
);
CREATE INDEX IF NOT EXISTS claude_design_files_project_updated_at_idx
ON claude_design_files(project_id, updated_at);
`);
}
});
purgeBadCachedAssets(store);
const runtime = {
autoAnswerQuestions,
assetProxy,
assetAutoUpdate,
assetDir,
assetSource,
assetPassthrough,
designIndexAssets: {
checkedAt: 0,
html: "",
scriptPath,
source: "configured",
stylePath
},
standaloneDesignIndexAssets: {
checkedAt: 0,
html: "",
scriptPath: DEFAULT_SCRIPT_PATH,
source: "default",
stylePath: DEFAULT_STYLE_PATH
},
gatewayApiKey,
gatewayConfigPath,
defaultGatewayModel,
frontendDefaultModel,
gatewayModelPresets,
gatewayUrl,
logger: ctx.logger,
me,
routing,
availableProviderNames,
unavailableRouteTargetWarnings: new Set(),
routeHost,
fallbackRouteHosts,
routePaths,
scriptPath,
store,
stylePath,
upstreamOrigin,
upstreamOrigins
};
const backend = await ctx.registerHttpBackend({
id: "claude-design-mock",
async handler(request, response) {
await handleMockRequest(runtime, request, response);
}
});
if (assetPassthrough) {
ctx.registerProxyRoute({
host: routeHost,
id: "claude-design-assets-passthrough",
paths: ["/design/assets", "/assets"],
preserveHost: false,
upstream: upstreamOrigin
});
}
if (options.directGatewayRoutes !== false) {
for (const pathPrefix of normalizeDirectGatewayRoutePaths(options.directGatewayPaths)) {
ctx.registerGatewayRoute({
auth: "none",
handler(request, response) {
return handleMockRequest(runtime, request, response);
},
id: `claude-design-ui-${sanitizeRouteId(pathPrefix)}`,
methods: ["DELETE", "GET", "HEAD", "OPTIONS", "PATCH", "POST", "PUT"],
pathPrefix
});
}
}
ctx.registerProxyRoute({
host: routeHost,
id: "claude-design-proxy",
paths: routePaths,
preserveHost: true,
upstream: backend.url
});
for (const fallbackHost of fallbackRouteHosts) {
ctx.registerProxyRoute({
host: fallbackHost,
id: `claude-design-fallback-${fallbackHost}`,
paths: FALLBACK_ROUTE_PATHS,
preserveHost: true,
upstream: backend.url
});
}
ctx.registerGatewayRoute({
auth: "none",
handler(request, response, helpers) {
return handleAdminRequest(runtime, backend, request, response, helpers);
},
id: "claude-design-admin",
methods: ["DELETE", "GET", "POST", "PUT"],
pathPrefix: "/plugins/claude-design"
});
ctx.logger.info(`Claude Design mock listening at ${backend.url} for ${routeHost} ${routePaths.join(", ")}`);
}
};
async function handleMockRequest(runtime, request, response) {
const url = new URL(request.url || "/", "http://claude-design.local");
const method = (request.method || "GET").toUpperCase();
const requestBody = await readRequestBody(request);
let result;
try {
if (method === "OPTIONS") {
result = {
body: "",
headers: corsHeaders({ "access-control-max-age": "86400" }),
status: 204
};
} else if (method === "HEAD") {
const getResult = await routeMockRequest(runtime, "GET", url, request, requestBody);
result = { ...getResult, body: "" };
} else {
result = await routeMockRequest(runtime, method, url, request, requestBody);
}
} catch (error) {
result = jsonResponse(500, {
error: {
message: error instanceof Error ? error.message : String(error)
}
});
}
sendResponse(response, result);
logRequest(runtime.store, {
method,
path: url.pathname,
requestBody,
requestHeaders: request.headers,
responseBody: result.body,
responseStatus: result.status,
search: url.search
});
}
async function routeMockRequest(runtime, method, url, request, requestBody) {
const path = normalizePath(url.pathname);
if (method === "GET" && path === "/health") {
return jsonResponse(200, { ok: true, plugin: "claude-design" });
}
if (isDesignAuthEscapeRoute(path)) {
if (method === "GET" || method === "HEAD") {
return redirectResponse(302, "/design");
}
return jsonResponse(200, authSessionPayload(runtime.me));
}
if (method === "GET" && runtime.assetSource === "claude-app" && isDesignSpaRoute(path)) {
if (isClaudeAppDesignIframeRequest(url, request)) {
const assets = await resolveStandaloneDesignIndexAssets(runtime, request);
return htmlResponse(renderDefaultDesignIndex(runtime.me, assets.scriptPath, assets.stylePath));
}
const redirectUrl = claudeAppDesignEntrypointRedirectUrl(url);
if (redirectUrl) {
return redirectResponse(302, redirectUrl);
}
}
if (method === "GET" && runtime.assetSource === "claude-app" && isClaudeAppSpaRoute(path)) {
const assets = await resolveDesignIndexAssets(runtime, request);
return htmlResponse(renderDesignIndex(runtime.me, assets.scriptPath, assets.stylePath, assets.html));
}
if (method === "GET" && (path === "/" || isDesignSpaRoute(path))) {
const assets = await resolveDesignIndexAssets(runtime, request);
return htmlResponse(renderDesignIndex(runtime.me, assets.scriptPath, assets.stylePath, assets.html));
}
if (method === "GET" && path === "/app-unavailable-in-region") {
const assets = await resolveDesignIndexAssets(runtime, request);
return htmlResponse(renderDesignIndex(runtime.me, assets.scriptPath, assets.stylePath, assets.html));
}
if (method === "GET" && path === "/design/favicon.png") {
return binaryResponse(200, Buffer.from(FAVICON_PNG_BASE64, "base64"), {
"cache-control": "public, max-age=86400",
"content-type": "image/png"
});
}
if (method === "GET" && path === "/design/introvid.html") {
return htmlResponse("<!doctype html><html lang=\"en\"><head><meta charset=\"UTF-8\"></head><body></body></html>\n");
}
if (method === "GET" && path.startsWith("/assets/")) {
return serveAsset(runtime, `/design${path}`, request);
}
if (method === "GET" && path.startsWith("/design/assets/")) {
return serveAsset(runtime, path, request);
}
if (method === "GET" && isClaudeAppStaticRoutePath(path)) {
return serveAsset(runtime, path, request);
}
if (method === "GET" && isDesignStaticAsset(path)) {
return serveAsset(runtime, path, request);
}
if (method === "GET" && path.startsWith("/cdn-cgi/")) {
return textResponse(200, "/* Cloudflare challenge disabled by Claude Design mock. */\n", {
"cache-control": "no-store",
"content-type": "application/javascript; charset=utf-8"
});
}
const storedResponse = readStoredResponse(runtime.store, method, path) || readStoredResponse(runtime.store, "ANY", path);
if (storedResponse) {
return storedResponse;
}
if (path.startsWith("/design/anthropic.omelette.api.v1alpha.OmeletteService/")) {
return handleOmeletteConnectRpc(runtime, method, path, request, requestBody);
}
if (path.startsWith("/design/v1/design/") || path.startsWith("/v1/design/")) {
return await handleDesignRestApi(runtime, method, path, request, requestBody);
}
if (isBootstrapRoutePath(path)) {
return jsonResponse(200, bootstrapPayload(runtime.me));
}
const bootstrapSubresource = handleBootstrapSubresource(runtime, method, path);
if (bootstrapSubresource) {
return bootstrapSubresource;
}
if (path === "/api/auth/session" || path === "/api/session") {
return jsonResponse(200, authSessionPayload(runtime.me));
}
if (path === "/api/me" || path === "/api/user" || path === "/api/account") {
return jsonResponse(200, accountPayload(runtime.me));
}
if (path === "/api/organizations") {
return jsonResponse(200, organizationsPayload(runtime.me));
}
if (path.startsWith("/api/organizations/")) {
return handleOrganizationApi(runtime, method, path, request, requestBody);
}
if (path.startsWith("/api/")) {
return handleGenericApi(method, path, requestBody);
}
return jsonResponse(404, {
error: {
message: `Claude Design mock has no route for ${method} ${path}`
}
});
}
async function handleOmeletteConnectRpc(runtime, method, path, request, requestBody) {
if (method !== "POST") {
return jsonResponse(405, {
error: {
message: `Claude Design mock only supports POST for ${path}`
}
});
}
const rpcName = path.split("/").pop();
const isConnectProtoRequest = headerIncludes(request.headers["content-type"], "application/connect+proto");
const rpcBody = rpcName === "Chat" || isConnectProtoRequest
? decodeConnectEnvelope(requestBody)
: requestBody;
switch (rpcName) {
case "CreateProject": {
const project = createOmeletteProject(runtime, rpcBody);
return protoResponse(encodeCreateProjectResponse(project.uuid));
}
case "UpdateProject":
updateOmeletteProject(runtime, rpcBody);
return protoResponse(Buffer.alloc(0));
case "DeleteProject":
deleteOmeletteProject(runtime, rpcBody);
return protoResponse(Buffer.alloc(0));
case "BundleProject":
return protoResponse(bundleProject(runtime, rpcBody));
case "GetProject": {
const projectId = decodeProtoStringField(rpcBody, 1);
const project = getOmeletteProject(runtime, projectId);
return protoResponse(encodeGetProjectResponse(project, runtime.me, getProjectDataBytes(runtime, project.projectId)));
}
case "GetProjectData": {
const projectId = decodeProtoStringField(rpcBody, 1);
return protoResponse(encodeGetProjectDataResponse(getProjectDataBytes(runtime, projectId)));
}
case "UpdateProjectData":
return protoResponse(updateProjectDataBytes(runtime, rpcBody));
case "GetChatMessages":
return protoResponse(encodeGetChatMessagesResponse(runtime, rpcBody));
case "ListChatsForExport":
return protoResponse(listChatsForExport(runtime, rpcBody));
case "ExportChatMessages":
return protoResponse(exportChatMessages(runtime, rpcBody));
case "CreateClaudeCodeSession":
return protoResponse(createClaudeCodeSession(runtime, rpcBody));
case "UpdateSharing":
return protoResponse(updateOmeletteProjectSharing(runtime, rpcBody));
case "DuplicateProject":
return protoResponse(duplicateOmeletteProject(runtime, rpcBody, {}));
case "RemixProject":
return protoResponse(duplicateOmeletteProject(runtime, rpcBody, { includeChats: decodeProtoBoolField(rpcBody, 2) }));
case "CreateTemplateFromProject":
return protoResponse(duplicateOmeletteProject(runtime, rpcBody, { type: PROJECT_TYPE_TEMPLATE }));
case "UpdateProjectType":
return protoResponse(updateProjectType(runtime, rpcBody));
case "SetProjectPublished":
return protoResponse(setProjectPublished(runtime, rpcBody));
case "UpdateProjectInfo":
updateProjectInfo(runtime, rpcBody);
return protoResponse(Buffer.alloc(0));
case "UpdateProjectDesignSystems":
return protoResponse(updateProjectDesignSystems(runtime, rpcBody));
case "PatchDesignSystemBinding":
return protoResponse(patchDesignSystemBinding(runtime, rpcBody));
case "RefreshBoundDesignSystem":
return protoResponse(protoInt32(1, 0));
case "SetProjectFavorite":
updateProjectFavorite(runtime, rpcBody);
return protoResponse(Buffer.alloc(0));
case "SetProjectThumbnail":
setProjectThumbnail(runtime, rpcBody);
return protoResponse(Buffer.alloc(0));
case "ListFiles":
return protoResponse(encodeListFilesResponse(runtime, rpcBody));
case "GetFile":
return protoResponse(encodeGetFileResponse(runtime, rpcBody));
case "WriteFiles":
return protoResponse(writeProjectFiles(runtime, rpcBody));
case "DeleteFile":
return protoResponse(deleteProjectFile(runtime, rpcBody));
case "DeleteFiles":
return protoResponse(deleteProjectFiles(runtime, rpcBody));
case "CopyFile":
return protoResponse(copyProjectFile(runtime, rpcBody));
case "EditFile":
return protoResponse(editProjectFile(runtime, rpcBody));
case "GrepFiles":
return protoResponse(grepProjectFiles(runtime, rpcBody));
case "CreateFileStream":
return protoResponse(createFileStream(runtime, rpcBody));
case "WriteFileStream":
return protoResponse(writeFileStream(runtime, rpcBody));
case "AbortFileStream":
return protoResponse(Buffer.alloc(0));
case "UploadFile": {
const responseBody = uploadFile(runtime, rpcBody);
return isConnectProtoRequest ? connectStreamResponse([responseBody]) : protoResponse(responseBody);
}
case "ListProjectAssets":
return protoResponse(listProjectAssets(runtime, rpcBody));
case "RecordAsset":
recordProjectAsset(runtime, rpcBody);
return protoResponse(Buffer.alloc(0));
case "SetAssetStatus":
setProjectAssetStatus(runtime, rpcBody);
return protoResponse(Buffer.alloc(0));
case "DeleteAsset":
deleteProjectAsset(runtime, rpcBody);
return protoResponse(Buffer.alloc(0));
case "GetMe":
return protoResponse(encodeGetMeResponse(runtime.me));
case "GetUsageStatus":
return protoResponse(Buffer.alloc(0));
case "UpdateOrgSettings":
updateOrgSettings(runtime, rpcBody);
return protoResponse(Buffer.alloc(0));
case "GetOrgSettings":
return protoResponse(encodeGetOrgSettingsResponse(runtime));
case "ListOrgProjects":
return protoResponse(encodeListProjectsResponse(runtime, decodeProtoEnumField(rpcBody, 1), decodeProtoBoolField(rpcBody, 2)));
case "ListProjects":
return protoResponse(encodeListProjectsResponse(runtime));
case "MintPreviewToken":
return protoResponse(encodeMintPreviewTokenResponse(runtime));
case "MintHandoffToken":
case "MintDesignSyncCode":
return protoResponse(encodeTokenResponse());
case "CountTokens":
return protoResponse(await countGatewayTokens(runtime, rpcBody));
case "Chat":
return await chatWithGateway(runtime, rpcBody);
case "TrackEvent":
recordTrackEvent(runtime, rpcBody);
return protoResponse(Buffer.alloc(0));
case "CreateComment":
return protoResponse(createComment(runtime, rpcBody));
case "UpdateComment":
return protoResponse(updateComment(runtime, rpcBody));
case "DeleteComment":
deleteComment(runtime, rpcBody);
return protoResponse(Buffer.alloc(0));
case "ListComments":
return protoResponse(listComments(runtime, rpcBody));
case "CreateCommentReply":
return protoResponse(createCommentReply(runtime, rpcBody));
case "UpdateCommentReply":
return protoResponse(updateCommentReply(runtime, rpcBody));
case "DeleteCommentReply":
deleteCommentReply(runtime, rpcBody);
return protoResponse(Buffer.alloc(0));
case "SendCommentsToChat":
return protoResponse(sendCommentsToChat(runtime, rpcBody));
case "SendMultiplayerMessage":
return protoResponse(sendMultiplayerMessage(runtime, rpcBody));
case "DeleteAccount":
case "DeleteOrganization":
return protoResponse(Buffer.alloc(0));
case "FigmaCallTool":
case "McpCallTool":
return protoResponse(encodeToolCallResponse(rpcName, rpcBody));
case "FigmaDisconnect":
case "FigmaExchangeCode":
case "FigmaStartAuth":
case "GithubDisconnect":
case "GithubExchangeCode":
case "GithubStartAuth":
return protoResponse(encodeIntegrationAuthResponse(rpcName));
case "FigmaListTools":
case "GithubListRepos":
case "GithubGetTree":
case "GithubReadFile":
case "GithubImportRepo":
return protoResponse(encodeIntegrationListResponse(rpcName, rpcBody));
case "RenewTurn":
case "ReleaseTurn":
return protoResponse(Buffer.alloc(0));
case "MarkCommentsRead":
return protoResponse(markCommentsRead(runtime, rpcBody));
case "ListExperiences":
case "TrackExperience":
case "ExecuteExperienceAction":
case "LintFiles":
case "FigmaGetStatus":
case "GithubGetStatus":
case "McpListConnected":
case "McpListConnectors":
case "McpListDesignImportPartners":
case "McpListTools":
return protoResponse(Buffer.alloc(0));
default:
return protoResponse(Buffer.alloc(0));
}
}
async function serveAsset(runtime, path, request) {
const cached = readCachedAsset(runtime.store, path);
if (cached) {
const cachedBody = Buffer.from(cached.bodyBase64, "base64");
const reusableFallback = isReusableFallbackAsset(cached, path);
if ((!isFallbackAsset(cached) || reusableFallback) && isUsableServedAssetBody(path, cached.contentType, cachedBody)) {
if (!reusableFallback) {
recordDesignIndexAssetHint(runtime, path, "cache");
}
return binaryResponse(200, cachedBody, {
"cache-control": reusableFallback ? "no-store" : "public, max-age=86400",
"content-type": cached.contentType,
"x-claude-design-asset-source": cached.upstreamUrl || "cache"
});
}
deleteCachedAsset(runtime.store, path);
}
const localAsset = readLocalAsset(runtime.assetDir, path);
if (localAsset && isUsableServedAssetBody(path, localAsset.contentType, localAsset.body)) {
writeCachedAsset(runtime.store, path, localAsset.source, localAsset.contentType, localAsset.body);
recordDesignIndexAssetHint(runtime, path, "local");
return binaryResponse(200, localAsset.body, {
"cache-control": "public, max-age=86400",
"content-type": localAsset.contentType,
"x-claude-design-asset-source": "local"
});
}
if (runtime.assetProxy) {
for (const origin of runtime.upstreamOrigins) {
for (const upstreamUrl of upstreamAssetUrlCandidates(path, origin)) {
const fetched = await fetchUpstreamAsset(upstreamUrl, request).catch((error) => {
runtime.logger?.warn?.(
`Claude Design failed to fetch upstream asset ${upstreamUrl.toString()}: ${
error instanceof Error ? error.message : String(error)
}`
);
return undefined;
});
if (fetched && fetched.status >= 200 && fetched.status < 300 && isUsableServedAssetBody(path, fetched.contentType, fetched.body)) {
writeCachedAsset(runtime.store, path, fetched.url || upstreamUrl.toString(), fetched.contentType, fetched.body);
recordDesignIndexAssetHint(runtime, path, "remote");
return binaryResponse(200, fetched.body, {
"cache-control": "public, max-age=86400",
"content-type": fetched.contentType,
"x-claude-design-asset-source": fetched.url || upstreamUrl.toString()
});
}
if (fetched) {
logRejectedUpstreamAsset(runtime, path, upstreamUrl, fetched);
}
}
}
}
const refreshedIndexAsset = await serveRefreshedDesignIndexAsset(runtime, path, request);
if (refreshedIndexAsset) {
return refreshedIndexAsset;
}
if (path === "/design/pictogram-bookapple.svg") {
return textResponse(200, TRANSPARENT_SVG, {
"cache-control": "public, max-age=86400",
"content-type": "image/svg+xml"
});
}
const fallback = fallbackDesignAsset(path);
if (fallback) {
if (isCacheableFallbackAsset(path)) {
writeCachedAsset(runtime.store, path, fallbackAssetCacheSource(path), fallback.contentType, fallback.body);
}
return binaryResponse(200, fallback.body, {
"cache-control": "no-store",
"content-type": fallback.contentType,
"x-claude-design-asset-source": "fallback"
});
}
return jsonResponse(502, {
error: {
message: `Claude Design asset is not cached and upstream fetch failed: ${path}`,
hint: "Provide the real Claude Design asset through config.assetDir or allow upstream asset proxying."
}
});
}
function logRejectedUpstreamAsset(runtime, path, upstreamUrl, fetched) {
const message = `Claude Design rejected upstream asset ${upstreamUrl.toString()}: status=${fetched.status} content-type=${
fetched.contentType || "unknown"
} bytes=${fetched.body.length} final-url=${fetched.url || upstreamUrl.toString()}`;
if (isCacheableFallbackAsset(path)) {
runtime.logger?.debug?.(`${message}; using local fallback`);
return;
}
runtime.logger?.warn?.(message);
}
async function serveRefreshedDesignIndexAsset(runtime, path, request) {
const requestPath = normalizePath(path);
const isScript = isDesignIndexScriptPath(requestPath);
const isStyle = isDesignIndexStylePath(requestPath);
if (!runtime.assetAutoUpdate || (!isScript && !isStyle)) {
return undefined;
}
const refreshed = await resolveDesignIndexAssets(runtime, request, { force: true });
const replacementPath = isScript ? refreshed.scriptPath : refreshed.stylePath;
if (!replacementPath || replacementPath === requestPath) {
return undefined;
}
const replacement = readResolvedDesignIndexAsset(runtime, replacementPath);
if (!replacement || !isUsableDesignIndexAssetResponse(replacementPath, replacement.contentType, replacement.body)) {
return undefined;
}
return binaryResponse(200, replacement.body, {
"cache-control": "no-store",
"content-type": replacement.contentType,
"x-claude-design-asset-source": `${replacement.source}; replacement=${replacementPath}`
});
}
function readResolvedDesignIndexAsset(runtime, path) {
const cached = runtime.store ? readCachedAsset(runtime.store, path) : undefined;
if (cached) {
return {
body: Buffer.from(cached.bodyBase64 || "", "base64"),
contentType: cached.contentType,
source: cached.upstreamUrl || "cache"
};
}
const local = readLocalAsset(runtime.assetDir, path);
return local
? {
body: local.body,
contentType: local.contentType,
source: local.source
}
: undefined;
}
function fallbackDesignAsset(path) {
if (!path.startsWith("/design/")) {
return undefined;
}
if (!path.startsWith("/design/assets/") && !/\.(?:gif|png|webp|jpe?g|svg|ico)$/i.test(path)) {
return undefined;
}
if (/\/QuestionsViewer-[^/]+\.js$/i.test(path)) {
return {
body: Buffer.from(questionsViewerFallbackModule(), "utf8"),
contentType: "application/javascript; charset=utf-8"
};
}
if (isDesignIndexScriptPath(path)) {
return undefined;
}
if (path.endsWith(".js")) {
return {
body: Buffer.from(
[
"export function HandoffModal(){ return null; }",
"export default function ClaudeDesignMissingChunk(){ return null; }"
].join("\n"),
"utf8"
),
contentType: "application/javascript; charset=utf-8"
};
}
if (path.endsWith(".css")) {
return {
body: Buffer.alloc(0),
contentType: "text/css; charset=utf-8"
};
}
if (/\.(?:gif|png|webp|jpe?g|svg|ico)$/i.test(path)) {
return {
body: Buffer.from(TRANSPARENT_SVG, "utf8"),
contentType: "image/svg+xml"
};
}
return undefined;
}
function isCacheableFallbackAsset(path) {
return path.startsWith("/design/assets/") &&
!isDesignIndexScriptPath(path) &&
!isDesignIndexStylePath(path) &&
/\.(?:js|mjs|gif|png|webp|jpe?g|svg|ico)$/i.test(path);
}
function fallbackAssetCacheSource(path) {
return `${FALLBACK_ASSET_CACHE_PREFIX}${path}`;
}
function isUsableAssetBody(path, contentType, body) {
const payload = Buffer.isBuffer(body) ? body : Buffer.from(body || []);
const normalizedContentType = stringValue(contentType)?.toLowerCase() || "";
if (payload.length === 0 && /\.(?:js|mjs|css|json|wasm|png|jpe?g|gif|webp|svg|ico)$/i.test(path)) {
return false;
}
if (normalizedContentType.includes("text/html") && !/\.html?$/i.test(path)) {
return false;
}
if (/\.(?:js|mjs)$/i.test(path)) {
return !normalizedContentType.includes("text/html");
}
if (/\.css$/i.test(path)) {
return !normalizedContentType.includes("text/html");
}
return true;
}
function isUsableServedAssetBody(path, contentType, body) {
if (!isUsableAssetBody(path, contentType, body)) {
return false;
}
if (isDesignIndexScriptPath(path)) {
return isUsableDesignIndexScriptBody(path, contentType, body);
}
return true;
}
function normalizeUpstreamOrigins(value, primaryOrigin) {
const origins = [];
const addOrigin = (origin) => {
const raw = stringValue(origin);
if (!raw) {
return;
}
try {
const parsed = new URL(raw);
const normalized = `${parsed.protocol}//${parsed.host}`;
if (!origins.includes(normalized)) {
origins.push(normalized);
}
} catch {
// Ignore malformed source origins; they cannot be used for asset proxying.
}
};
addOrigin(primaryOrigin);
if (Array.isArray(value)) {
value.forEach(addOrigin);
} else {
addOrigin(value);
}
return origins.length > 0 ? origins : [DEFAULT_UPSTREAM_ORIGIN];
}
function upstreamAssetUrlCandidates(path, origin) {
const requestPath = normalizePath(path);
const candidates = [new URL(requestPath, origin)];
if (requestPath.startsWith("/design/assets/")) {
candidates.push(new URL(requestPath.replace(/^\/design\/assets\//, "/assets/"), origin));
const assetName = requestPath.split("/").pop();
if (assetName) {
for (const baseUrl of DEFAULT_EXTERNAL_ASSET_BASE_URLS) {
candidates.push(new URL(assetName, baseUrl));
}
}
}
const seen = new Set();
return candidates.filter((url) => {
const key = url.toString();
if (seen.has(key)) {
return false;
}
seen.add(key);
return true;
});
}
function normalizeFallbackRouteHosts(value, primaryHost) {
const hosts = [];
const addHost = (host) => {
const raw = stringValue(host);
if (!raw) {
return;
}
const normalized = raw.replace(/^https?:\/\//i, "").replace(/\/.*$/, "").toLowerCase();
if (normalized && normalized !== primaryHost.toLowerCase() && !hosts.includes(normalized)) {
hosts.push(normalized);
}
};
DEFAULT_FALLBACK_ROUTE_HOSTS.forEach(addHost);
if (Array.isArray(value)) {
value.forEach(addHost);
} else {
addHost(value);
}
return hosts;
}
function claudeDesignBrowserAppUrl(routeHost, usingClaudeAppAssets) {
if (!usingClaudeAppAssets) {
return `https://${routeHost}/design`;
}
const targetPath = withClaudeAppDesignIframeMarker("/design");
return `https://${routeHost}${CLAUDE_APP_DESIGN_SHELL_PATH}?${CLAUDE_APP_DESIGN_PATH_QUERY}=${encodeURIComponent(targetPath)}`;
}
function normalizeDirectGatewayRoutePaths(value) {
const configured = stringArray(value);
const paths = configured?.length ? configured : DEFAULT_DIRECT_GATEWAY_ROUTE_PATHS;
return Array.from(new Set(paths.map(normalizePath).filter(Boolean).filter((path) => path !== "/")));
}
function sanitizeRouteId(value) {
return String(value || "")
.replace(/[^a-zA-Z0-9]+/g, "-")
.replace(/^-+|-+$/g, "")
|| "root";
}
async function resolveDesignIndexAssets(runtime, request, options = {}) {
const current = runtime.designIndexAssets || {
checkedAt: 0,
html: "",
scriptPath: runtime.scriptPath || DEFAULT_SCRIPT_PATH,
source: "default",
stylePath: runtime.stylePath || DEFAULT_STYLE_PATH
};
if (!runtime.assetAutoUpdate) {
return current;
}
const now = Date.now();
const forceRefresh = options.force === true || requestHasNoCache(request);
const hasRenderableIndex = isUsableDesignShellHtml(current.html) || Boolean(current.scriptPath || current.stylePath);
if (!forceRefresh && hasRenderableIndex && current.checkedAt && now - current.checkedAt < DESIGN_INDEX_ASSET_DISCOVERY_TTL_MS) {
return current;
}
const fromRequests = discoverRequestedDesignIndexAssets(runtime.store);
const fromLocal = discoverLocalDesignIndexAssets(runtime.assetDir);
const fromCache = discoverCachedDesignIndexAssets(runtime.store);
const shouldDiscoverRemote = runtime.assetSource !== "claude-app";
const fromRemote = shouldDiscoverRemote ? await discoverRemoteDesignIndexAssets(runtime, request) : undefined;
const fromRemoteCache = shouldDiscoverRemote ? await cacheRemoteDesignIndexAssets(runtime, fromRemote, request) : undefined;
const fallback = mergeDesignIndexAssetPartials(fromLocal, fromCache) || {};
const seeded = {
html: isUsableDesignShellHtml(fromLocal?.html) ? fromLocal.html : current.html,
scriptPath: fallback.scriptPath || current.scriptPath,
source: fallback.source || current.source || "current",
stylePath: fallback.stylePath || current.stylePath
};
const discovered = mergeDesignIndexAssets(seeded, fromRequests, fromRemoteCache || fromRemote, fromLocal);
const safeDiscovered = selectUsableDesignIndexAssets(runtime, discovered, fallback, current);
return updateDesignIndexAssets(runtime, safeDiscovered, safeDiscovered.source || "discovered", { checkedAt: now });
}
async function resolveStandaloneDesignIndexAssets(runtime, request) {
if (runtime.assetSource !== "claude-app") {
return resolveDesignIndexAssets(runtime, request);
}
const current = runtime.standaloneDesignIndexAssets || {
checkedAt: 0,
html: "",
scriptPath: DEFAULT_SCRIPT_PATH,
source: "default",
stylePath: DEFAULT_STYLE_PATH
};
if (!runtime.assetAutoUpdate) {
return current;
}
const now = Date.now();
const forceRefresh = requestHasNoCache(request);
if (!forceRefresh && current.checkedAt && now - current.checkedAt < DESIGN_INDEX_ASSET_DISCOVERY_TTL_MS) {
return current;
}
const fromCache = discoverCachedStandaloneDesignIndexAssets(runtime.store);
const fromRemote = await discoverRemoteDesignIndexAssets(runtime, request);
const fromRemoteCache = await cacheRemoteDesignIndexAssets(runtime, fromRemote, request);
const discovered = mergeDesignIndexAssets(current, fromCache, fromRemoteCache || fromRemote);
const assets = {
checkedAt: now,
html: "",
scriptPath: shouldKeepCurrentScriptPath(discovered.scriptPath) ? discovered.scriptPath : DEFAULT_SCRIPT_PATH,
source: discovered.source || current.source || "standalone",
stylePath: shouldKeepCurrentStylePath(discovered.stylePath) ? discovered.stylePath : DEFAULT_STYLE_PATH,
upstreamUrls: {
...(current.upstreamUrls || {}),
...(discovered.upstreamUrls || {})
}
};
runtime.standaloneDesignIndexAssets = assets;
return assets;
}
function requestHasNoCache(request) {
return headerIncludes(request?.headers?.["cache-control"], "no-cache") ||
headerIncludes(request?.headers?.pragma, "no-cache");
}
function updateDesignIndexAssets(runtime, assets, source, options = {}) {
const current = runtime.designIndexAssets || {
checkedAt: 0,
html: "",
scriptPath: runtime.scriptPath || DEFAULT_SCRIPT_PATH,
source: "default",
stylePath: runtime.stylePath || DEFAULT_STYLE_PATH
};
const candidateScriptPath = normalizePath(stringValue(assets?.scriptPath) || current.scriptPath || DEFAULT_SCRIPT_PATH);
const candidateStylePath = normalizePath(stringValue(assets?.stylePath) || current.stylePath || DEFAULT_STYLE_PATH);
const scriptPath = shouldKeepCurrentScriptPath(candidateScriptPath) ? candidateScriptPath : DEFAULT_SCRIPT_PATH;
const stylePath = shouldKeepCurrentStylePath(candidateStylePath) ? candidateStylePath : DEFAULT_STYLE_PATH;
const html = isUsableDesignShellHtml(assets?.html)
? String(assets.html)
: isUsableDesignShellHtml(current.html)
? String(current.html)
: "";
runtime.scriptPath = scriptPath;
runtime.stylePath = stylePath;
runtime.designIndexAssets = {
checkedAt: options.checkedAt ?? Date.now(),
html,
scriptPath,
source,
stylePath,
upstreamUrls: {
...(current.upstreamUrls || {}),
...(assets?.upstreamUrls || {})
}
};
return runtime.designIndexAssets;
}
function mergeDesignIndexAssets(current, ...candidates) {
const merged = {
html: isUsableDesignShellHtml(current.html) ? String(current.html) : "",
scriptPath: shouldKeepCurrentScriptPath(current.scriptPath) ? current.scriptPath : DEFAULT_SCRIPT_PATH,
source: current.source || "current",
stylePath: shouldKeepCurrentStylePath(current.stylePath) ? current.stylePath : DEFAULT_STYLE_PATH,
upstreamUrls: { ...(current.upstreamUrls || {}) }
};
for (const candidate of candidates) {
if (!candidate) {
continue;
}
if (candidate.upstreamUrls) {
merged.upstreamUrls = {
...merged.upstreamUrls,
...candidate.upstreamUrls
};
}
if (isUsableDesignShellHtml(candidate.html)) {
merged.html = String(candidate.html);
merged.source = candidate.source || merged.source;
}
if (candidate.scriptPath) {
merged.scriptPath = candidate.scriptPath;
merged.source = candidate.source || merged.source;
}
if (candidate.stylePath) {
merged.stylePath = candidate.stylePath;
merged.source = candidate.source || merged.source;
}
}
return merged;
}
function mergeDesignIndexAssetPartials(...candidates) {
const merged = { source: "remote", upstreamUrls: {} };
for (const candidate of candidates) {
if (!candidate) {
continue;
}
if (candidate.upstreamUrls) {
merged.upstreamUrls = {
...merged.upstreamUrls,
...candidate.upstreamUrls
};
}
if (candidate.scriptPath) {
merged.scriptPath = candidate.scriptPath;
merged.source = candidate.source || merged.source;
}
if (candidate.stylePath) {
merged.stylePath = candidate.stylePath;
merged.source = candidate.source || merged.source;
}
}
return merged.scriptPath || merged.stylePath ? merged : undefined;
}
function selectUsableDesignIndexAssets(runtime, discovered, fallback, current) {
const currentScriptPath = shouldKeepCurrentScriptPath(current.scriptPath) ? current.scriptPath : DEFAULT_SCRIPT_PATH;
const currentStylePath = shouldKeepCurrentStylePath(current.stylePath) ? current.stylePath : DEFAULT_STYLE_PATH;
const html = isUsableDesignShellHtml(discovered.html)
? discovered.html
: isUsableDesignShellHtml(current.html)
? current.html
: "";
const scriptPath = isUsableDesignIndexScript(runtime, discovered.scriptPath)
? discovered.scriptPath
: isUsableDesignIndexScript(runtime, fallback.scriptPath)
? fallback.scriptPath
: currentScriptPath;
const stylePath = isUsableDesignIndexStyle(runtime, discovered.stylePath)
? discovered.stylePath
: isUsableDesignIndexStyle(runtime, fallback.stylePath)
? fallback.stylePath
: currentStylePath;
const source = scriptPath === discovered.scriptPath || stylePath === discovered.stylePath
? discovered.source
: fallback.source || current.source || "current";
return { html, scriptPath, source, stylePath };
}
function recordDesignIndexAssetHint(runtime, path, source) {
if (!runtime.assetAutoUpdate) {
return;
}
const normalizedPath = normalizePath(path);
if (isDesignIndexScriptPath(normalizedPath)) {
if (!LEGACY_SCRIPT_PATHS.has(normalizedPath) && isAcceptableRequestedEntryScript(runtime, normalizedPath)) {
updateDesignIndexAssets(runtime, { scriptPath: normalizedPath }, source);
}
return;
}
if (isDesignIndexStylePath(normalizedPath)) {
if (!LEGACY_STYLE_PATHS.has(normalizedPath)) {
updateDesignIndexAssets(runtime, { stylePath: normalizedPath }, source);
}
}
}
async function discoverRemoteDesignIndexAssets(runtime, request) {
for (const origin of runtime.upstreamOrigins) {
for (const shellUrl of upstreamDesignShellUrlCandidates(origin, runtime)) {
const shell = await fetchUpstreamDesignShell(shellUrl, request).catch((error) => {
runtime.logger?.warn?.(
`Claude Design failed to discover upstream design assets from ${shellUrl.toString()}: ${
error instanceof Error ? error.message : String(error)
}`
);
return undefined;
});
if (!shell) {
continue;
}
logRemoteDesignShell(runtime, shellUrl, shell);
const assets = mergeDesignIndexAssetPartials(
extractDesignIndexAssetsFromLinkHeaders(shell.linkHeaders, shell.url),
extractDesignIndexAssetsFromHtml(shell.body, shell.url)
);
const html = isUsableDesignShellHtml(shell.body) ? shell.body : "";
if (html || assets?.scriptPath || assets?.stylePath) {
return {
html,
origin,
scriptPath: assets?.scriptPath,
source: "remote",
stylePath: assets?.stylePath,
upstreamUrls: assets?.upstreamUrls
};
}
}
}
return undefined;
}
function logRemoteDesignShell(runtime, shellUrl, shell) {
const summary = summarizeRemoteDesignShell(shell);
runtime.logger?.warn?.(
[
"Claude Design upstream /design raw HTML:",
`request-url=${shellUrl.toString()}`,
`final-url=${shell.url || shellUrl.toString()}`,
`status=${shell.status}`,
`content-type=${shell.contentType || "unknown"}`,
"----- BEGIN RAW HTML -----",
shell.body || "",
"----- END RAW HTML -----",
"Claude Design upstream /design raw HTML summary:",
`title=${summary.title || "unknown"}`,
`has-assets-proxy=${summary.hasAssetsProxy}`,
`has-design-assets=${summary.hasDesignAssets}`,
`has-app-unavailable=${summary.hasAppUnavailable}`,
`has-cloudflare-challenge=${summary.hasCloudflareChallenge}`,
`module-scripts=${summary.moduleScripts.join(", ") || "none"}`,
`stylesheets=${summary.stylesheets.join(", ") || "none"}`
].join("\n")
);
}
function summarizeRemoteDesignShell(shell) {
const html = String(shell?.body || "");
return {
hasAppUnavailable: /App unavailable in region/i.test(html),
hasAssetsProxy: /https:\/\/assets-proxy\.anthropic\.com\/claude-ai\/v2\/assets\/v1\//i.test(html),
hasCloudflareChallenge: /\bJust a moment\b|cf_chl|Performing security verification/i.test(html),
hasDesignAssets: /(?:src|href)=["'][^"']*(?:\/design)?\/assets\//i.test(html),
moduleScripts: firstMatches(html, /<script\b[^>]*\bsrc=["']([^"']+)["'][^>]*>/gi, 5),
stylesheets: firstMatches(html, /<link\b[^>]*rel=["']stylesheet["'][^>]*href=["']([^"']+)["'][^>]*>/gi, 5),
title: firstMatch(html, /<title[^>]*>([^<]*)<\/title>/i)
};
}
function firstMatch(value, pattern) {
const match = pattern.exec(String(value || ""));
return match?.[1]?.trim() || "";
}
function firstMatches(value, pattern, limit) {
const matches = [];
let match;
while ((match = pattern.exec(String(value || ""))) && matches.length < limit) {
matches.push(match[1]);
}
return matches;
}
function upstreamDesignShellUrlCandidates(origin, runtime) {
const paths = ["/design"];
const seen = new Set();
return paths.map((path) => new URL(path, origin)).filter((url) => {
const key = url.toString();
if (seen.has(key)) {
return false;
}
seen.add(key);
return true;
});
}
function resolveClaudeAppIonDistDir(options, logger) {
const configured = [
stringValue(options.claudeAppIonDistDir),
stringValue(options.claudeAppAssetDir),
stringValue(options.claudeAppPath),
...CLAUDE_APP_ION_DIST_ENV_KEYS.map((key) => stringValue(process.env[key])),
...CLAUDE_APP_PATH_ENV_KEYS.map((key) => stringValue(process.env[key])),
...defaultClaudeAppPathCandidates()
].filter(Boolean);
const checked = [];
for (const candidate of configured) {
for (const ionDistDir of claudeAppIonDistCandidates(candidate)) {
if (checked.includes(ionDistDir)) {
continue;
}
checked.push(ionDistDir);
if (isClaudeAppIonDistDir(ionDistDir)) {
logger?.info?.(`Claude Design using Claude app assets from ${ionDistDir}`);
return ionDistDir;
}
}
}
return "";
}
function defaultClaudeAppPathCandidates() {
if (process.platform === "darwin") {
return [
"/Applications/Claude.app",
"/Applications/Claude Desktop.app",
pathModule.join(os.homedir(), "Applications", "Claude.app"),
pathModule.join(os.homedir(), "Applications", "Claude Desktop.app")
];
}
if (process.platform === "win32") {
const localAppData = process.env.LOCALAPPDATA || pathModule.join(os.homedir(), "AppData", "Local");
const programFiles = [process.env.ProgramFiles, process.env["ProgramFiles(x86)"]].filter(Boolean);
return [
pathModule.join(localAppData, "Programs", "Claude"),
pathModule.join(localAppData, "Programs", "Claude Desktop"),
...programFiles.flatMap((root) => [
pathModule.join(root, "Claude"),
pathModule.join(root, "Claude Desktop")
])
];
}
return [
"/opt/Claude",
"/opt/Claude/resources",
pathModule.join(os.homedir(), ".local", "share", "Claude")
];
}
function claudeAppIonDistCandidates(candidate) {
const expanded = expandHomePath(candidate);
const resolved = pathModule.resolve(expanded);
const candidates = [
resolved,
pathModule.join(resolved, "ion-dist"),
pathModule.join(resolved, "resources", "ion-dist"),
pathModule.join(resolved, "Resources", "ion-dist"),
pathModule.join(resolved, "Contents", "Resources", "ion-dist")
];
if (resolved.includes(`${pathModule.sep}Contents${pathModule.sep}MacOS${pathModule.sep}`)) {
candidates.push(pathModule.resolve(resolved, "..", "..", "Resources", "ion-dist"));
}
if (resolved.endsWith(".app")) {
candidates.push(pathModule.join(resolved, "Contents", "Resources", "ion-dist"));
}
return Array.from(new Set(candidates));
}
function expandHomePath(value) {
const trimmed = stringValue(value);
if (trimmed === "~") {
return os.homedir();
}
if (trimmed.startsWith("~/") || trimmed.startsWith("~\\")) {
return pathModule.join(os.homedir(), trimmed.slice(2));
}
return trimmed;
}
function isClaudeAppIonDistDir(dir) {
try {
const root = pathModule.resolve(dir);
return fs.statSync(root).isDirectory() &&
fs.statSync(pathModule.join(root, "index.html")).isFile() &&
fs.statSync(pathModule.join(root, "assets")).isDirectory();
} catch {
return false;
}
}
async function cacheRemoteDesignIndexAssets(runtime, assets, request) {
if (!assets || !runtime.assetProxy) {
return undefined;
}
const cached = {
html: isUsableDesignShellHtml(assets.html) ? assets.html : "",
source: assets.source || "remote"
};
const scriptPath = normalizePath(assets.scriptPath);
if (scriptPath) {
if (
isUsableDesignIndexScript(runtime, scriptPath) ||
await fetchAndCacheRemoteDesignIndexAsset(runtime, scriptPath, request, assets.origin, assets.upstreamUrls?.[scriptPath])
) {
cached.scriptPath = scriptPath;
}
}
const stylePath = normalizePath(assets.stylePath);
if (stylePath) {
if (
isUsableDesignIndexStyle(runtime, stylePath) ||
await fetchAndCacheRemoteDesignIndexAsset(runtime, stylePath, request, assets.origin, assets.upstreamUrls?.[stylePath])
) {
cached.stylePath = stylePath;
}
}
if (cached.html || cached.scriptPath || cached.stylePath) {
cached.upstreamUrls = assets.upstreamUrls;
return cached;
}
return undefined;
}
async function fetchAndCacheRemoteDesignIndexAsset(runtime, path, request, preferredOrigin, preferredAssetUrl) {
const origins = preferredOrigin
? [preferredOrigin, ...runtime.upstreamOrigins.filter((origin) => origin !== preferredOrigin)]
: runtime.upstreamOrigins;
const explicitUrl = parseAbsoluteHttpUrl(preferredAssetUrl);
if (explicitUrl) {
const fetched = await fetchAndMaybeCacheDesignIndexAsset(runtime, path, explicitUrl, request);
if (fetched) {
return true;
}
}
for (const origin of origins) {
for (const upstreamUrl of upstreamAssetUrlCandidates(path, origin)) {
const fetched = await fetchAndMaybeCacheDesignIndexAsset(runtime, path, upstreamUrl, request);
if (fetched) {
return true;
}
}
}
return false;
}
async function fetchAndMaybeCacheDesignIndexAsset(runtime, path, upstreamUrl, request) {
const fetched = await fetchUpstreamAsset(upstreamUrl, request).catch((error) => {
runtime.logger?.warn?.(
`Claude Design failed to fetch upstream index asset ${upstreamUrl.toString()}: ${
error instanceof Error ? error.message : String(error)
}`
);
return undefined;
});
if (fetched && fetched.status >= 200 && fetched.status < 300 && isUsableDesignIndexAssetResponse(path, fetched.contentType, fetched.body)) {
writeCachedAsset(runtime.store, path, fetched.url || upstreamUrl.toString(), fetched.contentType, fetched.body);
return true;
}
if (fetched) {
logRejectedUpstreamAsset(runtime, path, upstreamUrl, fetched);
}
return false;
}
function isUsableDesignIndexAssetResponse(path, contentType, body) {
if (!isUsableAssetBody(path, contentType, body)) {
return false;
}
if (isDesignIndexScriptPath(path)) {
return isUsableDesignIndexScriptBody(path, contentType, body);
}
return isDesignIndexStylePath(path);
}
function discoverRequestedDesignIndexAssets(store) {
const rows = queryRows(
store.database,
`SELECT path
FROM claude_design_requests
WHERE path LIKE '/design/assets/index-%'
OR path LIKE '/design/assets/%/index-%'
ORDER BY id DESC
LIMIT 100`,
[]
);
const assets = { source: "request-hint" };
for (const row of rows) {
const requestPath = normalizePath(row.path);
if (!assets.scriptPath && isDesignIndexScriptPath(requestPath) && !LEGACY_SCRIPT_PATHS.has(requestPath) && isUsableCachedEntryScript(store, requestPath)) {
assets.scriptPath = requestPath;
}
if (!assets.stylePath && isDesignIndexStylePath(requestPath) && !LEGACY_STYLE_PATHS.has(requestPath) && isUsableCachedEntryStyle(store, requestPath)) {
assets.stylePath = requestPath;
}
if (assets.scriptPath && assets.stylePath) {
return assets;
}
}
return assets.scriptPath || assets.stylePath ? assets : undefined;
}
function discoverCachedDesignIndexAssets(store) {
const rows = queryRows(
store.database,
`SELECT path, content_type, body_base64, fetched_at
FROM claude_design_assets
WHERE path LIKE '/design/assets/index-%'
OR path LIKE '/design/assets/%/index-%'
ORDER BY fetched_at DESC`,
[]
);
const assets = { source: "cache" };
for (const row of rows) {
const requestPath = normalizePath(row.path);
const body = Buffer.from(row.body_base64 || "", "base64");
if (!isUsableAssetBody(requestPath, row.content_type, body)) {
continue;
}
if (!assets.scriptPath && isUsableDesignIndexScriptBody(requestPath, row.content_type, body)) {
assets.scriptPath = requestPath;
}
if (!assets.stylePath && isDesignIndexStylePath(requestPath)) {
assets.stylePath = requestPath;
}
if (assets.scriptPath && assets.stylePath) {
return assets;
}
}
return assets.scriptPath || assets.stylePath ? assets : undefined;
}
function discoverCachedStandaloneDesignIndexAssets(store) {
const rows = queryRows(
store.database,
`SELECT path, content_type, body_base64, fetched_at
FROM claude_design_assets
WHERE path LIKE '/design/assets/index-%'
OR path LIKE '/design/assets/%/index-%'
ORDER BY fetched_at DESC`,
[]
);
const assets = { source: "standalone-cache" };
for (const row of rows) {
const requestPath = normalizePath(row.path);
const body = Buffer.from(row.body_base64 || "", "base64");
if (!isUsableAssetBody(requestPath, row.content_type, body)) {
continue;
}
if (!assets.scriptPath && isStandaloneDesignIndexScriptBody(requestPath, row.content_type, body)) {
assets.scriptPath = requestPath;
}
if (!assets.stylePath && isDesignIndexStylePath(requestPath)) {
assets.stylePath = requestPath;
}
if (assets.scriptPath && assets.stylePath) {
return assets;
}
}
return assets.scriptPath || assets.stylePath ? assets : undefined;
}
function discoverLocalDesignIndexAssets(assetDir) {
if (!assetDir) {
return undefined;
}
const localRoot = localAssetRootInfo(assetDir);
const assetRoot = localRoot.kind === "claude-app-ion-dist"
? pathModule.join(localRoot.root, "assets")
: localRoot.root;
const scripts = [];
const styles = [];
for (const entry of listLocalAssetFiles(assetRoot)) {
const requestPath = `/design/assets/${entry.relativePath}`;
if (!isDesignIndexScriptPath(requestPath) && !isDesignIndexStylePath(requestPath)) {
continue;
}
if (isDesignIndexStylePath(requestPath)) {
styles.push({ mtimeMs: entry.stat.mtimeMs, path: requestPath, size: entry.stat.size });
continue;
}
let body;
try {
body = fs.readFileSync(entry.file);
} catch {
continue;
}
scripts.push({
mtimeMs: entry.stat.mtimeMs,
path: requestPath,
score: designEntryScriptScore(requestPath, body, entry.stat),
size: entry.stat.size
});
}
scripts.sort((a, b) => b.score - a.score || b.mtimeMs - a.mtimeMs || b.size - a.size);
styles.sort((a, b) => b.mtimeMs - a.mtimeMs || b.size - a.size);
const assets = {
html: localRoot.kind === "claude-app-ion-dist" ? readLocalClaudeAppShellHtml(localRoot.root) : "",
scriptPath: scripts[0]?.path,
source: localRoot.kind === "claude-app-ion-dist" ? "claude-app" : "local",
stylePath: styles[0]?.path
};
return assets.html || assets.scriptPath || assets.stylePath ? assets : undefined;
}
function localAssetDirExists(assetDir) {
if (!assetDir) {
return false;
}
try {
return fs.statSync(pathModule.resolve(expandHomePath(assetDir))).isDirectory();
} catch {
return false;
}
}
function localAssetRootInfo(assetDir) {
const root = pathModule.resolve(expandHomePath(assetDir));
return isClaudeAppIonDistDir(root)
? { kind: "claude-app-ion-dist", root }
: { kind: "design-assets", root };
}
function readLocalClaudeAppShellHtml(root) {
const indexPath = pathModule.join(root, "index.html");
try {
const html = fs.readFileSync(indexPath, "utf8");
return isUsableDesignShellHtml(html) ? html : "";
} catch {
return "";
}
}
function listLocalAssetFiles(assetRoot) {
const files = [];
const stack = [""];
while (stack.length) {
const relativeDir = stack.pop();
const absoluteDir = pathModule.join(assetRoot, relativeDir);
let entries;
try {
entries = fs.readdirSync(absoluteDir, { withFileTypes: true });
} catch {
continue;
}
for (const entry of entries) {
const relativePath = pathModule.join(relativeDir, entry.name);
const file = pathModule.join(assetRoot, relativePath);
if (entry.isDirectory()) {
stack.push(relativePath);
continue;
}
if (!entry.isFile()) {
continue;
}
let stat;
try {
stat = fs.statSync(file);
} catch {
continue;
}
files.push({
file,
relativePath: relativePath.split(pathModule.sep).join("/"),
stat
});
}
}
return files;
}
function isDesignIndexScriptPath(path) {
return /^\/design\/assets\/(?:v\d+\/)?index-[^/]+\.js$/i.test(path);
}
function isDesignIndexStylePath(path) {
return /^\/design\/assets\/(?:v\d+\/)?index-[^/]+\.css$/i.test(path);
}
function shouldKeepCurrentScriptPath(path) {
const normalizedPath = normalizePath(path);
return isDesignIndexScriptPath(normalizedPath) && !LEGACY_SCRIPT_PATHS.has(normalizedPath);
}
function shouldKeepCurrentStylePath(path) {
const normalizedPath = normalizePath(path);
return isDesignIndexStylePath(normalizedPath) && !LEGACY_STYLE_PATHS.has(normalizedPath);
}
function isUsableDesignIndexScript(runtime, path) {
const normalizedPath = normalizePath(path);
if (!isDesignIndexScriptPath(normalizedPath)) {
return false;
}
if (LEGACY_SCRIPT_PATHS.has(normalizedPath)) {
return false;
}
if (runtime?.store && isUsableCachedEntryScript(runtime.store, normalizedPath)) {
return true;
}
return isUsableLocalEntryScript(runtime?.assetDir, normalizedPath);
}
function isUsableDesignIndexStyle(runtime, path) {
const normalizedPath = normalizePath(path);
if (!isDesignIndexStylePath(normalizedPath)) {
return false;
}
if (LEGACY_STYLE_PATHS.has(normalizedPath)) {
return false;
}
if (runtime?.store && isUsableCachedEntryStyle(runtime.store, normalizedPath)) {
return true;
}
return Boolean(readLocalAsset(runtime?.assetDir, normalizedPath));
}
function isUsableCachedEntryScript(store, path) {
const cached = readCachedAsset(store, path);
if (!cached) {
return false;
}
const body = Buffer.from(cached.bodyBase64 || "", "base64");
return !isFallbackAsset(cached) && isUsableDesignIndexScriptBody(path, cached.contentType, body);
}
function isUsableCachedEntryStyle(store, path) {
const cached = readCachedAsset(store, path);
if (!cached) {
return false;
}
const body = Buffer.from(cached.bodyBase64 || "", "base64");
return !isFallbackAsset(cached) && isUsableAssetBody(path, cached.contentType, body) && isDesignIndexStylePath(path);
}
function isUsableLocalEntryScript(assetDir, path) {
const localAsset = readLocalAsset(assetDir, path);
return Boolean(localAsset && isUsableDesignIndexScriptBody(path, localAsset.contentType, localAsset.body));
}
function isAcceptableRequestedEntryScript(runtime, path) {
const cached = runtime?.store ? readCachedAsset(runtime.store, path) : undefined;
if (!cached) {
return true;
}
const body = Buffer.from(cached.bodyBase64 || "", "base64");
if (!isUsableAssetBody(path, cached.contentType, body)) {
return true;
}
return isUsableDesignIndexScriptBody(path, cached.contentType, body);
}
function isUsableDesignIndexScriptBody(path, contentType, body) {
return isDesignIndexScriptPath(path) && isUsableAssetBody(path, contentType, body);
}
function isStandaloneDesignIndexScriptBody(path, contentType, body) {
if (!isUsableDesignIndexScriptBody(path, contentType, body)) {
return false;
}
const text = body.toString("utf8", 0, Math.min(body.length, 2 * 1024 * 1024));
return [
"anthropic.omelette.api.v1alpha.OmeletteService",
"/v1/design",
"/design/v1/design",
"__OMELETTE_ME__"
].some((marker) => text.includes(marker));
}
function isDesignEntryScriptBuffer(path, body) {
return isDesignIndexScriptPath(path) && designEntryScriptScore(path, body, { mtimeMs: 0, size: body.length }) > 1_000_000;
}
function designEntryScriptScore(path, body, stat) {
if (!isDesignIndexScriptPath(path)) {
return 0;
}
const text = body.toString("utf8", 0, Math.min(body.length, 2 * 1024 * 1024));
const designMarkers = [
"anthropic.omelette.api.v1alpha.OmeletteService",
"/v1/design",
"/design/v1/design",
"__OMELETTE_ME__",
"desktop_design_entrypoint",
"DiscoverDesignRoute",
"claude_ai_omelette_enabled",
"path:\"design\"",
"OmeletteService"
];
if (!designMarkers.some((marker) => text.includes(marker))) {
return 0;
}
let score = Number(stat.size || body.length);
if (text.includes("__vite__mapDeps")) {
score += 1_000_000;
}
if (text.includes("createRoot")) {
score += 1_000_000;
}
if (text.includes("document.getElementById")) {
score += 500_000;
}
if (text.includes("ProjectPage") || text.includes("ProjectsPage")) {
score += 250_000;
}
return score;
}
function extractDesignIndexAssetsFromHtml(body, baseUrl) {
const html = String(body || "");
const assets = { source: "remote-html", upstreamUrls: {} };
const pattern = /\b(?:src|href)=["']([^"']+)["']/gi;
let match;
while ((match = pattern.exec(html))) {
const asset = normalizeDesignAssetReference(match[1], baseUrl);
if (asset.upstreamUrl) {
assets.upstreamUrls[asset.path] = asset.upstreamUrl;
}
if (isDesignIndexScriptPath(asset.path)) {
assets.scriptPath = asset.path;
} else if (isDesignIndexStylePath(asset.path)) {
assets.stylePath = asset.path;
}
}
return assets.scriptPath || assets.stylePath ? assets : undefined;
}
function extractDesignIndexAssetsFromLinkHeaders(linkHeaders, baseUrl) {
const headers = Array.isArray(linkHeaders) ? linkHeaders : linkHeaders ? [linkHeaders] : [];
const assets = { source: "remote-link", upstreamUrls: {} };
for (const header of headers) {
const pattern = /<([^>]+)>/g;
let match;
while ((match = pattern.exec(String(header)))) {
const asset = normalizeDesignAssetReference(match[1], baseUrl);
if (asset.upstreamUrl) {
assets.upstreamUrls[asset.path] = asset.upstreamUrl;
}
if (isDesignIndexScriptPath(asset.path)) {
assets.scriptPath = asset.path;
} else if (isDesignIndexStylePath(asset.path)) {
assets.stylePath = asset.path;
}
}
}
return assets.scriptPath || assets.stylePath ? assets : undefined;
}
function normalizeDesignAssetPath(value, baseUrl) {
return normalizeDesignAssetReference(value, baseUrl).path;
}
function normalizeDesignAssetReference(value, baseUrl) {
const raw = stringValue(value);
if (!raw) {
return { path: "" };
}
try {
const parsed = new URL(raw, normalizeDesignAssetBaseUrl(baseUrl));
const path = normalizeDesignAssetRequestPath(parsed.pathname);
return {
path,
...(isDesignAssetFilePath(path) && /^https?:$/i.test(parsed.protocol) ? { upstreamUrl: parsed.toString() } : {})
};
} catch {
return { path: normalizeDesignAssetRequestPath(raw.split("?")[0]) };
}
}
function normalizeDesignAssetBaseUrl(baseUrl) {
const value = stringValue(baseUrl) || DEFAULT_UPSTREAM_ORIGIN;
try {
const parsed = new URL(value);
if (parsed.pathname === "/design") {
parsed.pathname = "/design/";
}
return parsed.toString();
} catch {
return value;
}
}
function normalizeDesignAssetRequestPath(value) {
const normalizedPath = normalizePath(value);
if (normalizedPath.startsWith("/design/assets/")) {
return normalizedPath;
}
if (normalizedPath.startsWith("/assets/")) {
return `/design${normalizedPath}`;
}
const assetPath = designAssetFileName(normalizedPath);
if (assetPath) {
return `/design/assets/${assetPath}`;
}
return normalizedPath;
}
function designAssetFileName(path) {
const match = normalizePath(path).match(/\/assets\/((?:v\d+\/)?[^/?#]+\.(?:css|js|mjs|avif|gif|ico|jpe?g|json|png|svg|webp|woff2?))$/i);
return match?.[1];
}
function isDesignAssetFilePath(path) {
return /^\/design\/assets\/(?:[^/?#]+\/)*[^/?#]+\.(?:css|js|mjs|avif|gif|ico|jpe?g|json|png|svg|webp|woff2?)$/i.test(normalizePath(path));
}
function questionsViewerFallbackModule() {
return `
const REACT_ELEMENT = Symbol.for("react.transitional.element");
function e(type, props, ...children) {
const nextProps = { ...(props || {}) };
if (children.length === 1) {
nextProps.children = children[0];
} else if (children.length > 1) {
nextProps.children = children;
}
return {
$$typeof: REACT_ELEMENT,
key: nextProps.key == null ? null : String(nextProps.key),
props: nextProps,
ref: nextProps.ref == null ? null : nextProps.ref,
type
};
}
const styles = {
button: {
background: "#1f1e1d",
border: "0",
borderRadius: 6,
color: "#fff",
cursor: "pointer",
fontSize: 13,
fontWeight: 600,
padding: "9px 12px"
},
form: {
boxSizing: "border-box",
color: "#1f1e1d",
display: "flex",
flexDirection: "column",
fontFamily: "Inter, system-ui, -apple-system, BlinkMacSystemFont, sans-serif",
gap: 16,
height: "100%",
overflow: "auto",
padding: 24
},
option: {
alignItems: "center",
border: "1px solid #dedbd4",
borderRadius: 6,
display: "flex",
gap: 8,
padding: "8px 10px"
},
question: {
background: "#fff",
border: "1px solid #e8e3db",
borderRadius: 8,
display: "flex",
flexDirection: "column",
gap: 10,
padding: 14
},
secondaryButton: {
background: "transparent",
border: "1px solid #cfc9bf",
borderRadius: 6,
color: "#403d39",
cursor: "pointer",
fontSize: 13,
fontWeight: 600,
padding: "9px 12px"
}
};
function normalizeSpec(spec) {
const record = spec && typeof spec === "object" && !Array.isArray(spec) ? spec : {};
const questions = Array.isArray(record.questions) ? record.questions : [];
return {
title: typeof record.title === "string" && record.title.trim() ? record.title : "Claude has some questions",
questions: questions.map((question, index) => normalizeQuestion(question, index)).filter(Boolean)
};
}
function normalizeQuestion(question, index) {
const record = question && typeof question === "object" && !Array.isArray(question) ? question : {};
const title = String(record.title || record.question || record.label || "Question " + (index + 1));
const rawOptions = Array.isArray(record.options) ? record.options : [];
const options = rawOptions.map((option) => String(option)).filter(Boolean);
let kind = String(record.kind || "").trim();
if (!["text-options", "svg-options", "slider", "file", "freeform"].includes(kind)) {
kind = record.isSvg ? "svg-options" : options.length > 0 ? "text-options" : "freeform";
}
if ((kind === "text-options" || kind === "svg-options") && options.length === 0) {
kind = "freeform";
}
return {
accept: typeof record.accept === "string" ? record.accept : undefined,
default: record.default,
id: String(record.id || record.name || title).toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "") || "question_" + (index + 1),
kind,
max: record.max,
min: record.min,
multi: record.multi === true,
options,
step: record.step,
subtitle: typeof record.subtitle === "string" ? record.subtitle : "",
title
};
}
function field(question) {
if (question.kind === "slider") {
return e("input", {
defaultValue: question.default ?? question.min ?? 0,
max: question.max ?? 100,
min: question.min ?? 0,
name: question.id,
step: question.step ?? 1,
style: { width: "100%" },
type: "range"
});
}
if (question.kind === "file") {
return e("input", {
accept: question.accept || undefined,
name: question.id,
type: "file"
});
}
if (question.kind === "freeform") {
return e("textarea", {
name: question.id,
placeholder: "Type your answer",
rows: 4,
style: { border: "1px solid #d8d2c8", borderRadius: 6, font: "inherit", padding: 10, resize: "vertical" }
});
}
const type = question.multi ? "checkbox" : "radio";
return e(
"div",
{ style: { display: "flex", flexDirection: "column", gap: 8 } },
...question.options.map((option, index) =>
e(
"label",
{ key: question.id + "-" + index, style: styles.option },
e("input", {
defaultChecked: index === 0,
name: question.id,
type,
value: option
}),
question.kind === "svg-options"
? e("span", { dangerouslySetInnerHTML: { __html: option }, style: { display: "inline-flex", height: 52, width: 72 } })
: e("span", { style: { lineHeight: 1.35 } }, option)
)
)
);
}
export function QuestionsViewer(props) {
const spec = normalizeSpec(props && props.spec);
const submit = (event) => {
event.preventDefault();
const form = event.currentTarget;
const data = new FormData(form);
const answers = {};
for (const question of spec.questions) {
const values = data.getAll(question.id).map((value) => String(value)).filter(Boolean);
answers[question.id] = question.multi ? values.join(", ") : values[0] || "";
}
props && typeof props.onSubmit === "function" && props.onSubmit(answers, []);
};
return e(
"form",
{ onSubmit: submit, style: styles.form },
e("div", { style: { display: "flex", flexDirection: "column", gap: 4 } },
e("h2", { style: { fontSize: 20, lineHeight: 1.2, margin: 0 } }, spec.title),
props && props.streaming ? e("p", { style: { color: "#766f65", fontSize: 13, margin: 0 } }, "Claude is preparing questions...") : null
),
...spec.questions.map((question) =>
e(
"section",
{ key: question.id, style: styles.question },
e("div", null,
e("div", { style: { fontSize: 14, fontWeight: 650, lineHeight: 1.3 } }, question.title),
question.subtitle ? e("div", { style: { color: "#766f65", fontSize: 12, marginTop: 3 } }, question.subtitle) : null
),
field(question)
)
),
e("div", { style: { display: "flex", gap: 8, justifyContent: "flex-end" } },
e("button", {
onClick: () => props && typeof props.onTimeout === "function" && props.onTimeout(),
style: styles.secondaryButton,
type: "button"
}, "Use defaults"),
e("button", { style: styles.button, type: "submit" }, "Submit answers")
)
);
}
export default QuestionsViewer;
`;
}
function handleOrganizationApi(runtime, method, path, request, requestBody) {
const parts = path.split("/").filter(Boolean);
const organizationUuid = parts[2] || runtime.me.organizationUuid;
const tail = parts.slice(3);
if (tail.length === 0) {
return jsonResponse(200, organizationPayload(runtime.me, organizationUuid));
}
const first = tail[0];
if (["members", "memberships"].includes(first)) {
return jsonResponse(200, runtime.me.memberships || []);
}
if (["models", "model_presets", "model-presets"].includes(first)) {
return jsonResponse(200, runtime.me.modelPresets || []);
}
if (["features", "feature_flags", "growthbook", "experiments"].includes(first)) {
return jsonResponse(200, featureFlagsPayload(runtime.me));
}
if (["settings", "billing", "oauth_tokens", "oauth-tokens"].includes(first)) {
return jsonResponse(200, { ok: true, organization_uuid: organizationUuid });
}
const collection = canonicalCollection(first);
if (collection && tail.length === 1) {
if (method === "GET") {
return jsonResponse(200, listItems(runtime.store, collection, organizationUuid));
}
if (method === "POST") {
return jsonResponse(201, createItem(runtime.store, collection, organizationUuid, parseJsonBody(requestBody), runtime.me));
}
}
if (collection && tail.length >= 2) {
const itemUuid = tail[1];
const subresource = tail[2];
if (subresource === "messages" || subresource === "turns") {
if (method === "GET") {
return jsonResponse(200, getItemMessages(runtime.store, collection, itemUuid));
}
if (method === "POST") {
return jsonResponse(201, appendItemMessage(runtime.store, collection, organizationUuid, itemUuid, parseJsonBody(requestBody), runtime.me));
}
}
if (["completion", "completions", "generate", "run"].includes(subresource)) {
const wantsSse = headerIncludes(request.headers.accept, "text/event-stream");
if (wantsSse) {
return sseCompletionResponse(itemUuid);
}
return jsonResponse(200, completionPayload(itemUuid));
}
if (method === "GET") {
return jsonResponse(200, getItem(runtime.store, collection, organizationUuid, itemUuid, runtime.me));
}
if (method === "PATCH" || method === "PUT") {
return jsonResponse(200, updateItem(runtime.store, collection, organizationUuid, itemUuid, parseJsonBody(requestBody), runtime.me));
}
if (method === "DELETE") {
deleteItem(runtime.store, collection, itemUuid);
return jsonResponse(200, { deleted: true, id: itemUuid, uuid: itemUuid });
}
}
return handleGenericApi(method, path, requestBody);
}
function handleGenericApi(method, path, requestBody) {
if (method === "GET") {
return jsonResponse(200, {
count: 0,
data: [],
items: [],
ok: true,
path,
results: []
});
}
if (method === "DELETE") {
return jsonResponse(200, { deleted: true, ok: true, path });
}
const payload = parseJsonBody(requestBody);
return jsonResponse(200, {
created_at: new Date().toISOString(),
id: randomUuid(),
ok: true,
path,
request: payload,
uuid: randomUuid()
});
}
async function handleAdminRequest(runtime, backend, request, response, helpers) {
const url = new URL(request.url || "/", "http://127.0.0.1");
const method = (request.method || "GET").toUpperCase();
const path = normalizePath(url.pathname);
if (method === "GET" && path === "/plugins/claude-design") {
helpers.sendJson(response, 200, {
autoAnswerQuestions: runtime.autoAnswerQuestions,
backend: backend.url,
dbFile: runtime.store.dbFile,
defaultGatewayModel: runtime.defaultGatewayModel,
designIndexAssets: runtime.designIndexAssets,
frontendDefaultModel: runtime.frontendDefaultModel,
gatewayConfigPath: runtime.gatewayConfigPath,
gatewayModels: runtime.gatewayModelPresets,
plugin: "claude-design",
proxy: {
assetDir: runtime.assetDir,
assetPassthrough: runtime.assetPassthrough,
assetSource: runtime.assetSource,
fallbackHosts: runtime.fallbackRouteHosts,
host: runtime.routeHost,
paths: runtime.routePaths
},
upstreamOrigins: runtime.upstreamOrigins,
routes: {
assets: "/plugins/claude-design/assets",
projects: "/plugins/claude-design/projects",
requests: "/plugins/claude-design/requests",
responses: "/plugins/claude-design/responses"
}
});
return;
}
if (method === "GET" && path === "/plugins/claude-design/requests") {
helpers.sendJson(response, 200, {
requests: listRequests(runtime.store, numberValue(url.searchParams.get("limit")) || 100)
});
return;
}
if (method === "GET" && path === "/plugins/claude-design/assets") {
helpers.sendJson(response, 200, {
assets: listCachedAssets(runtime.store)
});
return;
}
if (method === "GET" && path === "/plugins/claude-design/projects") {
helpers.sendJson(response, 200, {
projects: listOmeletteProjects(runtime)
});
return;
}
if (method === "DELETE" && path === "/plugins/claude-design/assets") {
runtime.store.database.run("DELETE FROM claude_design_assets");
runtime.store.persist();
helpers.sendJson(response, 200, { cleared: true });
return;
}
if (method === "DELETE" && path === "/plugins/claude-design/requests") {
runtime.store.database.run("DELETE FROM claude_design_requests");
runtime.store.persist();
helpers.sendJson(response, 200, { cleared: true });
return;
}
if (method === "GET" && path === "/plugins/claude-design/responses") {
helpers.sendJson(response, 200, {
responses: listStoredResponses(runtime.store)
});
return;
}
if ((method === "POST" || method === "PUT") && path === "/plugins/claude-design/responses") {
const body = await helpers.readJson(request);
const record = isRecord(body) ? body : {};
const mockMethod = stringValue(record.method)?.toUpperCase() || "ANY";
const mockPath = normalizePath(stringValue(record.path) || "");
if (!mockPath) {
helpers.sendJson(response, 400, { error: { message: "path is required." } });
return;
}
const status = numberValue(record.status) || 200;
const headers = isRecord(record.headers) ? record.headers : { "content-type": "application/json; charset=utf-8" };
const responseBody = record.body === undefined ? {} : record.body;
upsertStoredResponse(runtime.store, mockMethod, mockPath, status, headers, responseBody);
helpers.sendJson(response, 200, {
method: mockMethod,
path: mockPath,
stored: true
});
return;
}
if (method === "DELETE" && path === "/plugins/claude-design/responses") {
const mockMethod = (url.searchParams.get("method") || "ANY").toUpperCase();
const mockPath = normalizePath(url.searchParams.get("path") || "");
if (!mockPath) {
helpers.sendJson(response, 400, { error: { message: "path query parameter is required." } });
return;
}
runtime.store.database.run("DELETE FROM claude_design_responses WHERE method = ? AND path = ?", [mockMethod, mockPath]);
runtime.store.persist();
helpers.sendJson(response, 200, {
deleted: true,
method: mockMethod,
path: mockPath
});
return;
}
helpers.sendJson(response, 404, {
error: {
message: `Unknown Claude Design admin route: ${method} ${path}`
}
});
}
function bootstrapPayload(me) {
const account = accountPayload(me);
const organization = organizationPayload(me, me.organizationUuid);
const featureFlags = featureFlagsPayload(me);
const growthbook = featureFlags.growthbook;
const statsig = statsigPayload();
return {
account,
accountUuid: me.accountUuid,
activeOrganizationUuid: me.organizationUuid,
canManageDs: me.canManageDs,
cowork_sysprompt_map: null,
current_user_access: currentUserAccessPayload(me),
defaultModelId: me.defaultModelId,
displayName: me.displayName,
email: me.email,
featureFlagsOrgUuid: me.organizationUuid,
features: featureFlags.features,
gated_imports: {},
gated_imports_build_id: "",
gated_messages: null,
growthbook,
growthbookPayload: growthbook,
me,
memberships: me.memberships || [],
memory_mode: "off",
model_selector_config: {},
model_selector_state: {},
modelPresets: me.modelPresets || [],
org_growthbook: growthbook,
org_statsig: statsig,
organization,
organizationUuid: me.organizationUuid,
organizations: organizationsPayload(me),
server_localizations: {},
settings: organization.settings,
statsig,
system_prompts: {},
user: account
};
}
function accountPayload(me) {
const organizations = organizationsPayload(me);
return {
account_uuid: me.accountUuid,
accountUuid: me.accountUuid,
display_name: me.displayName,
displayName: me.displayName,
email: me.email,
has_oauth_tokens: me.hasOauthTokens,
hasOauthTokens: me.hasOauthTokens,
memberships: organizations.map((organization) => ({
organization,
organization_uuid: organization.uuid,
organizationUuid: organization.uuid,
role: "owner"
})),
name: me.displayName,
settings: accountSettingsPayload(),
uuid: me.accountUuid
};
}
function authSessionPayload(me) {
return {
account: accountPayload(me),
active_organization_uuid: me.organizationUuid,
activeOrganizationUuid: me.organizationUuid,
authenticated: true,
email: me.email,
expires: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(),
me: accountPayload(me),
ok: true,
organization: organizationPayload(me, me.organizationUuid),
organization_uuid: me.organizationUuid,
organizationUuid: me.organizationUuid,
session: {
account_uuid: me.accountUuid,
organization_uuid: me.organizationUuid,
user_uuid: me.accountUuid
},
user: accountPayload(me),
user_uuid: me.accountUuid,
userUuid: me.accountUuid
};
}
function organizationPayload(me, organizationUuid) {
const settings = organizationSettingsPayload(me);
return {
access_level: me.accessLevel,
accessLevel: me.accessLevel,
capabilities: ["chat", "omelette", "raven"],
default_model_id: me.defaultModelId,
defaultModelId: me.defaultModelId,
entitlements: ["omelette"],
features: {
claude_design: true,
design: true,
omelette: true
},
is_personal: me.isPersonalOrg,
isPersonalOrg: me.isPersonalOrg,
name: me.orgName,
organization_uuid: organizationUuid,
orgName: me.orgName,
rbac_entitlements: ["omelette"],
rbacEntitlements: ["omelette"],
settings,
uuid: organizationUuid
};
}
function accountSettingsPayload() {
return {
preview_feature_uses_artifacts: true
};
}
function organizationSettingsPayload(me) {
return {
claude_ai_omelette_enabled: true,
default_model_id: me.defaultModelId,
defaultModelId: me.defaultModelId,
omelette: true
};
}
function organizationsPayload(me) {
return [
organizationPayload(me, me.organizationUuid),
...(me.memberships || [])
.filter((membership) => membership && membership.uuid !== me.organizationUuid)
.map((membership) => ({
...organizationPayload(me, membership.uuid),
name: membership.name || me.orgName
}))
];
}
function featureFlagsPayload(me) {
const features = claudeDesignFeatureValues();
const growthbook = claudeDesignGrowthbookPayload(me, features);
return {
experiments: {},
feature_flags: features,
features,
growthbook,
growthbookPayload: growthbook
};
}
function claudeDesignFeatureValues() {
return {
admin_settings_chicago: true,
claude_ai_omelette_enabled: true,
claude_design: true,
design: true,
desktop_design_entrypoint: {
openBehavior: "embed",
showOnTabs: ["chat", "cowork"]
},
omelette: true,
omelette_admin_settings: true,
trellis: true
};
}
function claudeDesignGrowthbookPayload(me, featureValues = claudeDesignFeatureValues()) {
const configured = parseMaybeJson(me?.growthbookPayload, {});
const configuredFeatures = isRecord(configured.features) ? configured.features : {};
const features = { ...configuredFeatures };
for (const [name, value] of Object.entries(featureValues)) {
features[growthbookFeatureKey(name)] = growthbookFeatureDefinition(value);
}
return {
...configured,
features
};
}
function growthbookFeatureDefinition(value) {
return {
defaultValue: value
};
}
function growthbookFeatureKey(name) {
const value = stringValue(name) || "";
if (value.startsWith("__gb__")) {
return value.slice(6);
}
let hash = 0;
for (let index = 0; index < value.length; index += 1) {
hash = (hash << 5) - hash + value.charCodeAt(index);
hash &= hash;
}
return String((0xffffffff & hash) >>> 0);
}
function statsigPayload() {
return {
dynamic_configs: {},
feature_gates: {},
layer_configs: {},
sdkParams: {}
};
}
function currentUserAccessPayload(me) {
return {
account_uuid: me.accountUuid,
accountUuid: me.accountUuid,
account_permissions: claudeDesignAccountPermissions(),
features: claudeDesignAccessFeatures(),
organization_uuid: me.organizationUuid,
organizationUuid: me.organizationUuid,
permissions: ["admin", "owner", "omelette", "claude_design:manage"],
role: "owner"
};
}
function claudeDesignAccessFeatures() {
return [
"chat",
"claude_api_in_artifacts",
"claude_code",
"claude_code_desktop",
"claude_code_web",
"cowork",
"interactive_content",
"mcp_artifacts",
"omelette",
"skills",
"wiggle",
"work_across_apps"
].map((feature) => ({
feature,
status: "available"
}));
}
function claudeDesignAccountPermissions() {
return [
"admin",
"claude_design:manage",
"members:view",
"organization:manage_settings",
"owner",
"workspaces:view"
];
}
function handleBootstrapSubresource(runtime, method, path) {
if (method !== "GET") {
return undefined;
}
const match = path.match(/^\/(?:api|edge-api)\/bootstrap\/[^/]+\/([^/]+)\/?$/) ||
path.match(/^\/_bootstrap\/[^/]+\/([^/]+)\/?$/);
if (!match) {
return undefined;
}
switch (match[1]) {
case "current_user_access":
return jsonResponse(200, currentUserAccessPayload(runtime.me));
case "cowork_sysprompt_map":
return jsonResponse(200, null);
case "gated_messages":
return jsonResponse(200, null);
case "model_selector_config":
case "model_selector_state":
case "server_localizations":
case "system_prompts":
return jsonResponse(200, {});
default:
return undefined;
}
}
function canonicalCollection(value) {
const normalized = String(value || "").toLowerCase();
const aliases = {
artifacts: "artifacts",
chat_conversations: "chat_conversations",
conversations: "chat_conversations",
design: "designs",
design_projects: "design_projects",
design_sessions: "design_sessions",
designs: "designs",
documents: "documents",
files: "files",
projects: "projects",
sessions: "design_sessions"
};
return aliases[normalized];
}
function listItems(store, collection, organizationUuid) {
return queryRows(
store.database,
"SELECT collection, uuid, created_at, updated_at, title, model, data_json, messages_json FROM claude_design_items WHERE collection = ? ORDER BY updated_at DESC",
[collection]
).map((row) => itemFromRow(row, organizationUuid));
}
function createItem(store, collection, organizationUuid, body, me) {
const now = new Date().toISOString();
const uuid = stringValue(body.uuid) || stringValue(body.id) || randomUuid();
const title = stringValue(body.title) || stringValue(body.name) || "Untitled design";
const model = stringValue(body.model) || me.defaultModelId;
const data = {
...body,
organization_uuid: organizationUuid
};
store.database.run(
"INSERT OR REPLACE INTO claude_design_items (collection, uuid, created_at, updated_at, title, model, data_json, messages_json) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
[collection, uuid, now, now, title, model, JSON.stringify(data), JSON.stringify([])]
);
store.persist();
return getItem(store, collection, organizationUuid, uuid, me);
}
function getItem(store, collection, organizationUuid, uuid, me) {
const row = queryRows(
store.database,
"SELECT collection, uuid, created_at, updated_at, title, model, data_json, messages_json FROM claude_design_items WHERE collection = ? AND uuid = ? LIMIT 1",
[collection, uuid]
)[0];
if (row) {
return itemFromRow(row, organizationUuid);
}
const created = createItem(store, collection, organizationUuid, { title: "Untitled design", uuid }, me);
return {
...created,
createdByFallback: true
};
}
function getExistingItem(store, collection, organizationUuid, uuid) {
const row = queryRows(
store.database,
"SELECT collection, uuid, created_at, updated_at, title, model, data_json, messages_json FROM claude_design_items WHERE collection = ? AND uuid = ? LIMIT 1",
[collection, uuid]
)[0];
return row ? itemFromRow(row, organizationUuid) : undefined;
}
function updateItem(store, collection, organizationUuid, uuid, body, me) {
const existing = getItem(store, collection, organizationUuid, uuid, me);
const now = new Date().toISOString();
const title = stringValue(body.title) || stringValue(body.name) || existing.title || "Untitled design";
const model = stringValue(body.model) || existing.model || me.defaultModelId;
const data = {
...existing,
...body,
organization_uuid: organizationUuid
};
delete data.messages;
store.database.run(
"UPDATE claude_design_items SET updated_at = ?, title = ?, model = ?, data_json = ? WHERE collection = ? AND uuid = ?",
[now, title, model, JSON.stringify(data), collection, uuid]
);
store.persist();
return getItem(store, collection, organizationUuid, uuid, me);
}
function deleteItem(store, collection, uuid) {
store.database.run("DELETE FROM claude_design_items WHERE collection = ? AND uuid = ?", [collection, uuid]);
store.persist();
}
function getItemMessages(store, collection, uuid) {
const row = queryRows(store.database, "SELECT messages_json FROM claude_design_items WHERE collection = ? AND uuid = ? LIMIT 1", [
collection,
uuid
])[0];
return parseMaybeJson(row?.messages_json, []);
}
function appendItemMessage(store, collection, organizationUuid, uuid, body, me) {
const item = getItem(store, collection, organizationUuid, uuid, me);
const now = new Date().toISOString();
const messages = Array.isArray(item.messages) ? item.messages : [];
const content = body.content || body.text || body.prompt || body.message || "";
const message = {
content,
created_at: now,
id: randomUuid(),
role: stringValue(body.role) || stringValue(body.sender) || "user",
uuid: randomUuid()
};
messages.push(message);
store.database.run("UPDATE claude_design_items SET updated_at = ?, messages_json = ? WHERE collection = ? AND uuid = ?", [
now,
JSON.stringify(messages),
collection,
uuid
]);
store.persist();
return {
conversation: getItem(store, collection, organizationUuid, uuid, me),
message
};
}
function itemFromRow(row, organizationUuid) {
const data = parseMaybeJson(row.data_json, {});
const messages = sanitizeStoredMessages(parseMaybeJson(row.messages_json, []));
return {
...data,
collection: row.collection,
created_at: row.created_at,
id: row.uuid,
messages,
model: row.model,
name: row.title,
organization_uuid: organizationUuid,
title: row.title,
updated_at: row.updated_at,
uuid: row.uuid
};
}
function createOmeletteProject(runtime, requestBody) {
const request = decodeCreateProjectRequest(requestBody);
const now = new Date().toISOString();
const uuid = randomUuid();
const name = request.name || request.templateTitle || "Untitled design";
const type = normalizeProjectTypeNumber(request.type);
const data = {
can_edit: true,
description: request.description || "",
intro_text: request.introText || "",
is_owned: true,
name,
organization_uuid: runtime.me.organizationUuid,
owner_display_name: runtime.me.displayName,
owner_email: runtime.me.email,
owner_uuid: runtime.me.accountUuid,
project_id: uuid,
project_type: type,
sharing: {
team_can_comment: false,
team_can_edit: false,
view_mode: "private"
},
source: "omelette-connect-rpc",
template_id: request.templateId || "",
template_title: request.templateTitle || "",
title: name,
type,
uuid
};
runtime.store.database.run(
"INSERT OR REPLACE INTO claude_design_items (collection, uuid, created_at, updated_at, title, model, data_json, messages_json) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
[PROJECT_COLLECTION, uuid, now, now, name, runtime.me.defaultModelId, JSON.stringify(data), JSON.stringify([])]
);
runtime.store.persist();
return {
...data,
created_at: now,
updated_at: now
};
}
function getOmeletteProject(runtime, projectId) {
const uuid = stringValue(projectId) || randomUuid();
if (uuid) {
const row = queryRows(
runtime.store.database,
"SELECT collection, uuid, created_at, updated_at, title, model, data_json, messages_json FROM claude_design_items WHERE collection = ? AND uuid = ? LIMIT 1",
[PROJECT_COLLECTION, uuid]
)[0];
if (row) {
return omeletteProjectFromRow(row, runtime.me);
}
}
const now = new Date().toISOString();
const name = "Untitled design";
const data = {
can_edit: true,
is_owned: true,
name,
organization_uuid: runtime.me.organizationUuid,
owner_display_name: runtime.me.displayName,
owner_email: runtime.me.email,
owner_uuid: runtime.me.accountUuid,
project_id: uuid,
project_type: PROJECT_TYPE_PROJECT,
sharing: {
view_mode: "private"
},
source: "omelette-connect-rpc-fallback",
title: name,
type: PROJECT_TYPE_PROJECT,
uuid
};
runtime.store.database.run(
"INSERT OR REPLACE INTO claude_design_items (collection, uuid, created_at, updated_at, title, model, data_json, messages_json) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
[PROJECT_COLLECTION, uuid, now, now, name, runtime.me.defaultModelId, JSON.stringify(data), JSON.stringify([])]
);
runtime.store.persist();
return omeletteProjectFromRow(
{
collection: PROJECT_COLLECTION,
created_at: now,
data_json: JSON.stringify(data),
messages_json: "[]",
model: runtime.me.defaultModelId,
title: name,
updated_at: now,
uuid
},
runtime.me
);
}
function listOmeletteProjects(runtime, typeFilter, publishedOnly) {
const projects = queryRows(
runtime.store.database,
"SELECT collection, uuid, created_at, updated_at, title, model, data_json, messages_json FROM claude_design_items WHERE collection = ? ORDER BY updated_at DESC",
[PROJECT_COLLECTION]
).map((row) => omeletteProjectFromRow(row, runtime.me));
return projects.filter((project) => {
if (typeFilter && project.type !== typeFilter) {
return false;
}
if (publishedOnly && !project.publishedAt) {
return false;
}
return true;
});
}
function getProjectRow(runtime, projectId) {
const uuid = stringValue(projectId);
if (!uuid) {
return undefined;
}
return queryRows(
runtime.store.database,
"SELECT collection, uuid, created_at, updated_at, title, model, data_json, messages_json FROM claude_design_items WHERE collection = ? AND uuid = ? LIMIT 1",
[PROJECT_COLLECTION, uuid]
)[0];
}
function saveProjectData(runtime, projectId, updater) {
const row = getProjectRow(runtime, projectId);
if (!row) {
return undefined;
}
const current = parseMaybeJson(row.data_json, {});
const next = updater(current, row) || current;
const title = stringValue(next.name) || stringValue(next.title) || row.title || "Untitled design";
runtime.store.database.run(
"UPDATE claude_design_items SET updated_at = ?, title = ?, data_json = ? WHERE collection = ? AND uuid = ?",
[new Date().toISOString(), title, JSON.stringify(next), PROJECT_COLLECTION, row.uuid]
);
runtime.store.persist();
return getProjectRow(runtime, row.uuid);
}
function updateOmeletteProject(runtime, requestBody) {
const projectId = decodeProtoStringField(requestBody, 1);
const name = decodeProtoStringField(requestBody, 2);
if (!projectId || !name) {
return;
}
saveProjectData(runtime, projectId, (data) => ({
...data,
name,
title: name
}));
}
function deleteOmeletteProject(runtime, requestBody) {
const projectId = decodeProtoStringField(requestBody, 1);
if (!projectId) {
return;
}
runtime.store.database.run("DELETE FROM claude_design_items WHERE collection = ? AND uuid = ?", [PROJECT_COLLECTION, projectId]);
runtime.store.database.run("DELETE FROM claude_design_files WHERE project_id = ?", [projectId]);
runtime.store.persist();
}
function normalizeProjectStoreData(runtime, row, value) {
const record = isRecord(value) ? value : {};
const project = omeletteProjectFromRow(row, runtime.me);
const created = stringValue(record.created) || project.createdAt || row.created_at || new Date().toISOString();
const lastOpened = stringValue(record.lastOpened) || project.updatedAt || row.updated_at || created;
const sourceChats = isRecord(record.chats) ? record.chats : {};
const chats = {};
for (const [chatId, chat] of Object.entries(sourceChats)) {
if (isRecord(chat)) {
chats[chatId] = normalizeProjectChat(chat, chatId, created, lastOpened);
}
}
const viewState = isRecord(record.viewState) ? record.viewState : {};
let activeChatId = stringValue(viewState.activeChatId);
const fallbackChatId = activeChatId || Object.keys(chats)[0] || defaultProjectChatId(project.projectId || row.uuid);
const mergedStoredChats = mergeStoredMessagesIntoProjectChats(chats, row, created, lastOpened, fallbackChatId);
if (!activeChatId || !isRecord(chats[activeChatId]) || (isEmptyProjectChat(chats[activeChatId]) && mergedStoredChats.lastChatId)) {
activeChatId = mergedStoredChats.lastChatId || Object.keys(chats)[0] || defaultProjectChatId(project.projectId || row.uuid);
}
if (!isRecord(chats[activeChatId])) {
chats[activeChatId] = normalizeProjectChat({}, activeChatId, created, lastOpened);
}
return {
...record,
activeSkills: Array.isArray(record.activeSkills) ? record.activeSkills : [],
chats,
closedChats: Array.isArray(record.closedChats) ? record.closedChats : [],
created,
lastOpened,
name: stringValue(record.name) || project.name,
viewState: {
...viewState,
activeChatId,
activeFileTab: numberValue(viewState.activeFileTab) ?? -1,
activeProjectTab: numberValue(viewState.activeProjectTab) ?? 0,
folderHistory: Array.isArray(viewState.folderHistory) ? viewState.folderHistory : [""],
folderHistoryIndex: numberValue(viewState.folderHistoryIndex) ?? 0,
folderPath: typeof viewState.folderPath === "string" ? viewState.folderPath : "",
openFiles: Array.isArray(viewState.openFiles) ? viewState.openFiles : []
}
};
}
function mergeStoredMessagesIntoProjectChats(chats, row, created, lastOpened, fallbackChatId) {
const storedMessages = sanitizeStoredMessages(parseMaybeJson(row?.messages_json, []));
const grouped = new Map();
const orderedChatIds = [];
let lastChatId;
for (const message of storedMessages) {
if (!isRecord(message)) {
continue;
}
const chatId = stringValue(message.chat_id) || stringValue(message.chatId) || fallbackChatId;
if (!chatId) {
continue;
}
if (!grouped.has(chatId)) {
grouped.set(chatId, []);
orderedChatIds.push(chatId);
}
grouped.get(chatId).push(message);
lastChatId = chatId;
}
for (const chatId of orderedChatIds) {
const chat = normalizeProjectChat(chats[chatId] || {}, chatId, created, lastOpened);
const mergedMessages = Array.isArray(chat.messages) ? [...chat.messages] : [];
const seenKeys = new Set();
const existingLooseKeys = new Set();
for (const message of mergedMessages) {
for (const key of projectChatMessageKeys(message)) {
seenKeys.add(key);
}
for (const key of projectChatMessageLooseKeys(message)) {
existingLooseKeys.add(key);
}
}
for (const storedMessage of grouped.get(chatId) || []) {
const projectMessage = projectChatMessageFromStoredMessage(storedMessage, lastOpened);
const keys = projectChatMessageKeys(projectMessage);
const looseKeys = projectChatMessageLooseKeys(projectMessage);
if (keys.some((key) => seenKeys.has(key)) || looseKeys.some((key) => existingLooseKeys.has(key))) {
continue;
}
mergedMessages.push(projectMessage);
for (const key of keys) {
seenKeys.add(key);
}
}
chats[chatId] = {
...chat,
lastOpened: latestProjectChatTimestamp(mergedMessages) || chat.lastOpened,
messages: sortProjectChatMessages(mergedMessages)
};
}
return {
lastChatId
};
}
function projectChatMessageFromStoredMessage(message, fallbackTimestamp) {
const projectMessage = {
...message
};
delete projectMessage.chat_id;
delete projectMessage.chatId;
const timestamp = stringValue(message.timestamp) || stringValue(message.created_at) || stringValue(message.createdAt) || fallbackTimestamp;
if (timestamp) {
projectMessage.timestamp = timestamp;
}
const role = stringValue(message.role);
if (role) {
projectMessage.role = role;
}
if (Array.isArray(message.contentBlocks) && !Array.isArray(projectMessage.content_blocks)) {
projectMessage.content_blocks = message.contentBlocks;
}
if (Array.isArray(message.content_blocks) && !Array.isArray(projectMessage.contentBlocks)) {
projectMessage.contentBlocks = message.content_blocks;
}
return projectMessage;
}
function projectChatMessageKeys(message) {
if (!isRecord(message)) {
return [`raw:${JSON.stringify(message)}`];
}
const keys = [];
const id = stringValue(message.id);
if (id) {
keys.push(`id:${id}`);
}
const uuid = stringValue(message.uuid);
if (uuid) {
keys.push(`uuid:${uuid}`);
}
const role = stringValue(message.role) || "";
const timestamp = stringValue(message.timestamp) || stringValue(message.created_at) || stringValue(message.createdAt) || "";
const content = projectChatMessageContentKey(message);
if (role || timestamp || content) {
keys.push(`content:${role}\n${timestamp}\n${content}`);
}
return keys;
}
function projectChatMessageLooseKeys(message) {
if (!isRecord(message)) {
return [];
}
const role = stringValue(message.role) || "";
const content = projectChatMessageContentKey(message);
return role || content ? [`content-loose:${role}\n${content}`] : [];
}
function projectChatMessageContentKey(message) {
if (!isRecord(message)) {
return JSON.stringify(message);
}
const blocks = Array.isArray(message.contentBlocks)
? message.contentBlocks
: Array.isArray(message.content_blocks)
? message.content_blocks
: undefined;
return JSON.stringify({
blocks,
content: message.content,
role: message.role
});
}
function sortProjectChatMessages(messages) {
return messages
.map((message, index) => ({
index,
message,
timestamp: projectChatMessageTimestamp(message)
}))
.sort((left, right) => {
if (left.timestamp !== undefined && right.timestamp !== undefined && left.timestamp !== right.timestamp) {
return left.timestamp - right.timestamp;
}
if (left.timestamp !== undefined && right.timestamp === undefined) {
return -1;
}
if (left.timestamp === undefined && right.timestamp !== undefined) {
return 1;
}
return left.index - right.index;
})
.map((entry) => entry.message);
}
function latestProjectChatTimestamp(messages) {
let latest;
for (const message of messages) {
const timestamp = projectChatMessageTimestamp(message);
if (timestamp === undefined || (latest !== undefined && timestamp <= latest.timestamp)) {
continue;
}
latest = {
iso: stringValue(message.timestamp) || stringValue(message.created_at) || stringValue(message.createdAt),
timestamp
};
}
return latest?.iso;
}
function projectChatMessageTimestamp(message) {
if (!isRecord(message)) {
return undefined;
}
const value = stringValue(message.timestamp) || stringValue(message.created_at) || stringValue(message.createdAt);
if (!value) {
return undefined;
}
const timestamp = Date.parse(value);
return Number.isFinite(timestamp) ? timestamp : undefined;
}
function isEmptyProjectChat(chat) {
return !isRecord(chat) || !Array.isArray(chat.messages) || chat.messages.length === 0;
}
function normalizeProjectChat(value, chatId, created, lastOpened) {
const chat = isRecord(value) ? value : {};
const name = stringValue(chat.name) || stringValue(chat.title) || "Chat";
const composer = isRecord(chat.composer) ? chat.composer : {};
return {
...chat,
composer: {
...composer,
activeSkills: Array.isArray(composer.activeSkills) ? composer.activeSkills : [],
attachments: Array.isArray(composer.attachments) ? composer.attachments : [],
text: typeof composer.text === "string" ? composer.text : ""
},
created: stringValue(chat.created) || created,
id: stringValue(chat.id) || chatId,
lastOpened: stringValue(chat.lastOpened) || lastOpened,
messages: sanitizeStoredMessages(Array.isArray(chat.messages) ? chat.messages : []),
name,
title: stringValue(chat.title) || name,
todos: Array.isArray(chat.todos) ? chat.todos : []
};
}
function defaultProjectChatId(projectId) {
const normalized = String(projectId || "").replace(/[^a-zA-Z0-9_-]/g, "").slice(0, 36);
return normalized ? `chat-${normalized}` : "chat-default";
}
function getProjectDataBytes(runtime, projectId) {
let row = getProjectRow(runtime, projectId);
if (!row && projectId) {
getOmeletteProject(runtime, projectId);
row = getProjectRow(runtime, projectId);
}
if (!row) {
return Buffer.alloc(0);
}
const record = parseMaybeJson(row.data_json, {});
const base64 = stringValue(record.project_data_base64);
const decoded = base64 ? parseMaybeJson(Buffer.from(base64, "base64").toString("utf8"), {}) : record;
const normalized = normalizeProjectStoreData(runtime, row, decoded);
const bytes = Buffer.from(JSON.stringify(normalized), "utf8");
const normalizedBase64 = bytes.toString("base64");
if (base64 !== normalizedBase64) {
runtime.store.database.run(
"UPDATE claude_design_items SET data_json = ? WHERE collection = ? AND uuid = ?",
[JSON.stringify({ ...record, project_data_base64: normalizedBase64 }), PROJECT_COLLECTION, row.uuid]
);
runtime.store.persist();
}
return bytes;
}
function updateProjectDataBytes(runtime, requestBody) {
const projectId = decodeProtoStringField(requestBody, 1);
const data = decodeProtoBytesField(requestBody, 2) || Buffer.alloc(0);
if (projectId) {
saveProjectData(runtime, projectId, (record, row) => {
const decoded = data.length ? parseMaybeJson(data.toString("utf8"), undefined) : undefined;
if (!isRecord(decoded)) {
return {
...record,
project_data_base64: data.toString("base64")
};
}
const normalized = normalizeProjectStoreData(runtime, row, decoded);
return {
...record,
project_data_base64: Buffer.from(JSON.stringify(normalized), "utf8").toString("base64")
};
});
}
return protoInt32(1, 5);
}
function encodeGetChatMessagesResponse(runtime, requestBody) {
const projectId = decodeProtoStringField(requestBody, 1);
const chatId = decodeProtoStringField(requestBody, 2);
const row = getProjectRow(runtime, projectId);
const messages = parseMaybeJson(row?.messages_json, []);
const sanitized = sanitizeStoredMessages(messages);
const filtered = chatId ? sanitized.filter((message) => !message.chat_id || message.chat_id === chatId) : sanitized;
return protoBytes(1, Buffer.from(JSON.stringify(filtered), "utf8"), true);
}
function updateOmeletteProjectSharing(runtime, requestBody) {
const projectId = decodeProtoStringField(requestBody, 1);
const sharing = {
view_mode: decodeProtoStringField(requestBody, 2) || "private",
team_can_edit: decodeProtoBoolField(requestBody, 3),
team_can_comment: decodeProtoBoolField(requestBody, 4)
};
if (projectId) {
saveProjectData(runtime, projectId, (data) => ({
...data,
sharing
}));
}
return protoMessage(1, encodeSharing(sharing));
}
function duplicateOmeletteProject(runtime, requestBody, options) {
const projectId = decodeProtoStringField(requestBody, 1);
const row = getProjectRow(runtime, projectId);
const source = row ? omeletteProjectFromRow(row, runtime.me) : getOmeletteProject(runtime, projectId);
const now = new Date().toISOString();
const uuid = randomUuid();
const sourceData = parseMaybeJson(row?.data_json, {});
const type = normalizeProjectTypeNumber(options.type || source.type);
const name = type === PROJECT_TYPE_TEMPLATE && !source.name.toLowerCase().includes("template")
? `${source.name} Template`
: `${source.name} Copy`;
const data = {
...sourceData,
name,
project_id: uuid,
project_type: type,
source_project_uuid: source.projectId,
title: name,
type,
uuid
};
if (!options.includeChats) {
delete data.project_data_base64;
}
runtime.store.database.run(
"INSERT OR REPLACE INTO claude_design_items (collection, uuid, created_at, updated_at, title, model, data_json, messages_json) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
[PROJECT_COLLECTION, uuid, now, now, name, runtime.me.defaultModelId, JSON.stringify(data), options.includeChats ? row?.messages_json || "[]" : "[]"]
);
for (const file of listProjectFileRows(runtime, source.projectId, "")) {
runtime.store.database.run(
"INSERT OR REPLACE INTO claude_design_files (project_id, path, created_at, updated_at, content_type, body_base64, version) VALUES (?, ?, ?, ?, ?, ?, ?)",
[uuid, file.path, now, now, file.content_type, file.body_base64, 1]
);
}
runtime.store.persist();
return protoString(1, uuid);
}
function updateProjectType(runtime, requestBody) {
const projectId = decodeProtoStringField(requestBody, 1);
const type = normalizeProjectTypeNumber(decodeProtoEnumField(requestBody, 2));
if (projectId) {
saveProjectData(runtime, projectId, (data) => ({
...data,
project_type: type,
type
}));
}
return protoEnum(1, type);
}
function setProjectPublished(runtime, requestBody) {
const projectId = decodeProtoStringField(requestBody, 1);
const published = decodeProtoBoolField(requestBody, 2);
const publishedAt = published ? new Date().toISOString() : "";
if (projectId) {
saveProjectData(runtime, projectId, (data) => ({
...data,
published_at: publishedAt
}));
}
return published ? protoTimestamp(1, publishedAt) : Buffer.alloc(0);
}
function updateProjectInfo(runtime, requestBody) {
const projectId = decodeProtoStringField(requestBody, 1);
if (!projectId) {
return;
}
const templateTitle = decodeProtoStringField(requestBody, 2);
const description = decodeProtoStringField(requestBody, 3);
const introText = decodeProtoStringField(requestBody, 4);
saveProjectData(runtime, projectId, (data) => ({
...data,
description: description ?? data.description ?? "",
intro_text: introText ?? data.intro_text ?? "",
template_title: templateTitle ?? data.template_title ?? ""
}));
}
function updateProjectDesignSystems(runtime, requestBody) {
const projectId = decodeProtoStringField(requestBody, 1);
const bindings = decodeDesignSystemBindings(decodeProtoMessageFields(requestBody, 2));
if (projectId) {
saveProjectData(runtime, projectId, (data) => ({
...data,
design_systems: bindings
}));
}
return Buffer.concat(bindings.map((binding) => protoMessage(1, encodeDesignSystemBinding(binding))));
}
function patchDesignSystemBinding(runtime, requestBody) {
const projectId = decodeProtoStringField(requestBody, 1);
const dsProjectId = decodeProtoStringField(requestBody, 2);
const syncedAtVersion = decodeProtoIntField(requestBody, 3);
let bindings = [];
if (projectId && dsProjectId) {
const row = getProjectRow(runtime, projectId);
const data = parseMaybeJson(row?.data_json, {});
bindings = Array.isArray(data.design_systems) ? data.design_systems : [];
const existing = bindings.find((binding) => binding.ds_project_id === dsProjectId);
if (existing) {
existing.synced_at_version = syncedAtVersion;
} else {
bindings.push({ ds_project_id: dsProjectId, synced_at_version: syncedAtVersion });
}
saveProjectData(runtime, projectId, (record) => ({
...record,
design_systems: bindings
}));
}
return Buffer.concat(bindings.map((binding) => protoMessage(1, encodeDesignSystemBinding(binding))));
}
function updateProjectFavorite(runtime, requestBody) {
const projectId = decodeProtoStringField(requestBody, 1);
const favorite = decodeProtoBoolField(requestBody, 2);
if (projectId) {
saveProjectData(runtime, projectId, (data) => ({
...data,
is_favorite: favorite
}));
}
}
function decodeDesignSystemBindings(messages) {
return messages
.map((message) => ({
ds_project_id: decodeProtoStringField(message, 1) || "",
has_v2_layout: decodeProtoBoolField(message, 3),
synced_at_version: decodeProtoIntField(message, 2)
}))
.filter((binding) => binding.ds_project_id);
}
function encodeDesignSystemBinding(binding) {
return Buffer.concat([
protoString(1, binding.ds_project_id || binding.dsProjectId),
protoInt64(2, binding.synced_at_version ?? binding.syncedAtVersion ?? 0),
protoBool(3, binding.has_v2_layout === true || binding.hasV2Layout === true)
]);
}
function listProjectFileRows(runtime, projectId, prefix) {
const address = normalizeProjectFileAddress(runtime, projectId, prefix || "");
const normalizedProjectId = address.projectId;
if (!normalizedProjectId) {
return [];
}
const rows = queryRows(
runtime.store.database,
"SELECT project_id, path, created_at, updated_at, content_type, body_base64, version FROM claude_design_files WHERE project_id = ? ORDER BY path ASC",
[normalizedProjectId]
);
const normalizedPrefix = address.path;
if (!normalizedPrefix) {
return rows;
}
const prefixWithSlash = `${normalizedPrefix.replace(/\/+$/, "")}/`;
return rows.filter((row) => row.path === normalizedPrefix || row.path.startsWith(prefixWithSlash));
}
function encodeListFilesResponse(runtime, requestBody) {
const projectId = decodeProtoStringField(requestBody, 1);
const directory = sanitizeProjectFilePath(decodeProtoStringField(requestBody, 2) || "");
const depth = decodeProtoIntField(requestBody, 3) || 1;
const offset = decodeProtoIntField(requestBody, 4) || 0;
const filter = decodeProtoStringField(requestBody, 5);
const allEntries = listProjectDirectoryEntries(runtime, projectId, directory, depth, filter);
const limit = 200;
const visible = allEntries.slice(offset, offset + limit);
return Buffer.concat([
...visible.map((entry) => protoMessage(1, encodeFileEntry(entry))),
protoInt32(2, allEntries.length),
protoInt32(3, offset),
protoInt32(4, limit),
protoBool(5, offset + limit < allEntries.length)
]);
}
function listProjectDirectoryEntries(runtime, projectId, directory, depth, filter) {
const address = normalizeProjectFileAddress(runtime, projectId, directory || "");
const normalizedDirectory = address.path;
const rows = listProjectFileRows(runtime, address.projectId, normalizedDirectory);
const prefix = normalizedDirectory ? `${normalizedDirectory.replace(/\/+$/, "")}/` : "";
const directories = new Map();
const files = [];
const normalizedFilter = stringValue(filter)?.toLowerCase();
for (const row of rows) {
if (normalizedDirectory && row.path === normalizedDirectory) {
continue;
}
const relative = prefix ? row.path.slice(prefix.length) : row.path;
if (!relative || relative.startsWith("../")) {
continue;
}
const slashIndex = relative.indexOf("/");
if (slashIndex >= 0 && depth <= 1) {
const name = relative.slice(0, slashIndex);
const path = prefix ? `${prefix}${name}` : name;
const existing = directories.get(path);
if (!existing || String(row.updated_at) > String(existing.updatedAt)) {
directories.set(path, {
contentType: "inode/directory",
name,
path,
size: 0,
type: "directory",
updatedAt: row.updated_at,
version: 0
});
}
continue;
}
if (normalizedFilter && !row.path.toLowerCase().includes(normalizedFilter)) {
continue;
}
files.push(fileEntryFromRow(row));
}
return [...directories.values(), ...files].sort((left, right) => {
if (left.type !== right.type) {
return left.type === "directory" ? -1 : 1;
}
return left.path.localeCompare(right.path);
});
}
function fileEntryFromRow(row) {
return {
contentType: row.content_type || guessContentType(row.path),
name: row.path.split("/").pop() || row.path,
path: row.path,
size: Buffer.from(row.body_base64 || "", "base64").length,
type: "file",
updatedAt: row.updated_at,
version: Number(row.version) || 1
};
}
function encodeFileEntry(entry) {
return Buffer.concat([
protoString(1, entry.name),
protoString(2, entry.path),
protoString(3, entry.type),
protoInt64(4, entry.size || 0),
protoString(5, entry.contentType),
protoTimestamp(6, entry.updatedAt),
protoInt64(7, entry.version || 0)
]);
}
function encodeGetFileResponse(runtime, requestBody) {
const projectId = decodeProtoStringField(requestBody, 1);
const filePath = sanitizeProjectFilePath(decodeProtoStringField(requestBody, 2) || "");
const row = getProjectFileRow(runtime, projectId, filePath);
if (!row) {
return Buffer.alloc(0);
}
const body = Buffer.from(row.body_base64 || "", "base64");
return Buffer.concat([
protoBytes(1, body, true),
protoString(2, row.content_type || guessContentType(filePath)),
protoBool(3, false),
protoInt64(4, Number(row.version) || 1)
]);
}
function writeProjectFiles(runtime, requestBody) {
const projectId = decodeProtoStringField(requestBody, 1);
const fileMessages = decodeProtoMessageFields(requestBody, 2);
const deletePaths = decodeProtoStringFields(requestBody, 6);
const mutationMessages = decodeProtoMessageFields(requestBody, 7);
const written = [];
for (const fileMessage of fileMessages) {
const file = decodeFileToWrite(fileMessage);
if (!file.path) {
continue;
}
const row = upsertProjectFile(runtime, projectId, file.path, file.body, file.mimeType);
written.push(fileEntryFromRow(row));
}
for (const deletePath of deletePaths) {
deleteProjectFileByPath(runtime, projectId, deletePath);
}
for (const mutation of mutationMessages) {
const result = applyFileMutation(runtime, projectId, mutation);
if (result?.entry) {
written.push(result.entry);
}
}
runtime.store.persist();
return Buffer.concat(written.map((file) => protoMessage(1, encodeWrittenFile(file))));
}
function decodeFileToWrite(message) {
const path = sanitizeProjectFilePath(decodeProtoStringField(message, 1) || "");
const data = decodeProtoStringField(message, 2) || "";
const mimeType = decodeProtoStringField(message, 3) || guessContentType(path);
const encoding = decodeProtoStringField(message, 4) || "";
const body = encoding.toLowerCase() === "base64" ? Buffer.from(data, "base64") : Buffer.from(data, "utf8");
return {
body,
encoding,
mimeType,
path
};
}
function applyFileMutation(runtime, projectId, mutation) {
const path = sanitizeProjectFilePath(decodeProtoStringField(mutation, 1) || "");
if (!path) {
return undefined;
}
const write = decodeProtoMessageFields(mutation, 4)[0];
if (write) {
const data = decodeProtoStringField(write, 1) || "";
const mimeType = decodeProtoStringField(write, 2) || guessContentType(path);
const encoding = decodeProtoStringField(write, 3) || "";
const body = encoding.toLowerCase() === "base64" ? Buffer.from(data, "base64") : Buffer.from(data, "utf8");
return { entry: fileEntryFromRow(upsertProjectFile(runtime, projectId, path, body, mimeType)) };
}
if (decodeProtoMessageFields(mutation, 6)[0]) {
deleteProjectFileByPath(runtime, projectId, path);
return { deleted: true };
}
const move = decodeProtoMessageFields(mutation, 8)[0];
if (move) {
const fromPath = sanitizeProjectFilePath(decodeProtoStringField(move, 1) || "");
const source = getProjectFileRow(runtime, projectId, fromPath);
if (source) {
const row = upsertProjectFile(runtime, projectId, path, Buffer.from(source.body_base64 || "", "base64"), source.content_type);
deleteProjectFileByPath(runtime, projectId, fromPath);
return { entry: fileEntryFromRow(row) };
}
}
const edit = decodeProtoMessageFields(mutation, 5)[0];
if (edit) {
return { entry: editProjectFileByMessages(runtime, projectId, path, decodeProtoMessageFields(edit, 1)) };
}
return undefined;
}
function encodeWrittenFile(entry) {
return Buffer.concat([
protoString(1, entry.name),
protoString(2, entry.path),
protoString(3, entry.contentType),
protoInt64(4, entry.version || 1)
]);
}
function deleteProjectFile(runtime, requestBody) {
const projectId = decodeProtoStringField(requestBody, 1);
const filePath = decodeProtoStringField(requestBody, 2);
const deleted = deleteProjectFileByPath(runtime, projectId, filePath);
runtime.store.persist();
return protoInt32(1, deleted);
}
function deleteProjectFiles(runtime, requestBody) {
const projectId = decodeProtoStringField(requestBody, 1);
const paths = decodeProtoStringFields(requestBody, 2);
let deleted = 0;
for (const filePath of paths) {
deleted += deleteProjectFileByPath(runtime, projectId, filePath);
}
runtime.store.persist();
return protoInt32(1, deleted);
}
function copyProjectFile(runtime, requestBody) {
const projectId = decodeProtoStringField(requestBody, 1);
const src = sanitizeProjectFilePath(decodeProtoStringField(requestBody, 2) || "");
const dest = sanitizeProjectFilePath(decodeProtoStringField(requestBody, 3) || "");
const move = decodeProtoBoolField(requestBody, 4);
const srcProjectId = decodeProtoStringField(requestBody, 5) || projectId;
const sourceAddress = normalizeProjectFileAddress(runtime, srcProjectId, src);
const targetAddress = normalizeProjectFileAddress(runtime, projectId, dest);
const normalizedSrcProjectId = sourceAddress.projectId;
const normalizedTargetProjectId = targetAddress.projectId || normalizedSrcProjectId || projectId;
const normalizedSrc = sourceAddress.path;
const normalizedDest = targetAddress.path;
const copied = [];
for (const row of listProjectFileRows(runtime, normalizedSrcProjectId, normalizedSrc)) {
const suffix = row.path === normalizedSrc ? "" : row.path.slice(normalizedSrc.length).replace(/^\/+/, "");
const target = suffix ? `${normalizedDest.replace(/\/+$/, "")}/${suffix}` : normalizedDest;
const written = upsertProjectFile(runtime, normalizedTargetProjectId, target, Buffer.from(row.body_base64 || "", "base64"), row.content_type);
copied.push(written.path);
if (move && normalizedSrcProjectId === normalizedTargetProjectId) {
deleteProjectFileByPath(runtime, normalizedSrcProjectId, row.path);
}
}
runtime.store.persist();
return Buffer.concat(copied.map((filePath) => protoString(1, filePath)));
}
function editProjectFile(runtime, requestBody) {
const projectId = decodeProtoStringField(requestBody, 1);
const filePath = sanitizeProjectFilePath(decodeProtoStringField(requestBody, 2) || "");
const replacements = decodeProtoMessageFields(requestBody, 3);
const entry = editProjectFileByMessages(runtime, projectId, filePath, replacements);
return Buffer.concat([protoString(1, entry.path), protoInt32(2, replacements.length), protoInt64(3, entry.version)]);
}
function editProjectFileByMessages(runtime, projectId, filePath, replacements) {
const row = getProjectFileRow(runtime, projectId, filePath);
let text = row ? Buffer.from(row.body_base64 || "", "base64").toString("utf8") : "";
let applied = 0;
for (const replacement of replacements) {
const oldString = decodeProtoStringField(replacement, 1) || "";
const newString = decodeProtoStringField(replacement, 2) || "";
if (oldString && text.includes(oldString)) {
text = text.replace(oldString, newString);
applied += 1;
}
}
const written = upsertProjectFile(runtime, projectId, filePath, Buffer.from(text, "utf8"), row?.content_type || guessContentType(filePath));
runtime.store.persist();
return {
...fileEntryFromRow(written),
editsApplied: applied
};
}
function grepProjectFiles(runtime, requestBody) {
const projectId = decodeProtoStringField(requestBody, 1);
const pattern = decodeProtoStringField(requestBody, 2);
const directory = decodeProtoStringField(requestBody, 3) || "";
if (!pattern) {
return Buffer.alloc(0);
}
let regex;
try {
regex = new RegExp(pattern, "i");
} catch {
regex = new RegExp(escapeRegExp(pattern), "i");
}
const matches = [];
for (const row of listProjectFileRows(runtime, projectId, directory)) {
const text = Buffer.from(row.body_base64 || "", "base64").toString("utf8");
const lines = text.split(/\r?\n/);
for (let index = 0; index < lines.length; index += 1) {
if (regex.test(lines[index])) {
matches.push(protoMessage(1, encodeGrepMatch(row.path, index + 1, lines[index])));
}
}
}
return Buffer.concat(matches);
}
function encodeGrepMatch(path, line, text) {
return Buffer.concat([protoString(1, path), protoInt32(2, line), protoString(3, text)]);
}
function createFileStream(runtime, requestBody) {
const projectId = decodeProtoStringField(requestBody, 1);
const streamId = randomUuid();
const paths = decodeProtoStringFields(requestBody, 2);
runtime.store.database.run(
"INSERT OR REPLACE INTO claude_design_items (collection, uuid, created_at, updated_at, title, model, data_json, messages_json) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
["file_streams", streamId, new Date().toISOString(), new Date().toISOString(), streamId, runtime.me.defaultModelId, JSON.stringify({ projectId, paths }), "[]"]
);
runtime.store.persist();
return protoString(1, streamId);
}
function writeFileStream(runtime, requestBody) {
const streamId = decodeProtoStringField(requestBody, 1);
const stream = getItem(runtime.store, "file_streams", runtime.me.organizationUuid, streamId, runtime.me);
const projectId = stream.projectId;
for (const op of decodeProtoMessageFields(requestBody, 2)) {
const path = sanitizeProjectFilePath(decodeProtoStringField(op, 1) || "");
const operation = decodeProtoStringField(op, 2) || "";
const delta = decodeProtoStringField(op, 3) || "";
const reset = decodeProtoBoolField(op, 4);
if (!path || operation === "delete") {
if (operation === "delete") {
deleteProjectFileByPath(runtime, projectId, path);
}
continue;
}
const existing = reset ? "" : getProjectFileText(runtime, projectId, path);
upsertProjectFile(runtime, projectId, path, Buffer.from(`${existing}${delta}`, "utf8"), guessContentType(path));
}
runtime.store.persist();
return Buffer.alloc(0);
}
function getProjectFileRow(runtime, projectId, filePath) {
const address = normalizeProjectFileAddress(runtime, projectId, filePath || "");
const normalizedProjectId = address.projectId;
const normalizedPath = address.path;
if (!normalizedProjectId || !normalizedPath) {
return undefined;
}
return queryRows(
runtime.store.database,
"SELECT project_id, path, created_at, updated_at, content_type, body_base64, version FROM claude_design_files WHERE project_id = ? AND path = ? LIMIT 1",
[normalizedProjectId, normalizedPath]
)[0];
}
function getProjectFileText(runtime, projectId, filePath) {
const row = getProjectFileRow(runtime, projectId, filePath);
return row ? Buffer.from(row.body_base64 || "", "base64").toString("utf8") : "";
}
function upsertProjectFile(runtime, projectId, filePath, body, contentType) {
const address = normalizeProjectFileAddress(runtime, projectId, filePath || "");
const normalizedProjectId = address.projectId;
const normalizedPath = address.path;
if (!normalizedProjectId || !normalizedPath) {
throw new Error("project_id and path are required to write a Claude Design file.");
}
const now = new Date().toISOString();
const existing = getProjectFileRow(runtime, normalizedProjectId, normalizedPath);
const version = existing ? Number(existing.version || 0) + 1 : 1;
runtime.store.database.run(
"INSERT OR REPLACE INTO claude_design_files (project_id, path, created_at, updated_at, content_type, body_base64, version) VALUES (?, ?, ?, ?, ?, ?, ?)",
[
normalizedProjectId,
normalizedPath,
existing?.created_at || now,
now,
contentType || guessContentType(normalizedPath),
Buffer.isBuffer(body) ? body.toString("base64") : Buffer.from(String(body || ""), "utf8").toString("base64"),
version
]
);
return getProjectFileRow(runtime, normalizedProjectId, normalizedPath);
}
function deleteProjectFileByPath(runtime, projectId, filePath) {
const address = normalizeProjectFileAddress(runtime, projectId, filePath || "");
const normalizedProjectId = address.projectId;
const normalizedPath = address.path;
if (!normalizedProjectId || !normalizedPath) {
return 0;
}
const existing = getProjectFileRow(runtime, normalizedProjectId, normalizedPath);
runtime.store.database.run("DELETE FROM claude_design_files WHERE project_id = ? AND path = ?", [normalizedProjectId, normalizedPath]);
return existing ? 1 : 0;
}
function normalizeProjectFileAddress(runtime, projectId, filePath) {
let normalizedProjectId = stringValue(projectId);
let normalizedPath = sanitizeProjectFilePath(filePath || "");
if (normalizedProjectId) {
if (normalizedPath === normalizedProjectId) {
normalizedPath = "";
} else if (normalizedPath.startsWith(`${normalizedProjectId}/`)) {
normalizedPath = sanitizeProjectFilePath(normalizedPath.slice(normalizedProjectId.length + 1));
}
}
if (!normalizedProjectId && normalizedPath) {
const split = splitProjectPrefixedFilePath(runtime, normalizedPath);
if (split) {
normalizedProjectId = split.projectId;
normalizedPath = split.path;
}
}
return {
path: normalizedPath,
projectId: normalizedProjectId
};
}
function splitProjectPrefixedFilePath(runtime, filePath) {
const slashIndex = filePath.indexOf("/");
if (slashIndex < 0 && getProjectRow(runtime, filePath)) {
return { path: "", projectId: filePath };
}
if (slashIndex <= 0) {
return undefined;
}
const projectId = filePath.slice(0, slashIndex);
const path = sanitizeProjectFilePath(filePath.slice(slashIndex + 1));
if (!projectId || !path || !getProjectRow(runtime, projectId)) {
return undefined;
}
return { path, projectId };
}
function sanitizeProjectFilePath(value) {
const normalized = String(value || "").replace(/\\/g, "/").replace(/^\/+/, "");
const parts = [];
for (const part of normalized.split("/")) {
if (!part || part === ".") {
continue;
}
if (part === "..") {
continue;
}
parts.push(part);
}
return parts.join("/");
}
function listProjectAssets(runtime, requestBody) {
const projectId = decodeProtoStringField(requestBody, 1);
const assets = listItems(runtime.store, "assets", runtime.me.organizationUuid).filter((asset) => asset.project_id === projectId);
return Buffer.concat(assets.map((asset) => protoMessage(1, encodeProjectAsset(asset))));
}
function recordProjectAsset(runtime, requestBody) {
const projectId = decodeProtoStringField(requestBody, 1);
const name = decodeProtoStringField(requestBody, 2);
const filePath = decodeProtoStringField(requestBody, 3);
if (!projectId || !filePath) {
return;
}
const uuid = assetUuid(projectId, name, filePath);
const body = {
chat_id: decodeProtoStringField(requestBody, 6) || "",
name: name || filePath,
path: filePath,
project_id: projectId,
section: decodeProtoStringField(requestBody, 8) || "",
status: decodeProtoEnumField(requestBody, 7) || 0,
subtitle: decodeProtoStringField(requestBody, 4) || "",
uuid
};
upsertGenericRecord(runtime.store, "assets", uuid, body.name, runtime.me.defaultModelId, body);
}
function setProjectAssetStatus(runtime, requestBody) {
const projectId = decodeProtoStringField(requestBody, 1);
const name = decodeProtoStringField(requestBody, 2);
const filePath = decodeProtoStringField(requestBody, 3);
const status = decodeProtoEnumField(requestBody, 4) || 0;
const uuid = assetUuid(projectId, name, filePath);
const item = getItem(runtime.store, "assets", runtime.me.organizationUuid, uuid, runtime.me);
upsertGenericRecord(runtime.store, "assets", uuid, item.name || name || filePath || "asset", runtime.me.defaultModelId, {
...item,
project_id: projectId,
status,
status_changed_at: new Date().toISOString()
});
}
function deleteProjectAsset(runtime, requestBody) {
const projectId = decodeProtoStringField(requestBody, 1);
const name = decodeProtoStringField(requestBody, 2);
const filePath = decodeProtoStringField(requestBody, 3);
deleteItem(runtime.store, "assets", assetUuid(projectId, name, filePath));
}
function encodeProjectAsset(asset) {
return Buffer.concat([
protoString(1, asset.name),
protoString(2, asset.path),
protoEnum(3, asset.status || 0),
protoString(4, asset.subtitle),
protoBool(6, asset.pinned === true),
protoString(7, asset.chat_id),
protoTimestamp(8, asset.created_at || asset.createdAt),
protoTimestamp(9, asset.status_changed_at),
protoString(11, asset.section)
]);
}
function assetUuid(projectId, name, filePath) {
return `${projectId || ""}:${name || ""}:${filePath || ""}`;
}
function bundleProject(runtime, requestBody) {
const projectId = decodeProtoStringField(requestBody, 1) || firstUuidLikeString(requestBody) || "";
if (projectId) {
const project = getOmeletteProject(runtime, projectId);
const files = listProjectFileRows(runtime, projectId, "").map((row) => ({
contentType: row.content_type,
path: row.path,
text: Buffer.from(row.body_base64 || "", "base64").toString("utf8"),
version: row.version
}));
upsertGenericRecord(runtime.store, "bundles", projectId, project.name || projectId, runtime.me.defaultModelId, {
files,
project,
project_id: projectId
});
}
return Buffer.alloc(0);
}
function uploadFile(runtime, requestBody) {
const projectId = decodeProtoStringField(requestBody, 1) || firstUuidLikeString(requestBody) || "";
const directFilePath = decodeProtoStringField(requestBody, 2);
const fileMessages = decodeProtoMessageFields(requestBody, 2);
if (!directFilePath && fileMessages.length > 0) {
return writeProjectFiles(runtime, requestBody);
}
const strings = decodeAllProtoStrings(requestBody);
const filePath = sanitizeProjectFilePath(
directFilePath ||
strings.find((item) => item.value && item.value !== projectId && !item.value.includes("{"))?.value ||
`uploads/upload-${Date.now()}.txt`
);
const mimeType = decodeProtoStringField(requestBody, 4) || guessContentType(filePath);
const data = decodeProtoBytesField(requestBody, 3) || Buffer.from(decodeProtoStringField(requestBody, 3) || "", "utf8");
const row = upsertProjectFile(runtime, projectId, filePath, Buffer.from(data), mimeType);
runtime.store.persist();
return protoMessage(1, encodeWrittenFile(fileEntryFromRow(row)));
}
function setProjectThumbnail(runtime, requestBody) {
const projectId = decodeProtoStringField(requestBody, 1) || firstUuidLikeString(requestBody) || "";
const dataUrl = decodeProtoStringField(requestBody, 2) || decodeAllProtoStrings(requestBody).find((item) => item.value.startsWith("data:"))?.value || "";
storeProjectThumbnail(runtime, projectId, dataUrl);
}
function storeProjectThumbnail(runtime, projectId, dataUrl) {
if (!projectId || !dataUrl) {
return;
}
const decoded = decodeDataUrl(dataUrl);
if (!decoded) {
return;
}
upsertGenericRecord(runtime.store, THUMBNAIL_COLLECTION, projectId, projectId, runtime.me.defaultModelId, {
body_base64: decoded.body.toString("base64"),
content_type: decoded.contentType,
project_id: projectId
});
}
function readProjectThumbnail(runtime, projectId) {
if (!projectId) {
return undefined;
}
const row = queryRows(
runtime.store.database,
"SELECT data_json FROM claude_design_items WHERE collection = ? AND uuid = ? LIMIT 1",
[THUMBNAIL_COLLECTION, projectId]
)[0];
const data = parseMaybeJson(row?.data_json, {});
if (!data.body_base64) {
return undefined;
}
return {
body: Buffer.from(data.body_base64, "base64"),
contentType: data.content_type || "image/png"
};
}
function decodeDataUrl(dataUrl) {
const match = /^data:([^;,]+)?(;base64)?,(.*)$/s.exec(String(dataUrl || ""));
if (!match) {
return undefined;
}
const contentType = match[1] || "application/octet-stream";
const body = match[2] ? Buffer.from(match[3], "base64") : Buffer.from(decodeURIComponent(match[3]), "utf8");
return { body, contentType };
}
function createClaudeCodeSession(runtime, requestBody) {
const projectId = decodeProtoStringField(requestBody, 1) || firstUuidLikeString(requestBody) || "";
const sessionId = randomUuid();
upsertGenericRecord(runtime.store, SESSION_COLLECTION, sessionId, "Claude Code Session", runtime.me.defaultModelId, {
project_id: projectId,
session_id: sessionId,
source: "claude-design-mock"
});
if (projectId) {
saveProjectData(runtime, projectId, (data) => ({
...data,
has_claude_code_session: true
}));
}
return protoString(1, sessionId);
}
function listChatsForExport(runtime, requestBody) {
const projectId = decodeProtoStringField(requestBody, 1) || firstUuidLikeString(requestBody) || "";
const chats = getProjectChats(runtime, projectId);
return Buffer.concat(chats.map((chat) => protoMessage(1, encodeExportChatSummary(chat))));
}
function exportChatMessages(runtime, requestBody) {
const projectId = decodeProtoStringField(requestBody, 1) || firstUuidLikeString(requestBody) || "";
const chatId = decodeProtoStringField(requestBody, 2) || decodeAllProtoStrings(requestBody).find((item) => item.value !== projectId)?.value || "";
const chat = getProjectChats(runtime, projectId).find((item) => item.id === chatId) || { id: chatId, messages: [] };
upsertGenericRecord(runtime.store, "chat_exports", randomUuid(), chat.id || "chat", runtime.me.defaultModelId, {
chat_id: chat.id,
exported_at: new Date().toISOString(),
messages: chat.messages || [],
project_id: projectId
});
return Buffer.alloc(0);
}
function getProjectChats(runtime, projectId) {
const data = parseMaybeJson(getProjectDataBytes(runtime, projectId).toString("utf8"), {});
const openChats = Object.values(isRecord(data.chats) ? data.chats : {});
const closedChats = Array.isArray(data.closedChats) ? data.closedChats : [];
return [...openChats, ...closedChats].filter(isRecord).map((chat) => ({
closedAt: chat.closedAt || "",
id: stringValue(chat.id) || randomUuid(),
lastOpened: chat.lastOpened || "",
messageCount: Array.isArray(chat.messages) ? chat.messages.length : Number(chat.messageCount) || 0,
messages: Array.isArray(chat.messages) ? chat.messages : [],
name: stringValue(chat.name) || "Chat"
}));
}
function encodeExportChatSummary(chat) {
return Buffer.concat([
protoString(1, chat.id),
protoString(2, chat.name),
protoInt32(3, chat.messageCount || 0),
protoTimestamp(4, chat.lastOpened || chat.closedAt)
]);
}
function recordTrackEvent(runtime, requestBody) {
const strings = decodeAllProtoStrings(requestBody);
const eventName = decodeProtoStringField(requestBody, 1) || strings[0]?.value || "event";
upsertGenericRecord(runtime.store, EVENT_COLLECTION, randomUuid(), eventName, runtime.me.defaultModelId, {
event_name: eventName,
fields: strings,
recorded_at: new Date().toISOString()
});
}
function createComment(runtime, requestBody) {
const comment = commentFromRequest(runtime, requestBody, {}, "create");
upsertComment(runtime, comment);
return protoMessage(1, encodeComment(comment));
}
function updateComment(runtime, requestBody) {
const projectId = decodeProtoStringField(requestBody, 1) || "";
const commentId = decodeProtoStringField(requestBody, 2) || firstUuidLikeString(requestBody) || "";
const existing = getComment(runtime, commentId);
const updated = commentFromRequest(runtime, requestBody, existing || { id: commentId, project_id: projectId }, "update");
upsertComment(runtime, { ...existing, ...updated, id: commentId || updated.id });
return protoMessage(1, encodeComment(getComment(runtime, commentId || updated.id) || updated));
}
function deleteComment(runtime, requestBody) {
const commentId = decodeProtoStringField(requestBody, 2) || firstUuidLikeString(requestBody) || "";
if (commentId) {
deleteItem(runtime.store, COMMENT_COLLECTION, commentId);
}
}
function listComments(runtime, requestBody) {
const projectId = decodeProtoStringField(requestBody, 1) || firstUuidLikeString(requestBody) || "";
const comments = listItems(runtime.store, COMMENT_COLLECTION, runtime.me.organizationUuid)
.filter((comment) => !projectId || comment.project_id === projectId)
.map(normalizeCommentRecord);
return Buffer.concat([
...comments.map((comment) => protoMessage(1, encodeComment(comment))),
getCommentsReadAt(runtime, projectId) ? protoTimestamp(2, getCommentsReadAt(runtime, projectId)) : Buffer.alloc(0)
]);
}
function markCommentsRead(runtime, requestBody) {
const projectId = decodeProtoStringField(requestBody, 1) || firstUuidLikeString(requestBody) || "";
const readAt = new Date().toISOString();
if (projectId) {
upsertGenericRecord(runtime.store, "comment_state", projectId, projectId, runtime.me.defaultModelId, {
comments_read_at: readAt,
project_id: projectId
});
}
return protoTimestamp(1, readAt);
}
function getCommentsReadAt(runtime, projectId) {
if (!projectId) {
return "";
}
const state = getExistingItem(runtime.store, "comment_state", runtime.me.organizationUuid, projectId);
return stringValue(state?.comments_read_at);
}
function createCommentReply(runtime, requestBody) {
const commentId = decodeProtoStringField(requestBody, 2) || "";
const comment = getComment(runtime, commentId) || { id: commentId || randomUuid(), replies: [] };
const strings = decodeAllProtoStrings(requestBody).map((item) => item.value);
const text = decodeProtoStringField(requestBody, 3) || strings.find((value) => value !== comment.project_id && value !== commentId) || "";
const reply = {
author_account_uuid: runtime.me.accountUuid,
author_display_name: runtime.me.displayName,
author_email: runtime.me.email,
author_name: runtime.me.displayName,
body: text,
comment_id: comment.id,
created_at: new Date().toISOString(),
id: randomUuid(),
text
};
comment.replies = [...(Array.isArray(comment.replies) ? comment.replies : []), reply];
upsertComment(runtime, comment);
return protoMessage(1, encodeCommentReply(reply));
}
function updateCommentReply(runtime, requestBody) {
const commentId = decodeProtoStringField(requestBody, 2) || "";
const replyId = decodeProtoStringField(requestBody, 3) || "";
const text = decodeProtoStringField(requestBody, 4) || "";
const comment = getComment(runtime, commentId);
if (comment && Array.isArray(comment.replies)) {
comment.replies = comment.replies.map((reply) => reply.id === replyId ? { ...reply, body: text, text, updated_at: new Date().toISOString() } : reply);
upsertComment(runtime, comment);
const reply = comment.replies.find((item) => item.id === replyId);
return protoMessage(1, encodeCommentReply(reply || {}));
}
return Buffer.alloc(0);
}
function deleteCommentReply(runtime, requestBody) {
const commentId = decodeProtoStringField(requestBody, 2) || "";
const replyId = decodeProtoStringField(requestBody, 3) || "";
const comment = getComment(runtime, commentId);
if (comment && Array.isArray(comment.replies)) {
comment.replies = comment.replies.filter((reply) => reply.id !== replyId);
upsertComment(runtime, comment);
}
}
function sendCommentsToChat(runtime, requestBody) {
const projectId = decodeProtoStringField(requestBody, 1) || firstUuidLikeString(requestBody) || "";
const chatId = decodeProtoStringField(requestBody, 3) || randomUuid();
const messageId = randomUuid();
saveProjectData(runtime, projectId, (data) => {
const chats = isRecord(data.chats) ? { ...data.chats } : {};
const chat = isRecord(chats[chatId]) ? { ...chats[chatId] } : { id: chatId, messages: [] };
const messages = Array.isArray(chat.messages) ? [...chat.messages] : [];
messages.push({
content: "Attached comments were sent to chat.",
id: messageId,
role: "user",
timestamp: new Date().toISOString()
});
chats[chatId] = { ...chat, messages };
return { ...data, chats };
});
const commentIds = decodeProtoStringFields(requestBody, 2);
return Buffer.concat((commentIds.length > 0 ? commentIds : [messageId]).map((commentId) => protoString(1, commentId)));
}
function sendMultiplayerMessage(runtime, requestBody) {
const projectId = decodeProtoStringField(requestBody, 1) || "";
const chatId = decodeProtoStringField(requestBody, 2) || randomUuid();
const embeddedRequest = extractGatewayMessagesRequestFromProto(requestBody);
const embeddedUserText = cleanVisibleUserText(textFromMessageContent(lastGatewayUserMessage(embeddedRequest)?.content));
const content = decodeProtoStringField(requestBody, 3) || embeddedUserText || "";
const role = decodeProtoStringField(requestBody, 4) || "user";
const clientMessageId = decodeProtoStringField(requestBody, 7) || randomUuid();
const epoch = Date.now();
saveProjectData(runtime, projectId, (data) => {
const chats = isRecord(data.chats) ? { ...data.chats } : {};
const chat = isRecord(chats[chatId]) ? { ...chats[chatId] } : { id: chatId, messages: [] };
const messages = Array.isArray(chat.messages) ? [...chat.messages] : [];
if (content) {
messages.push({
content,
id: clientMessageId,
role,
timestamp: new Date().toISOString()
});
}
chats[chatId] = { ...chat, messages };
return { ...data, chats };
});
return Buffer.concat([
protoString(1, clientMessageId),
protoInt64(2, epoch),
protoString(4, "")
]);
}
function commentFromRequest(runtime, requestBody, fallback, mode) {
const strings = decodeAllProtoStrings(requestBody).map((item) => item.value);
const projectId = decodeProtoStringField(requestBody, 1) || fallback.project_id || firstUuidLikeString(requestBody) || "";
const id = mode === "update" ? decodeProtoStringField(requestBody, 2) || fallback.id || fallback.commentId || randomUuid() : fallback.id || fallback.commentId || randomUuid();
const bodyField = mode === "create" ? 2 : 3;
const filePathField = mode === "create" ? 3 : 0;
const selectorField = mode === "create" ? 4 : 0;
const descriptorField = mode === "create" ? 5 : 0;
const text =
decodeProtoStringField(requestBody, bodyField) ||
strings.find((value) => value && value !== projectId && value !== id) ||
fallback.text ||
fallback.body ||
"";
const filePath = sanitizeProjectFilePath((filePathField ? decodeProtoStringField(requestBody, filePathField) : "") || fallback.path || fallback.filePath || "");
const elementSelector = (selectorField ? decodeProtoStringField(requestBody, selectorField) : "") || fallback.element_selector || fallback.elementSelector || "";
const elementDescriptor = (descriptorField ? decodeProtoStringField(requestBody, descriptorField) : "") || fallback.element_descriptor || fallback.elementDescriptor || "";
const now = new Date().toISOString();
return {
author_email: runtime.me.email,
author_account_uuid: runtime.me.accountUuid,
author_display_name: runtime.me.displayName,
author_name: runtime.me.displayName,
body: text,
created_at: fallback.created_at || now,
element_descriptor: elementDescriptor,
element_selector: elementSelector,
id,
path: filePath,
project_id: projectId,
replies: Array.isArray(fallback.replies) ? fallback.replies : [],
resolved: mode === "update" ? decodeProtoBoolField(requestBody, 4) || fallback.resolved === true : fallback.resolved === true,
text,
updated_at: now
};
}
function getComment(runtime, commentId) {
if (!commentId) {
return undefined;
}
const row = queryRows(
runtime.store.database,
"SELECT collection, uuid, created_at, updated_at, title, model, data_json, messages_json FROM claude_design_items WHERE collection = ? AND uuid = ? LIMIT 1",
[COMMENT_COLLECTION, commentId]
)[0];
return row ? normalizeCommentRecord(itemFromRow(row, runtime.me.organizationUuid)) : undefined;
}
function upsertComment(runtime, comment) {
const normalized = normalizeCommentRecord(comment);
upsertGenericRecord(runtime.store, COMMENT_COLLECTION, normalized.id, normalized.text || "Comment", runtime.me.defaultModelId, normalized);
}
function normalizeCommentRecord(comment) {
const id = stringValue(comment.commentId) || stringValue(comment.id) || stringValue(comment.uuid) || randomUuid();
const text = stringValue(comment.body) || stringValue(comment.text) || "";
return {
...comment,
author_account_uuid: stringValue(comment.author_account_uuid) || stringValue(comment.authorAccountUuid),
author_display_name: stringValue(comment.author_display_name) || stringValue(comment.authorDisplayName) || stringValue(comment.author_name),
body: text,
commentId: id,
element_descriptor: stringValue(comment.element_descriptor) || stringValue(comment.elementDescriptor),
element_selector: stringValue(comment.element_selector) || stringValue(comment.elementSelector),
filePath: stringValue(comment.filePath) || stringValue(comment.path),
id,
replies: Array.isArray(comment.replies) ? comment.replies : []
};
}
function encodeComment(comment) {
const normalized = normalizeCommentRecord(comment);
return Buffer.concat([
protoString(1, normalized.id),
protoString(2, normalized.project_id),
protoString(3, normalized.author_account_uuid),
protoString(4, normalized.author_display_name),
protoString(5, normalized.body),
protoString(9, "mock"),
...normalized.replies.map((reply) => protoMessage(12, encodeCommentReply(reply)))
]);
}
function encodeCommentReply(reply) {
const normalized = {
...reply,
author_account_uuid: stringValue(reply.author_account_uuid) || stringValue(reply.authorAccountUuid),
author_display_name: stringValue(reply.author_display_name) || stringValue(reply.authorDisplayName) || stringValue(reply.author_name),
body: stringValue(reply.body) || stringValue(reply.text),
comment_id: stringValue(reply.comment_id) || stringValue(reply.commentId),
id: stringValue(reply.replyId) || stringValue(reply.id) || randomUuid()
};
return Buffer.concat([
protoString(1, normalized.id),
protoString(2, normalized.comment_id),
protoString(3, normalized.author_account_uuid),
protoString(4, normalized.author_display_name),
protoString(5, normalized.body)
]);
}
function encodeToolCallResponse(rpcName, requestBody) {
const strings = decodeAllProtoStrings(requestBody);
const payload = {
mock: true,
method: rpcName,
request: strings
};
return Buffer.concat([
protoString(1, JSON.stringify(payload)),
protoBool(2, false)
]);
}
function encodeIntegrationAuthResponse(rpcName) {
if (!rpcName.endsWith("StartAuth")) {
return Buffer.alloc(0);
}
return Buffer.concat([
protoString(1, `${rpcName}:mock`),
protoString(2, "mock-connected")
]);
}
function encodeIntegrationListResponse(rpcName, requestBody) {
return Buffer.alloc(0);
}
function updateOrgSettings(runtime, requestBody) {
const defaultDesignSystemProjectUuid = decodeProtoStringField(requestBody, 1) || "";
upsertGenericRecord(runtime.store, "org_settings", "default", "default", runtime.me.defaultModelId, {
default_design_system_project_uuid: defaultDesignSystemProjectUuid
});
}
function encodeGetOrgSettingsResponse(runtime) {
const settings = getItem(runtime.store, "org_settings", runtime.me.organizationUuid, "default", runtime.me);
return Buffer.concat([
protoString(1, settings.default_design_system_project_uuid),
protoTimestamp(2, settings.updated_at)
]);
}
function encodeGetMeResponse(me) {
return Buffer.concat([
protoString(1, me.accountUuid),
protoString(2, me.organizationUuid),
protoString(3, me.email),
protoString(4, me.displayName),
protoString(5, me.orgName),
protoString(6, me.growthbookPayload),
...((me.modelPresets || []).map((preset) => protoMessage(8, encodeModelPreset(preset)))),
protoString(9, me.defaultModelId),
protoBool(10, me.overrideStickyModel),
protoBool(11, me.isPersonalOrg),
protoEnum(12, me.accessLevel === "ACCESS_LEVEL_VIEWER" ? 2 : 1),
protoBool(14, me.hasOauthTokens),
protoBool(15, Boolean(me.dsManageEnforced)),
protoBool(16, me.canManageDs),
...((me.memberships || []).map((membership) => protoMessage(17, Buffer.concat([protoString(1, membership.uuid), protoString(2, membership.name)]))))
]);
}
function encodeModelPreset(preset) {
return Buffer.concat([
protoString(1, preset.id),
protoString(2, preset.label),
protoInt32(3, preset.maxTokens || 0),
protoString(4, preset.description),
protoBool(5, preset.overflow === true),
protoBool(6, preset.supportsAdaptiveThinking === true)
]);
}
function encodeMintPreviewTokenResponse(runtime) {
return Buffer.concat([
protoString(1, runtime.upstreamOrigin),
protoString(2, randomUuid()),
protoInt64(3, Math.floor(Date.now() / 1000) + 3600)
]);
}
function encodeTokenResponse() {
return Buffer.concat([protoString(1, randomUuid()), protoInt64(2, Math.floor(Date.now() / 1000) + 3600)]);
}
function designSettingsPayload(runtime) {
const item = getExistingItem(runtime.store, "settings", runtime.me.organizationUuid, "default");
const model = normalizeClaudeDesignSelectableModel(runtime, stringValue(item?.model) || stringValue(item?.selected_model)) ||
runtime.me.defaultModelId;
return {
defaultModelId: model,
default_model_id: model,
model,
selectedModel: model,
selected_model: model
};
}
function persistDesignSelectedModel(runtime, modelValue) {
const model = normalizeClaudeDesignSelectableModel(runtime, modelValue) || runtime.me.defaultModelId;
runtime.me.defaultModelId = model;
upsertGenericRecord(runtime.store, "settings", "default", "Claude Design Settings", model, {
default_model_id: model,
defaultModelId: model,
model,
selected_model: model,
selectedModel: model
});
return designSettingsPayload(runtime);
}
function normalizeClaudeDesignSelectableModel(runtime, value) {
const normalized = normalizeRouteTarget(value);
if (!normalized) {
return undefined;
}
const presets = Array.isArray(runtime?.gatewayModelPresets) ? runtime.gatewayModelPresets : [];
const direct = findGatewayModelPresetId(presets, normalized);
if (direct) {
return direct;
}
const publicModel = publicGatewayModelSelector(normalized);
return findGatewayModelPresetId(presets, publicModel) || publicModel || normalized;
}
function findGatewayModelPresetId(presets, model) {
const normalized = stringValue(model)?.toLowerCase();
if (!normalized) {
return undefined;
}
const preset = presets.find((preset) => isRecord(preset) && stringValue(preset.id)?.toLowerCase() === normalized);
return stringValue(preset?.id);
}
function upsertGenericRecord(store, collection, uuid, title, model, data) {
const now = new Date().toISOString();
const existing = queryRows(
store.database,
"SELECT created_at FROM claude_design_items WHERE collection = ? AND uuid = ? LIMIT 1",
[collection, uuid]
)[0];
store.database.run(
"INSERT OR REPLACE INTO claude_design_items (collection, uuid, created_at, updated_at, title, model, data_json, messages_json) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
[collection, uuid, existing?.created_at || now, now, title || uuid, model || "", JSON.stringify(data || {}), "[]"]
);
store.persist();
}
async function handleDesignRestApi(runtime, method, path, request, requestBody) {
const restPath = normalizeDesignRestPath(path);
if (method === "POST" && restPath === "/v1/design/telemetry") {
return jsonResponse(200, { ok: true });
}
if (method === "GET" && (restPath === "/v1/design/settings" || restPath === "/v1/design/model-selection")) {
return jsonResponse(200, designSettingsPayload(runtime));
}
if (method === "POST" && (restPath === "/v1/design/settings" || restPath === "/v1/design/model-selection")) {
const payload = parseJsonBody(requestBody);
const settings = persistDesignSelectedModel(runtime, stringValue(payload.model) || stringValue(payload.defaultModelId));
return jsonResponse(200, {
ok: true,
...settings
});
}
if (method === "POST" && restPath === "/v1/design/turn-title") {
const payload = parseJsonBody(requestBody);
const message = stringValue(payload.message) || stringValue(payload.title) || stringValue(payload.prompt) || "";
const title = titleFromTurnMessage(message);
return jsonResponse(200, {
kind: stringValue(payload.kind) || "chat",
ok: true,
title
});
}
if (method === "POST" && restPath === "/v1/design/artifact-proxy/v1/messages") {
return await proxyArtifactMessages(runtime, requestBody);
}
const serveMatch = restPath.match(/^\/v1\/design\/projects\/([^/]+)\/serve\/(.+)$/);
if (method === "GET" && serveMatch) {
const projectId = decodeURIComponent(serveMatch[1]);
const filePath = sanitizeProjectFilePath(decodeURIComponent(serveMatch[2]));
const row = getProjectFileRow(runtime, projectId, filePath);
if (!row) {
return jsonResponse(404, { error: { message: `File not found: ${filePath}` } });
}
return serveProjectFileResponse(runtime, projectId, row, filePath);
}
const dataMatch = restPath.match(/^\/v1\/design\/projects\/([^/]+)\/data$/);
if (method === "PUT" && dataMatch) {
const projectId = decodeURIComponent(dataMatch[1]);
const payload = parseJsonBody(requestBody);
const base64 = stringValue(payload.data);
if (base64) {
saveProjectData(runtime, projectId, (data) => ({
...data,
project_data_base64: base64
}));
}
return jsonResponse(200, { ok: true, project_id: projectId });
}
const thumbnailPutMatch = restPath.match(/^\/v1\/design\/projects\/([^/]+)\/thumbnail$/);
if (method === "PUT" && thumbnailPutMatch) {
const projectId = decodeURIComponent(thumbnailPutMatch[1]);
const payload = parseJsonBody(requestBody);
storeProjectThumbnail(runtime, projectId, stringValue(payload.thumbnail_data_url) || stringValue(payload.thumbnailDataUrl) || "");
return jsonResponse(200, { ok: true, project_id: projectId });
}
const thumbnailGetMatch = restPath.match(/^\/v1\/design\/projects\/([^/]+)\/thumbnail(?:\/[^/]+)?$/);
if (method === "GET" && thumbnailGetMatch) {
const projectId = decodeURIComponent(thumbnailGetMatch[1]);
const thumbnail = readProjectThumbnail(runtime, projectId);
if (thumbnail) {
return binaryResponse(200, thumbnail.body, {
"cache-control": "no-store",
"content-type": thumbnail.contentType
});
}
return textResponse(200, TRANSPARENT_SVG, {
"cache-control": "no-store",
"content-type": "image/svg+xml"
});
}
const downloadMatch = restPath.match(/^\/v1\/design\/projects\/([^/]+)\/download$/);
if (method === "GET" && downloadMatch) {
const projectId = decodeURIComponent(downloadMatch[1]);
const manifest = listProjectFileRows(runtime, projectId, "").map((row) => ({
content_type: row.content_type,
path: row.path,
size: Buffer.from(row.body_base64 || "", "base64").length,
version: row.version
}));
return textResponse(200, JSON.stringify({ files: manifest, project_id: projectId }, null, 2), {
"content-disposition": `attachment; filename="${projectId}.json"`,
"content-type": "application/json; charset=utf-8"
});
}
if (method === "POST" && restPath === "/v1/design/drop-suggestions") {
return jsonResponse(200, { suggestions: [] });
}
return jsonResponse(404, {
error: {
message: `Claude Design REST mock has no route for ${method} ${path}`
}
});
}
async function proxyArtifactMessages(runtime, requestBody) {
const payload = parseJsonBody(requestBody);
const gatewayBody = normalizeGatewayMessagesRequest(runtime, payload);
const routingDecision = applyClaudeDesignRouting(runtime, gatewayBody);
const sessionContext = claudeDesignSessionContext({
chatId: readClaudeDesignChatId(payload),
projectId: readClaudeDesignProjectId(payload)
});
let response;
try {
response = await fetchGateway(runtime, "/v1/messages", gatewayBody, routingDecision, sessionContext);
} catch (error) {
return textResponse(200, artifactProxySseError(error instanceof Error ? error.message : String(error)), {
"cache-control": "no-store",
"content-type": "text/event-stream; charset=utf-8"
});
}
const bodyText = await response.text();
if (
headerIncludes(response.headers.get("content-type"), "text/event-stream") ||
bodyText.startsWith("data: ") ||
bodyText.includes("\ndata: ") ||
bodyText.includes("\r\ndata: ")
) {
return textResponse(response.status, bodyText, {
"cache-control": "no-store",
"content-type": response.headers.get("content-type") || "text/event-stream; charset=utf-8"
});
}
const body = parseMaybeJson(bodyText, {});
if (!response.ok) {
const message = body?.error?.message || bodyText || `Gateway request failed with HTTP ${response.status}`;
return textResponse(200, artifactProxySseError(message), {
"cache-control": "no-store",
"content-type": "text/event-stream; charset=utf-8"
});
}
return textResponse(200, artifactProxySseText(extractGatewayAssistantText(body)), {
"cache-control": "no-store",
"content-type": "text/event-stream; charset=utf-8"
});
}
function artifactProxySseText(text) {
const value = stringValue(text);
const events = [];
if (value) {
events.push({
delta: {
text: value,
type: "text_delta"
},
type: "content_block_delta"
});
}
return `${events.map((event) => `data: ${JSON.stringify(event)}\n\n`).join("")}data: [DONE]\n\n`;
}
function artifactProxySseError(message) {
return `data: ${JSON.stringify({ error: { message: message || "Unknown gateway error." }, type: "error" })}\n\ndata: [DONE]\n\n`;
}
function normalizeDesignRestPath(path) {
if (path.startsWith("/design/v1/design/")) {
return path.slice("/design".length);
}
return path;
}
function serveProjectFileResponse(runtime, projectId, row, filePath) {
const body = Buffer.from(row.body_base64 || "", "base64");
const contentType = row.content_type || guessContentType(filePath);
if (isHtmlProjectFile(filePath, contentType)) {
const html = body.toString("utf8");
const normalizedHtml = normalizeBabelJsxScriptTags(html);
const inlinedHtml = inlineLocalBabelJsxScriptTags(runtime, projectId, filePath, normalizedHtml);
return textResponse(200, injectOmelettePreviewEvalBridge(inlinedHtml), {
"cache-control": "no-store",
"content-type": "text/html; charset=utf-8"
});
}
return binaryResponse(200, body, {
"cache-control": "no-store",
"content-type": contentType
});
}
function isHtmlProjectFile(filePath, contentType) {
return /\.html?$/i.test(filePath) || headerIncludes(contentType, "text/html");
}
function injectOmelettePreviewEvalBridge(html) {
const source = String(html || "");
if (source.includes("__om_eval") || source.includes("__omEvalBridgeInstalled")) {
return source;
}
const scriptTag = `<script>${OMELETTE_PREVIEW_EVAL_BRIDGE_SCRIPT}</script>`;
if (/<head\b[^>]*>/i.test(source)) {
return source.replace(/<head\b([^>]*)>/i, (tag) => `${tag}\n${scriptTag}`);
}
if (/<body\b[^>]*>/i.test(source)) {
return source.replace(/<body\b([^>]*)>/i, (tag) => `${tag}\n${scriptTag}`);
}
return `${scriptTag}\n${source}`;
}
function normalizeBabelJsxScriptTags(html) {
if (!/(?:@babel\/standalone|babel\.min\.js|type=["']text\/babel["'])/i.test(html)) {
return html;
}
return String(html).replace(/<script\b([^>]*)>/gi, (tag, attrs) => {
if (/\btype\s*=/.test(attrs)) {
return tag;
}
const srcMatch = /\bsrc\s*=\s*(["'])([^"']+)\1/i.exec(attrs);
if (!srcMatch || !/\.jsx(?:[?#].*)?$/i.test(srcMatch[2])) {
return tag;
}
return `<script type="text/babel"${attrs}>`;
});
}
function inlineLocalBabelJsxScriptTags(runtime, projectId, filePath, html) {
if (!/(?:@babel\/standalone|babel\.min\.js|type=["']text\/babel["'])/i.test(html)) {
return html;
}
return String(html).replace(/<script\b([^>]*)>\s*<\/script>/gi, (tag, attrs) => {
const typeMatch = /\btype\s*=\s*(["'])([^"']+)\1/i.exec(attrs);
if (!typeMatch || typeMatch[2].toLowerCase() !== "text/babel") {
return tag;
}
const srcMatch = /\bsrc\s*=\s*(["'])([^"']+)\1/i.exec(attrs);
if (!srcMatch || !/\.jsx(?:[?#].*)?$/i.test(srcMatch[2])) {
return tag;
}
const scriptPath = resolveLocalProjectScriptPath(filePath, srcMatch[2]);
if (!scriptPath) {
return tag;
}
const scriptRow = getProjectFileRow(runtime, projectId, scriptPath);
if (!scriptRow) {
return tag;
}
const scriptBody = Buffer.from(scriptRow.body_base64 || "", "base64").toString("utf8");
const inlineAttrs = attrs.replace(srcMatch[0], "");
return `<script${inlineAttrs}>${escapeInlineScriptBody(scriptBody)}\n</script>`;
});
}
function resolveLocalProjectScriptPath(filePath, src) {
const rawSrc = String(src || "").trim();
if (!rawSrc || rawSrc.startsWith("/") || rawSrc.startsWith("//") || /^[a-z][a-z0-9+.-]*:/i.test(rawSrc)) {
return "";
}
const pathOnly = rawSrc.split("#", 1)[0].split("?", 1)[0];
let decodedPath = pathOnly;
try {
decodedPath = decodeURIComponent(pathOnly);
} catch {
decodedPath = pathOnly;
}
const baseDir = pathModule.posix.dirname(sanitizeProjectFilePath(filePath));
return sanitizeProjectFilePath(pathModule.posix.join(baseDir === "." ? "" : baseDir, decodedPath));
}
function escapeInlineScriptBody(body) {
return String(body || "").replace(/<\/script/gi, "<\\/script");
}
function titleFromTurnMessage(message) {
const text = String(message || "")
.replace(/\s+/g, " ")
.trim();
if (!text) {
return "New turn";
}
if (/questions timed out/i.test(text)) {
return "Continue with defaults";
}
return text.length > 60 ? `${text.slice(0, 57).trim()}...` : text;
}
async function countGatewayTokens(runtime, requestBody) {
const messagesRequest = decodeProtoBytesField(requestBody, 1) || Buffer.alloc(0);
const body = parseGatewayMessagesRequestBuffer(messagesRequest) ||
extractGatewayMessagesRequestFromProto(requestBody) ||
{};
const normalized = normalizeGatewayMessagesRequest(runtime, body);
const routingDecision = applyClaudeDesignRouting(runtime, normalized);
try {
const response = await fetchGateway(runtime, "/v1/messages/count_tokens", normalized, routingDecision);
if (response.ok) {
const payload = await response.json();
return protoInt32(1, Number(payload.input_tokens) || estimateTokenCount(normalized));
}
} catch {
// Fall through to local estimate.
}
return protoInt32(1, estimateTokenCount(normalized));
}
async function chatWithGateway(runtime, requestBody) {
const projectId = decodeProtoStringField(requestBody, 1) || "";
const chatId = decodeProtoStringField(requestBody, 3) || (projectId ? defaultProjectChatId(projectId) : randomUuid());
const request = {
assistantMessageId: decodeProtoStringField(requestBody, 4) || randomUuid(),
chatId,
messagesRequest: decodeProtoBytesField(requestBody, 2) || Buffer.alloc(0),
projectId
};
const originalBody = parseGatewayMessagesRequestBuffer(request.messagesRequest) ||
extractGatewayMessagesRequestFromProto(requestBody) ||
gatewayMessagesRequestFromProjectChat(runtime, request) ||
{};
if (!hasGatewayMessagesInput(originalBody)) {
return connectStreamResponse([
encodeChatResponseMessageStart(request.assistantMessageId),
encodeChatResponseError("Claude Design chat request did not include a messages payload.")
]);
}
const gatewayBody = normalizeGatewayMessagesRequest(runtime, {
...originalBody,
stream: false
});
const routingDecision = applyClaudeDesignRouting(runtime, gatewayBody);
const sessionContext = claudeDesignSessionContext(request);
let assistantText = "";
let toolCalls = [];
let stopReason = "end_turn";
let messageId = request.assistantMessageId;
let model = gatewayBody.model || runtime.me.defaultModelId;
const events = [encodeChatResponseMessageStart(messageId), encodeChatResponseRaw("message_start", { message: { content: [], id: messageId, model, role: "assistant" } })];
try {
const response = await fetchGateway(runtime, "/v1/messages", gatewayBody, routingDecision, sessionContext);
const payloadText = await response.text();
const payload = parseMaybeJson(payloadText, {});
if (!response.ok) {
const message = payload?.error?.message || payloadText || `Gateway request failed with HTTP ${response.status}`;
events.push(encodeChatResponseError(message));
return connectStreamResponse(events);
}
assistantText = extractGatewayAssistantText(payload);
toolCalls = extractGatewayToolCalls(payload);
const gatewayStopReason = extractGatewayStopReason(payload, stopReason);
if (runtime.autoAnswerQuestions) {
const absorbed = absorbQuestionToolCalls(assistantText, toolCalls);
assistantText = absorbed.assistantText;
toolCalls = absorbed.toolCalls;
}
stopReason = toolCalls.length > 0 ? "tool_use" : gatewayStopReason === "tool_use" ? "end_turn" : gatewayStopReason;
messageId = stringValue(payload.id) || messageId;
model = publicGatewayModelSelector(routingDecision.routedModel || gatewayBody.model || model) || model;
} catch (error) {
events.push(encodeChatResponseError(error instanceof Error ? error.message : String(error)));
return connectStreamResponse(events);
}
if (!assistantText && toolCalls.length === 0) {
assistantText = "Claude Design gateway returned an empty response.";
}
if (assistantText) {
events.push(encodeChatResponseTextDelta(assistantText));
events.push(encodeChatResponseRaw("text_block", { index: 0, text: assistantText }));
}
const firstToolIndex = assistantText ? 1 : 0;
toolCalls.forEach((toolCall, index) => {
const blockIndex = firstToolIndex + index;
events.push(
encodeChatResponseRaw("tool_delta", {
id: toolCall.id,
index: blockIndex,
partial_json: JSON.stringify(toolCall.input || {}),
tool: toolCall.name
})
);
events.push(
encodeChatResponseRaw("tool_block_complete", {
id: toolCall.id,
index: blockIndex,
input: toolCall.input || {},
name: toolCall.name
})
);
});
const messageContent = gatewayMessageContent(assistantText, toolCalls);
events.push(
encodeChatResponseRaw("done", {
message: {
content: messageContent,
id: messageId,
model,
role: "assistant",
stop_reason: stopReason
}
})
);
events.push(encodeChatResponseMessageStop(stopReason));
appendChatExchange(runtime, request, gatewayBody, assistantText, messageId, gatewayContentBlocks(assistantText, toolCalls));
return connectStreamResponse(events);
}
function parseGatewayMessagesRequestBuffer(buffer) {
if (!Buffer.isBuffer(buffer) || buffer.length === 0) {
return undefined;
}
const text = buffer.toString("utf8").trim();
if (!text || !text.startsWith("{")) {
return undefined;
}
const body = parseMaybeJson(text, undefined);
return hasGatewayMessagesInput(body) ? body : undefined;
}
function extractGatewayMessagesRequestFromProto(buffer, depth = 0, seen = new Set()) {
if (!Buffer.isBuffer(buffer) || buffer.length === 0 || depth > 6 || seen.has(buffer)) {
return undefined;
}
seen.add(buffer);
const direct = parseGatewayMessagesRequestBuffer(buffer);
if (direct) {
return direct;
}
let found;
forEachProtoField(buffer, (field) => {
if (found || field.wireType !== 2 || !Buffer.isBuffer(field.value) || field.value.length === 0) {
return;
}
const directValue = parseGatewayMessagesRequestBuffer(field.value);
if (directValue) {
found = directValue;
return;
}
const textValue = field.value.toString("utf8");
const embeddedValue = extractGatewayMessagesRequestFromText(textValue);
if (embeddedValue) {
found = embeddedValue;
return;
}
if (field.value.length <= 512 * 1024) {
found = extractGatewayMessagesRequestFromProto(field.value, depth + 1, seen);
}
});
return found;
}
function extractGatewayMessagesRequestFromText(text) {
if (typeof text !== "string" || !text.includes("messages")) {
return undefined;
}
const start = text.indexOf("{");
const end = text.lastIndexOf("}");
if (start < 0 || end <= start) {
return undefined;
}
const body = parseMaybeJson(text.slice(start, end + 1), undefined);
return hasGatewayMessagesInput(body) ? body : undefined;
}
function hasGatewayMessagesInput(body) {
if (!isRecord(body)) {
return false;
}
if (stringValue(body.message) || stringValue(body.prompt)) {
return true;
}
if (!Array.isArray(body.messages) || body.messages.length === 0) {
return false;
}
return body.messages.some((message) => {
if (!isRecord(message)) {
return false;
}
return Boolean(stringValue(message.content) || storedProjectMessageText(message) ||
(Array.isArray(message.content) && message.content.some((block) =>
typeof block === "string" ||
(isRecord(block) && (stringValue(block.text) || stringValue(block.content) || stringValue(block.output_text)))
)));
});
}
function gatewayMessagesRequestFromProjectChat(runtime, request) {
const row = getProjectRow(runtime, request.projectId);
if (!row) {
return undefined;
}
const data = normalizedProjectDataFromRow(runtime, row);
const chatId = request.chatId || stringValue(data.viewState?.activeChatId) || defaultProjectChatId(row.uuid);
const chat = isRecord(data.chats) ? data.chats[chatId] : undefined;
const messages = Array.isArray(chat?.messages) ? chat.messages : [];
const gatewayMessages = messages
.map(gatewayMessageFromStoredProjectMessage)
.filter(Boolean);
return gatewayMessages.length > 0 ? { messages: gatewayMessages, model: runtime.me.defaultModelId } : undefined;
}
function normalizedProjectDataFromRow(runtime, row) {
const record = parseMaybeJson(row?.data_json, {});
const base64 = stringValue(record.project_data_base64);
const decoded = base64 ? parseMaybeJson(Buffer.from(base64, "base64").toString("utf8"), record) : record;
return normalizeProjectStoreData(runtime, row, decoded);
}
function gatewayMessageFromStoredProjectMessage(message) {
if (!isRecord(message)) {
return undefined;
}
const role = stringValue(message.role);
if (role !== "user" && role !== "assistant") {
return undefined;
}
const content = cleanVisibleUserText(storedProjectMessageText(message));
if (!content) {
return undefined;
}
return {
content,
role
};
}
function storedProjectMessageText(message) {
const parts = [];
const content = stringValue(message.content);
if (content) {
parts.push(content);
}
const blocks = Array.isArray(message.contentBlocks)
? message.contentBlocks
: Array.isArray(message.content_blocks)
? message.content_blocks
: undefined;
const blockText = textFromMessageContent(blocks);
if (blockText) {
parts.push(blockText);
}
if (Array.isArray(message.attachments)) {
for (const attachment of message.attachments) {
if (!isRecord(attachment)) {
continue;
}
const attachmentContent = stringValue(attachment.content) || stringValue(attachment.text);
if (!attachmentContent) {
continue;
}
const attachmentName = stringValue(attachment.name);
parts.push(attachmentName ? `${attachmentName}\n${attachmentContent}` : attachmentContent);
}
}
return parts.join("\n\n");
}
function normalizeGatewayMessagesRequest(runtime, body) {
const record = isRecord(body) ? body : {};
const text = stringValue(record.message) || stringValue(record.prompt) || "";
const normalizedMessages = Array.isArray(record.messages) ? normalizeGatewayMessagesForRequest(record.messages) : [];
const messages = normalizedMessages.length > 0
? normalizedMessages
: text
? [{ content: text, role: "user" }]
: [{ content: "Continue.", role: "user" }];
const maxTokens = Math.max(128, Number(record.max_tokens || record.maxTokens) || 4096);
return {
...record,
max_tokens: maxTokens,
messages,
model: publicGatewayModelSelector(stringValue(record.model) || runtime.me.defaultModelId) || runtime.me.defaultModelId,
stream: false
};
}
function normalizeGatewayMessagesForRequest(messages) {
return messages
.map((message) => {
if (!isRecord(message)) {
return undefined;
}
const role = stringValue(message.role) || "user";
if (role !== "system" && role !== "user" && role !== "assistant") {
return undefined;
}
if (stringValue(message.content) || Array.isArray(message.content)) {
return {
...message,
role
};
}
const content = cleanVisibleUserText(storedProjectMessageText(message));
return content ? { content, role } : undefined;
})
.filter(Boolean);
}
function applyClaudeDesignRouting(runtime, body) {
const requestedModel = stringValue(body?.model) || runtime.me.defaultModelId;
const routing = runtime.routing;
if (!routing || routing.enabled === false) {
return { requestedModel };
}
const route = resolveClaudeDesignRoute(routing, body, requestedModel, runtime.defaultGatewayModel, runtime.gatewayModelPresets);
if (!route?.target) {
return { requestedModel };
}
const target = usableClaudeDesignRouteTarget(runtime, route.target);
if (!target) {
return { requestedModel };
}
body.model = target;
return {
requestedModel,
reason: target === route.target ? route.reason : `${route.reason}:fallback`,
routedModel: target
};
}
function resolveClaudeDesignRoute(routing, body, requestedModel, defaultGatewayModel, gatewayModelPresets) {
for (const rule of routing.rules || []) {
if (rule.enabled === false || !rule.target) {
continue;
}
if (matchesClaudeDesignRouteRule(rule, body, requestedModel)) {
return {
reason: rule.id ? `plugin-rule:${rule.id}` : `plugin-rule:${rule.type}`,
target: rule.target
};
}
}
const aliasedModel = claudeDesignModelAliasTarget(requestedModel, defaultGatewayModel);
if (aliasedModel) {
return {
reason: "plugin-model-alias",
target: aliasedModel
};
}
if (routing.defaultTarget && !isKnownClaudeDesignGatewayModel(gatewayModelPresets, requestedModel)) {
return {
reason: "plugin-default",
target: routing.defaultTarget
};
}
return undefined;
}
function claudeDesignModelAliasTarget(requestedModel, defaultGatewayModel) {
const normalizedModel = stringValue(requestedModel);
if (!normalizedModel) {
return undefined;
}
const target = CLAUDE_DESIGN_MODEL_ALIASES.get(normalizedModel);
if (!target) {
return undefined;
}
return normalizeRouteTarget(defaultGatewayModel) || target;
}
function isKnownClaudeDesignGatewayModel(gatewayModelPresets, requestedModel) {
const publicModel = publicGatewayModelSelector(requestedModel);
if (!publicModel || !Array.isArray(gatewayModelPresets)) {
return false;
}
return gatewayModelPresets.some((preset) => isRecord(preset) && stringValue(preset.id)?.toLowerCase() === publicModel.toLowerCase());
}
function usableClaudeDesignRouteTarget(runtime, target) {
const normalized = normalizeRouteTarget(target);
if (!normalized) {
return undefined;
}
if (routeTargetProviderAvailable(runtime.availableProviderNames, normalized)) {
return normalized;
}
const fallback = usableClaudeDesignFallbackTarget(runtime);
warnUnavailableClaudeDesignRouteTarget(runtime, normalized, fallback);
return fallback;
}
function usableClaudeDesignFallbackTarget(runtime) {
const configured = normalizeRouteTarget(runtime?.defaultGatewayModel);
if (configured && routeTargetProviderAvailable(runtime?.availableProviderNames, configured)) {
return configured;
}
return DEFAULT_GATEWAY_MODEL;
}
function routeTargetProviderAvailable(availableProviderNames, target) {
if (!(availableProviderNames instanceof Set) || availableProviderNames.size === 0) {
return true;
}
const provider = routeTargetProviderName(target);
return !provider || availableProviderNames.has(provider.toLowerCase());
}
function routeTargetProviderName(target) {
const normalized = normalizeRouteTarget(target);
if (!normalized) {
return undefined;
}
const separator = normalized.indexOf("/");
if (separator <= 0 || separator >= normalized.length - 1) {
return undefined;
}
return normalized.slice(0, separator).trim();
}
function warnUnavailableClaudeDesignRouteTarget(runtime, target, fallback) {
const key = `${target}\n${fallback}`;
if (runtime?.unavailableRouteTargetWarnings?.has(key)) {
return;
}
runtime?.unavailableRouteTargetWarnings?.add(key);
runtime?.logger?.warn?.(
`Claude Design routing target "${target}" references a provider that is not configured in CCR; using "${fallback}" instead.`
);
}
function matchesClaudeDesignRouteRule(rule, body, requestedModel) {
switch (rule.type) {
case "always":
return true;
case "image":
return hasImageContent(body?.messages);
case "long-context":
return estimateTokenCount(body) > (rule.threshold || 200000);
case "model":
return Boolean(rule.model && requestedModel === rule.model);
case "model-prefix":
return Boolean(rule.pattern && requestedModel?.startsWith(rule.pattern));
case "thinking":
return Boolean(body?.thinking);
case "web-search":
return hasWebSearchTool(body?.tools);
default:
return false;
}
}
function normalizeClaudeDesignRouting(value, options = {}) {
const fallbackTarget = normalizeRouteTarget(composeRouteTarget(options.targetProvider, options.targetModel) || stringValue(options.targetModel));
const record = isRecord(value) ? value : {};
const rules = [];
if (isRecord(record.modelMap)) {
for (const [model, target] of Object.entries(record.modelMap)) {
const normalizedTarget = normalizeRouteTarget(stringValue(target));
const normalizedModel = stringValue(model);
if (!normalizedModel || !normalizedTarget) {
continue;
}
rules.push({
enabled: true,
id: `model-${sanitizeRouteId(normalizedModel)}`,
model: normalizedModel,
name: normalizedModel,
target: normalizedTarget,
type: "model"
});
}
}
if (Array.isArray(record.rules)) {
record.rules.forEach((rule, index) => {
const normalized = normalizeClaudeDesignRoutingRule(rule, index);
if (normalized) {
rules.push(normalized);
}
});
}
return {
defaultTarget: normalizeRouteTarget(stringValue(record.default) || stringValue(record.defaultTarget)) || fallbackTarget,
enabled: value === false ? false : record.enabled !== false,
rules
};
}
function normalizeClaudeDesignRoutingRule(value, index) {
if (!isRecord(value)) {
return undefined;
}
const type = stringValue(value.type) || "model";
if (!CLAUDE_DESIGN_ROUTE_TYPES.has(type)) {
return undefined;
}
const target = normalizeRouteTarget(
stringValue(value.target) ||
composeRouteTarget(value.targetProvider, value.targetModel) ||
stringValue(value.targetModel)
);
if (!target) {
return undefined;
}
const model = stringValue(value.model) || stringValue(value.sourceModel);
const pattern = stringValue(value.pattern) || (type === "model-prefix" ? model : undefined);
const threshold = positiveNumber(value.threshold) || positiveNumber(value.tokenThreshold);
const id = stringValue(value.id) || `${type}-${index + 1}`;
return {
enabled: value.enabled !== false,
id,
name: stringValue(value.name) || id,
...(model ? { model } : {}),
...(pattern ? { pattern } : {}),
target,
...(threshold ? { threshold } : {}),
type
};
}
function normalizeRouteTarget(value) {
const raw = stringValue(value);
if (!raw) {
return undefined;
}
const commaIndex = raw.indexOf(",");
if (commaIndex > 0 && commaIndex < raw.length - 1) {
const provider = raw.slice(0, commaIndex).trim();
const model = raw.slice(commaIndex + 1).trim();
return provider && model ? `${provider}/${model}` : undefined;
}
return raw;
}
function composeRouteTarget(providerValue, modelValue) {
const provider = stringValue(providerValue);
const model = stringValue(modelValue);
if (provider && model) {
return `${provider}/${model}`;
}
return model || provider;
}
function claudeDesignProviderSelectorNames(config) {
const names = new Set();
const providers = claudeDesignProviderConfigs(config);
for (const provider of providers) {
if (!isRecord(provider)) {
continue;
}
const name = stringValue(provider.name);
if (!name) {
continue;
}
addProviderSelectorName(names, name);
addProviderSelectorName(names, provider.provider);
const capabilities = Array.isArray(provider.capabilities) ? provider.capabilities : [];
for (const capability of capabilities) {
if (!isRecord(capability)) {
continue;
}
const protocol = normalizeGatewayProviderProtocol(capability.type || capability.protocol);
if (protocol && stringValue(capability.baseUrl || capability.base_url)) {
addProviderSelectorName(names, `${name}::${protocol}`);
}
}
const credentialProtocols = claudeDesignProviderProtocols(provider);
providerCredentials(provider).forEach((credential, index) => {
if (!providerCredentialApiKey(credential)) {
return;
}
const credentialSlug = providerCredentialSlug(providerCredentialRuntimeId(credential, index));
for (const protocol of credentialProtocols) {
addProviderSelectorName(names, `${name}::${protocol}::cred:${credentialSlug}`);
}
});
}
return names;
}
function defaultClaudeDesignGatewayConfigPath() {
const home = stringValue(process.env.HOME) || stringValue(process.env.USERPROFILE);
return home ? pathModule.join(home, ".claude-code-router", "gateway.config.json") : undefined;
}
function loadClaudeDesignGatewayConfig(file, logger) {
const configFile = stringValue(file);
if (!configFile || !fs.existsSync(configFile)) {
return {};
}
try {
return parseMaybeJson(fs.readFileSync(configFile, "utf8"), {});
} catch (error) {
logger?.warn?.(`Claude Design could not read gateway config ${configFile}: ${error?.message || error}`);
return {};
}
}
function claudeDesignModelSourceConfig(config, gatewayConfig) {
return {
...(isRecord(config) ? config : {}),
Providers: uniqueProviderRecords([
...claudeDesignProviderConfigs(config),
...claudeDesignProviderConfigs(gatewayConfig)
]),
virtualModelProfiles: uniqueVirtualModelProfiles([
...claudeDesignVirtualModelProfilesForConfig(config),
...claudeDesignVirtualModelProfilesForConfig(gatewayConfig)
])
};
}
function claudeDesignProviderConfigs(config) {
if (!isRecord(config)) {
return [];
}
return [
...(Array.isArray(config.Providers) ? config.Providers : []),
...(Array.isArray(config.providers) ? config.providers : [])
].filter(isRecord);
}
function claudeDesignVirtualModelProfilesForConfig(config) {
if (!isRecord(config)) {
return [];
}
return [
...(Array.isArray(config.virtualModelProfiles) ? config.virtualModelProfiles : []),
...(Array.isArray(config.virtual_model_profiles) ? config.virtual_model_profiles : [])
].filter(isRecord);
}
function uniqueProviderRecords(providers) {
const seen = new Set();
const result = [];
for (const provider of providers) {
const name = stringValue(provider.name);
const key = `${name}\n${JSON.stringify(Array.isArray(provider.models) ? provider.models : [])}`;
if (!name || seen.has(key)) {
continue;
}
seen.add(key);
result.push(provider);
}
return result;
}
function uniqueVirtualModelProfiles(profiles) {
const seen = new Set();
const result = [];
for (const profile of profiles) {
const key = stringValue(profile.id) || stringValue(profile.name) || JSON.stringify(profile.match || profile);
if (!key || seen.has(key)) {
continue;
}
seen.add(key);
result.push(profile);
}
return result;
}
function claudeDesignGatewayModelPresets(config, defaultGatewayModel) {
const defaultModel = publicGatewayModelSelector(defaultGatewayModel) || DEFAULT_GATEWAY_MODEL;
const selectors = uniqueCaseInsensitiveStrings([
defaultModel,
...claudeDesignProviderModelSelectors(config),
...claudeDesignVirtualModelSelectors(config),
...DEFAULT_ME.modelPresets.map((preset) => preset.id)
]);
return selectors.map((selector, index) => claudeDesignGatewayModelPreset(selector, index === 0));
}
function claudeDesignProviderModelSelectors(config) {
const providers = claudeDesignProviderConfigs(config);
const selectors = [];
for (const provider of providers) {
if (!Array.isArray(provider.models)) {
continue;
}
const providerSelectors = claudeDesignProviderModelProviderSelectors(provider);
for (const model of provider.models) {
const modelName = stringValue(model);
if (!modelName) {
continue;
}
for (const providerSelector of providerSelectors) {
selectors.push(normalizeRouteTarget(`${providerSelector}/${modelName}`));
}
}
}
return selectors.filter(Boolean);
}
function claudeDesignProviderModelProviderSelectors(provider) {
const selectors = [];
addProviderModelSelector(selectors, publicGatewayProviderName(provider.name));
addProviderModelSelector(selectors, provider.name);
addProviderModelSelector(selectors, provider.provider);
const name = stringValue(provider.name);
const publicName = publicGatewayProviderName(name);
const capabilities = Array.isArray(provider.capabilities) ? provider.capabilities : [];
for (const capability of capabilities) {
const protocol = isRecord(capability) ? normalizeGatewayProviderProtocol(capability.type || capability.protocol) : undefined;
if (publicName && protocol) {
addProviderModelSelector(selectors, `${publicName}::${protocol}`);
}
if (name && protocol) {
addProviderModelSelector(selectors, `${name}::${protocol}`);
}
}
providerCredentials(provider).forEach((credential, index) => {
if (!providerCredentialApiKey(credential)) {
return;
}
const credentialSlug = providerCredentialSlug(providerCredentialRuntimeId(credential, index));
for (const protocol of claudeDesignProviderProtocols(provider)) {
if (publicName) {
addProviderModelSelector(selectors, `${publicName}::${protocol}::cred:${credentialSlug}`);
}
if (name) {
addProviderModelSelector(selectors, `${name}::${protocol}::cred:${credentialSlug}`);
}
}
});
return uniqueCaseInsensitiveStrings(selectors);
}
function addProviderModelSelector(selectors, value) {
const normalized = stringValue(value);
if (normalized) {
selectors.push(normalized);
}
}
function claudeDesignVirtualModelSelectors(config) {
const profiles = claudeDesignVirtualModelProfilesForConfig(config);
const selectors = [];
for (const profile of profiles) {
if (!isRecord(profile) || profile.enabled === false) {
continue;
}
const materialization = isRecord(profile.materialization) ? profile.materialization : {};
if (materialization.enabled === false || materialization.includeInGatewayModels === false) {
continue;
}
const match = isRecord(profile.match) ? profile.match : {};
const aliases = Array.isArray(match.exactAliases) ? match.exactAliases : [];
for (const alias of aliases) {
const normalizedAlias = stringValue(alias);
if (!normalizedAlias) {
continue;
}
selectors.push(normalizedAlias.toLowerCase().startsWith("fusion/") ? normalizedAlias : `Fusion/${normalizedAlias}`);
}
}
return selectors;
}
function claudeDesignGatewayModelPreset(selector, isDefault) {
return {
id: selector,
label: gatewayModelPresetLabel(selector),
maxTokens: 1000000,
supportsAdaptiveThinking: true,
description: isDefault ? "CCR gateway default" : "CCR gateway model"
};
}
function gatewayModelPresetLabel(selector) {
const value = stringValue(selector) || DEFAULT_GATEWAY_MODEL;
const separator = value.indexOf("/");
if (separator <= 0 || separator >= value.length - 1) {
return value;
}
const provider = value.slice(0, separator);
const model = value.slice(separator + 1);
return `${model} (${provider})`;
}
function publicGatewayModelSelector(value) {
const normalized = normalizeRouteTarget(value);
if (!normalized) {
return undefined;
}
const separator = normalized.indexOf("/");
if (separator <= 0 || separator >= normalized.length - 1) {
return normalized;
}
const provider = normalized.slice(0, separator).trim();
const model = normalized.slice(separator + 1).trim();
const protocolSeparator = provider.indexOf("::");
const publicProvider = protocolSeparator > 0 ? provider.slice(0, protocolSeparator) : provider;
return publicProvider && model ? `${publicProvider}/${model}` : normalized;
}
function publicGatewayProviderName(value) {
const name = stringValue(value);
if (!name) {
return undefined;
}
const protocolSeparator = name.indexOf("::");
return protocolSeparator > 0 ? name.slice(0, protocolSeparator) : name;
}
function uniqueCaseInsensitiveStrings(values) {
const seen = new Set();
const result = [];
for (const value of values) {
const normalized = stringValue(value);
if (!normalized) {
continue;
}
const key = normalized.toLowerCase();
if (seen.has(key)) {
continue;
}
seen.add(key);
result.push(normalized);
}
return result;
}
function addProviderSelectorName(names, value) {
const name = stringValue(value);
if (name) {
names.add(name.toLowerCase());
const publicName = publicGatewayProviderName(name);
if (publicName) {
names.add(publicName.toLowerCase());
}
}
}
function claudeDesignProviderProtocols(provider) {
const protocols = [];
const seen = new Set();
const capabilities = Array.isArray(provider.capabilities) ? provider.capabilities : [];
for (const capability of capabilities) {
if (!isRecord(capability)) {
continue;
}
const protocol = normalizeGatewayProviderProtocol(capability.type || capability.protocol);
if (protocol && stringValue(capability.baseUrl || capability.base_url) && !seen.has(protocol)) {
seen.add(protocol);
protocols.push(protocol);
}
}
const direct = normalizeGatewayProviderProtocol(provider.type) ||
normalizeGatewayProviderProtocol(provider.provider) ||
inferGatewayProviderProtocol(provider);
if (direct && !seen.has(direct)) {
protocols.push(direct);
}
return protocols;
}
function normalizeGatewayProviderProtocol(value) {
const normalized = stringValue(value)?.toLowerCase();
if (normalized === "openai" || normalized === "openai_responses") {
return "openai_responses";
}
if (normalized === "openai_chat" || normalized === "openai_chat_completions") {
return "openai_chat_completions";
}
if (normalized === "anthropic" || normalized === "anthropic_messages") {
return "anthropic_messages";
}
if (normalized === "gemini" || normalized === "gemini_generate_content") {
return "gemini_generate_content";
}
return undefined;
}
function inferGatewayProviderProtocol(provider) {
const url = stringValue(provider.baseurl || provider.baseUrl || provider.api_base_url)?.toLowerCase() || "";
const transformerNames = JSON.stringify(provider.transformer ?? "").toLowerCase();
if (url.includes("generativelanguage.googleapis.com") || transformerNames.includes("gemini")) {
return "gemini_generate_content";
}
if (url.includes("anthropic") || transformerNames.includes("anthropic")) {
return "anthropic_messages";
}
return "openai_chat_completions";
}
function providerCredentials(provider) {
return Array.isArray(provider?.credentials)
? provider.credentials.filter((credential) => isRecord(credential) && credential.enabled !== false)
: [];
}
function providerCredentialApiKey(credential) {
return stringValue(credential.api_key) || stringValue(credential.apiKey) || stringValue(credential.apikey);
}
function providerCredentialRuntimeId(credential, index) {
const explicitId = stringValue(credential.id);
if (explicitId) {
return explicitId;
}
const oneBasedIndex = index >= 0 ? index + 1 : 1;
const label = stringValue(credential.name) || stringValue(credential.label);
return label ? `${providerCredentialSlug(label)}-${oneBasedIndex}` : `key-${oneBasedIndex}`;
}
function providerCredentialSlug(value) {
return stringValue(value)
.toLowerCase()
.replace(/[^a-z0-9_.-]+/g, "-")
.replace(/^-+|-+$/g, "") || "key";
}
function positiveNumber(value) {
const number = Number(value);
return Number.isFinite(number) && number > 0 ? number : undefined;
}
function hasWebSearchTool(tools) {
return Array.isArray(tools) && tools.some((tool) => isRecord(tool) && stringValue(tool.type)?.startsWith("web_search"));
}
function hasImageContent(messages) {
return Array.isArray(messages) && messages.some((message) => JSON.stringify(message).includes("\"image\""));
}
function sanitizeRouteId(value) {
return String(value || "")
.toLowerCase()
.replace(/[^a-z0-9._-]+/g, "-")
.replace(/^-+|-+$/g, "")
.slice(0, 80) || "route";
}
function fetchGateway(runtime, path, body, routingDecision, sessionContext) {
const url = new URL(path, runtime.gatewayUrl.replace(/\/+$/, ""));
const headers = {
"anthropic-version": "2023-06-01",
"content-type": "application/json",
"x-ccr-client": "Claude Design",
"x-claude-design-proxy": "1"
};
if (runtime.gatewayApiKey) {
headers.authorization = `Bearer ${runtime.gatewayApiKey}`;
headers["x-api-key"] = runtime.gatewayApiKey;
}
if (routingDecision?.requestedModel) {
headers["x-claude-design-requested-model"] = routingDecision.requestedModel;
}
if (routingDecision?.routedModel) {
headers["x-claude-design-routed-model"] = routingDecision.routedModel;
}
if (routingDecision?.reason) {
headers["x-claude-design-route-reason"] = routingDecision.reason;
}
if (sessionContext?.sessionId) {
headers["x-agent-session-id"] = sessionContext.sessionId;
}
if (sessionContext?.projectId) {
headers["x-claude-design-project-id"] = sessionContext.projectId;
}
if (sessionContext?.chatId) {
headers["x-claude-design-chat-id"] = sessionContext.chatId;
}
return fetch(url, {
body: `${JSON.stringify(body)}\n`,
headers,
method: "POST"
});
}
function claudeDesignSessionContext(value) {
const projectId = stringValue(value?.projectId);
const chatId = stringValue(value?.chatId);
const sessionId = projectId && chatId
? `${projectId}:${chatId}`
: chatId || projectId;
return {
chatId,
projectId,
sessionId
};
}
function readClaudeDesignProjectId(value) {
if (!isRecord(value)) {
return undefined;
}
const metadata = isRecord(value.metadata) ? value.metadata : undefined;
return (
stringValue(value.project_id) ||
stringValue(value.projectId) ||
stringValue(metadata?.project_id) ||
stringValue(metadata?.projectId)
);
}
function readClaudeDesignChatId(value) {
if (!isRecord(value)) {
return undefined;
}
const metadata = isRecord(value.metadata) ? value.metadata : undefined;
return (
stringValue(value.chat_id) ||
stringValue(value.chatId) ||
stringValue(metadata?.chat_id) ||
stringValue(metadata?.chatId) ||
stringValue(value.conversation_id) ||
stringValue(value.conversationId) ||
stringValue(metadata?.conversation_id) ||
stringValue(metadata?.conversationId)
);
}
function extractGatewayAssistantText(payload) {
if (typeof payload === "string") {
return payload;
}
if (!isRecord(payload)) {
return "";
}
if (typeof payload.completion === "string") {
return payload.completion;
}
if (typeof payload.output_text === "string") {
return payload.output_text;
}
if (Array.isArray(payload.content)) {
return textFromMessageContent(payload.content);
}
if (Array.isArray(payload.output)) {
return textFromMessageContent(payload.output);
}
if (Array.isArray(payload.choices)) {
return payload.choices
.map((choice) => {
if (!isRecord(choice)) {
return "";
}
const message = isRecord(choice.message) ? choice.message : {};
const delta = isRecord(choice.delta) ? choice.delta : {};
return textFromMessageContent(message.content) || textFromMessageContent(delta.content) || stringValue(choice.text) || "";
})
.filter(Boolean)
.join("\n");
}
return "";
}
function extractGatewayToolCalls(payload) {
const toolCalls = [];
const addToolCall = (value, fallbackIndex) => {
const toolCall = normalizeGatewayToolCall(value, fallbackIndex);
if (toolCall) {
toolCalls.push(toolCall);
}
};
if (!isRecord(payload)) {
return toolCalls;
}
if (Array.isArray(payload.content)) {
payload.content.forEach(addToolCall);
}
if (Array.isArray(payload.output)) {
payload.output.forEach(addToolCall);
}
if (Array.isArray(payload.tool_calls)) {
payload.tool_calls.forEach(addToolCall);
}
if (Array.isArray(payload.choices)) {
for (const choice of payload.choices) {
if (!isRecord(choice)) {
continue;
}
const message = isRecord(choice.message) ? choice.message : {};
const delta = isRecord(choice.delta) ? choice.delta : {};
if (Array.isArray(message.tool_calls)) {
message.tool_calls.forEach(addToolCall);
}
if (Array.isArray(delta.tool_calls)) {
delta.tool_calls.forEach(addToolCall);
}
if (Array.isArray(message.content)) {
message.content.forEach(addToolCall);
}
}
}
return toolCalls;
}
function normalizeGatewayToolCall(value, fallbackIndex) {
if (!isRecord(value)) {
return undefined;
}
const fn = isRecord(value.function) ? value.function : {};
const name = stringValue(value.name) || stringValue(fn.name) || stringValue(value.tool);
if (!name) {
return undefined;
}
const inputSource = value.input ?? value.arguments ?? fn.arguments ?? {};
const input = normalizeToolInput(inputSource);
return {
id: stringValue(value.id) || `toolu_${String(fallbackIndex || 0)}_${randomUuid().replace(/-/g, "").slice(0, 18)}`,
input: normalizeGatewayToolInput(name, input),
name
};
}
function normalizeGatewayToolInput(name, input) {
if (name === "questions_v2") {
return normalizeQuestionsV2Input(input);
}
if (name === "questions") {
return normalizeLegacyQuestionsInput(input);
}
return input;
}
function absorbQuestionToolCalls(assistantText, toolCalls) {
const kept = [];
const questions = [];
for (const toolCall of Array.isArray(toolCalls) ? toolCalls : []) {
if (isQuestionToolName(toolCall?.name)) {
questions.push(toolCall);
} else {
kept.push(toolCall);
}
}
if (questions.length === 0) {
return {
assistantText,
toolCalls: kept
};
}
const questionText = questionToolCallsToText(questions);
return {
assistantText: [assistantText, questionText].filter(Boolean).join("\n\n"),
toolCalls: kept
};
}
function questionToolCallsToText(toolCalls) {
const summaries = [];
for (const toolCall of Array.isArray(toolCalls) ? toolCalls : []) {
const input = isRecord(toolCall?.input) ? toolCall.input : {};
const title = stringValue(input.title);
const questions = Array.isArray(input.questions) ? input.questions : [];
const questionTitles = questions
.map((question, index) => {
if (!isRecord(question)) {
return "";
}
return stringValue(question.title) || stringValue(question.question) || `Question ${index + 1}`;
})
.filter(Boolean);
if (title || questionTitles.length > 0) {
summaries.push([title, ...questionTitles].filter(Boolean).join(": "));
}
}
const suffix = summaries.length > 0 ? ` (${summaries.join("; ")})` : "";
return `Claude Design questions were skipped; continuing with default answers.${suffix}`;
}
function isQuestionToolName(name) {
return QUESTION_TOOL_NAMES.has(String(name || ""));
}
function normalizeToolInput(value) {
if (typeof value === "string") {
const parsed = parseMaybeJson(value, undefined);
if (isRecord(parsed)) {
return parsed;
}
if (Array.isArray(parsed)) {
return { value: parsed };
}
return value.trim() ? { arguments: value } : {};
}
if (isRecord(value)) {
return value;
}
if (Array.isArray(value)) {
return { value };
}
return value === undefined || value === null ? {} : { value };
}
function normalizeQuestionsV2Input(value) {
const record = isRecord(value) ? value : {};
const rawQuestions = Array.isArray(record.questions) ? record.questions : [];
const questions = rawQuestions.map(normalizeQuestionsV2Question).filter(Boolean);
const title = stringValue(record.title) || "Claude has some questions";
return {
...record,
questions: questions.length > 0 ? questions : [defaultQuestionsV2Question()],
title
};
}
function normalizeLegacyQuestionsInput(value) {
const record = isRecord(value) ? value : {};
const rawQuestions = Array.isArray(record.questions) ? record.questions : [];
return {
...record,
questions: rawQuestions.map((question, index) => {
if (isRecord(question)) {
return {
...question,
options: plainStringArray(question.options),
question: stringValue(question.question) || stringValue(question.title) || `Question ${index + 1}`
};
}
return {
options: [],
question: stringValue(question) || `Question ${index + 1}`
};
})
};
}
function normalizeQuestionsV2Question(question, index) {
const record = isRecord(question) ? question : {};
const options = plainStringArray(record.options);
const title = stringValue(record.title) || stringValue(record.question) || stringValue(record.label) || `Question ${index + 1}`;
let kind = stringValue(record.kind);
if (!["text-options", "svg-options", "slider", "file", "freeform"].includes(kind)) {
if (record.isSvg === true) {
kind = "svg-options";
} else if (options.length > 0) {
kind = "text-options";
} else if (record.min !== undefined || record.max !== undefined || record.step !== undefined) {
kind = "slider";
} else if (stringValue(record.accept)) {
kind = "file";
} else {
kind = "freeform";
}
}
if ((kind === "text-options" || kind === "svg-options") && options.length === 0) {
kind = "freeform";
}
const normalized = {
id: normalizeQuestionId(stringValue(record.id) || stringValue(record.name) || title, index),
kind,
title
};
const subtitle = stringValue(record.subtitle);
if (subtitle) {
normalized.subtitle = subtitle;
}
if (options.length > 0) {
normalized.options = options;
}
if (record.multi === true) {
normalized.multi = true;
}
if (kind === "slider") {
const min = numberValue(record.min);
const max = numberValue(record.max);
const step = numberValue(record.step);
const defaultValue = numberValue(record.default);
if (min !== undefined) {
normalized.min = min;
}
if (max !== undefined) {
normalized.max = max;
}
if (step !== undefined) {
normalized.step = step;
}
if (defaultValue !== undefined) {
normalized.default = defaultValue;
}
}
if (kind === "file") {
const accept = stringValue(record.accept);
if (accept) {
normalized.accept = accept;
}
}
return normalized;
}
function plainStringArray(value) {
if (!Array.isArray(value)) {
return [];
}
return value.map((item) => String(item ?? "").trim()).filter(Boolean);
}
function defaultQuestionsV2Question() {
return {
id: "details",
kind: "freeform",
title: "What should Claude know before continuing?"
};
}
function normalizeQuestionId(value, index) {
const normalized = stringValue(value)
.toLowerCase()
.replace(/[^a-z0-9]+/g, "_")
.replace(/^_+|_+$/g, "")
.slice(0, 64);
return normalized || `question_${index + 1}`;
}
function extractGatewayStopReason(payload, fallback) {
if (!isRecord(payload)) {
return fallback;
}
const direct = stringValue(payload.stop_reason) || stringValue(payload.stopReason);
if (direct) {
return direct;
}
if (Array.isArray(payload.choices)) {
for (const choice of payload.choices) {
const mapped = mapGatewayFinishReason(isRecord(choice) ? stringValue(choice.finish_reason) || stringValue(choice.finishReason) : undefined);
if (mapped) {
return mapped;
}
}
}
return fallback;
}
function mapGatewayFinishReason(reason) {
switch (reason) {
case "tool_calls":
case "function_call":
return "tool_use";
case "length":
return "max_tokens";
case "stop":
return "end_turn";
default:
return reason;
}
}
function textFromMessageContent(content) {
if (typeof content === "string") {
return content;
}
if (!Array.isArray(content)) {
return "";
}
return content
.map((block) => {
if (typeof block === "string") {
return block;
}
if (!isRecord(block)) {
return "";
}
if (block.type === "tool_use" || block.type === "function_call" || isToolResultContentBlock(block)) {
return "";
}
return stringValue(block.text) || stringValue(block.content) || stringValue(block.output_text) || "";
})
.filter(Boolean)
.join("\n");
}
function gatewayMessageContent(assistantText, toolCalls) {
const content = [];
if (assistantText) {
content.push({ text: assistantText, type: "text" });
}
for (const toolCall of toolCalls) {
content.push({
id: toolCall.id,
input: toolCall.input || {},
name: toolCall.name,
type: "tool_use"
});
}
return content;
}
function gatewayContentBlocks(assistantText, toolCalls) {
const blocks = [];
if (assistantText) {
blocks.push({ text: assistantText, type: "text" });
}
for (const toolCall of toolCalls) {
blocks.push({
toolCall: {
id: toolCall.id,
input: toolCall.input || {},
name: toolCall.name,
serverSide: false,
type: gatewayToolKind(toolCall.name)
},
type: "tool_call"
});
}
return blocks;
}
function sanitizeStoredMessages(messages) {
if (!Array.isArray(messages)) {
return [];
}
return dedupeStoredMessages(messages
.map(sanitizeStoredMessage)
.filter(isVisibleStoredMessage));
}
function dedupeStoredMessages(messages) {
const result = [];
let previousKey;
for (const message of messages) {
const key = storedMessageDedupeKey(message);
if (key && key === previousKey) {
continue;
}
result.push(message);
previousKey = key;
}
return result;
}
function storedMessageDedupeKey(message) {
if (!isRecord(message)) {
return `raw:${JSON.stringify(message)}`;
}
const role = stringValue(message.role) || "";
const chatId = stringValue(message.chat_id) || stringValue(message.chatId) || "";
const text = role === "user" ? cleanVisibleUserText(storedMessageDisplayText(message)) : storedMessageDisplayText(message);
const blocks = Array.isArray(message.contentBlocks)
? message.contentBlocks
: Array.isArray(message.content_blocks)
? message.content_blocks
: [];
return JSON.stringify({
blocks: role === "user" ? [] : blocks,
chatId,
role,
text
});
}
function sanitizeStoredMessage(message) {
if (!isRecord(message)) {
return message;
}
const sourceBlocks = Array.isArray(message.contentBlocks)
? message.contentBlocks
: Array.isArray(message.content_blocks)
? message.content_blocks
: [];
if (sourceBlocks.length === 0) {
return sanitizeStoredMessageDisplayContent(message);
}
const keptBlocks = [];
const questionBlocks = [];
for (const block of sourceBlocks) {
if (isQuestionContentBlock(block)) {
questionBlocks.push(block);
} else {
keptBlocks.push(block);
}
}
if (questionBlocks.length === 0) {
return sanitizeStoredMessageDisplayContent(message);
}
const existingContent = stringValue(message.content);
const content = existingContent || questionToolBlocksToText(questionBlocks);
const blocks = keptBlocks.length > 0
? keptBlocks
: content
? [{ text: content, type: "text" }]
: [];
return sanitizeStoredMessageDisplayContent({
...message,
content,
contentBlocks: blocks,
content_blocks: blocks
});
}
function isQuestionContentBlock(block) {
if (!isRecord(block)) {
return false;
}
if (isQuestionToolName(block.name)) {
return true;
}
const toolCall = isRecord(block.toolCall) ? block.toolCall : isRecord(block.tool_call) ? block.tool_call : undefined;
if (toolCall && isQuestionToolName(toolCall.name)) {
return true;
}
return false;
}
function questionToolBlocksToText(blocks) {
const toolCalls = [];
for (const block of Array.isArray(blocks) ? blocks : []) {
if (!isRecord(block)) {
continue;
}
const toolCall = isRecord(block.toolCall) ? block.toolCall : isRecord(block.tool_call) ? block.tool_call : block;
toolCalls.push({
input: isRecord(toolCall.input) ? toolCall.input : {},
name: stringValue(toolCall.name) || "questions_v2"
});
}
return questionToolCallsToText(toolCalls);
}
function isToolResultOnlyMessage(message) {
if (!isRecord(message)) {
return false;
}
const role = stringValue(message.role);
if (role && role !== "user" && role !== "tool") {
return false;
}
const content = message.content;
if (Array.isArray(content) && content.length > 0) {
return content.every(isToolResultContentBlock);
}
const blocks = Array.isArray(message.contentBlocks)
? message.contentBlocks
: Array.isArray(message.content_blocks)
? message.content_blocks
: [];
if (blocks.length > 0) {
return blocks.every(isToolResultContentBlock);
}
return role === "tool";
}
function isToolResultContentBlock(block) {
if (!isRecord(block)) {
return false;
}
const type = stringValue(block.type);
return (
type === "tool_result" ||
type === "tool_result_error" ||
type === "function_result" ||
isRecord(block.toolResult) ||
isRecord(block.tool_result)
);
}
function sanitizeStoredMessageDisplayContent(message) {
if (!isRecord(message)) {
return message;
}
const role = stringValue(message.role);
if (role === "user") {
const content = cleanVisibleUserText(storedProjectMessageText(message));
const sanitized = {
...message,
content
};
if (Array.isArray(message.contentBlocks) || Array.isArray(message.content_blocks)) {
const blocks = content ? [{ text: content, type: "text" }] : [];
sanitized.contentBlocks = blocks;
sanitized.content_blocks = blocks;
}
return sanitized;
}
if (role === "assistant") {
const sourceBlocks = Array.isArray(message.contentBlocks)
? message.contentBlocks
: Array.isArray(message.content_blocks)
? message.content_blocks
: [];
if (sourceBlocks.length === 0) {
return message;
}
const blocks = sourceBlocks.filter((block) => !isToolResultContentBlock(block));
if (blocks.length === sourceBlocks.length) {
return message;
}
const content = stringValue(message.content) || textFromMessageContent(blocks);
return {
...message,
content,
contentBlocks: blocks,
content_blocks: blocks
};
}
return message;
}
function isVisibleStoredMessage(message) {
if (!isRecord(message)) {
return true;
}
if (isToolResultOnlyMessage(message)) {
return false;
}
const role = stringValue(message.role);
const text = storedMessageDisplayText(message);
if (isInternalChatStatusText(text)) {
return false;
}
if (role === "user") {
return Boolean(cleanVisibleUserText(text));
}
if (role === "tool") {
return false;
}
if (role === "assistant" && !text) {
const blocks = Array.isArray(message.contentBlocks)
? message.contentBlocks
: Array.isArray(message.content_blocks)
? message.content_blocks
: [];
return blocks.some((block) => isToolCallContentBlock(block) || !isToolResultContentBlock(block));
}
return true;
}
function storedMessageDisplayText(message) {
if (!isRecord(message)) {
return "";
}
return storedProjectMessageText(message);
}
function cleanVisibleUserText(text) {
let value = (stringValue(text) || "")
.replace(/<system-info\b[^>]*>[\s\S]*?<\/system-info>/gi, "\n")
.replace(/<default\s+aesthetic_system_instructions\b[^>]*>[\s\S]*?<\/default\s+aesthetic_system_instructions>/gi, "\n")
.replace(/<system-reminder\b[^>]*>[\s\S]*?<\/system-reminder>/gi, "\n")
.replace(/\s*\[id:m[0-9a-z_-]+\]\s*$/i, "")
.replace(/[ \t]+\n/g, "\n")
.replace(/\n{3,}/g, "\n\n")
.trim();
if (isInternalChatStatusText(value)) {
value = "";
}
return value;
}
function isInternalChatStatusText(text) {
const normalized = (stringValue(text) || "").replace(/\s+/g, " ").trim();
return (
/^Questions timed out;\s*go with defaults\.?$/i.test(normalized) ||
/^Claude Design questions were skipped;\s*continuing with default answers\./i.test(normalized) ||
/^Wrote \d+ characters to .+$/i.test(normalized) ||
/^Opened .+ for the user\./i.test(normalized) ||
/^Error:\s*(screenshot capture failed:)?\s*Error at step \d+:/i.test(normalized) ||
/^Error:\s*\[internal\]/i.test(normalized) ||
/^--- Script output ---/i.test(normalized) ||
/^(Edited|Created|Deleted|Renamed|Moved|Copied) .+/i.test(normalized) ||
/No preview pane available/i.test(normalized) ||
/^\[File:\s+.+\]/i.test(normalized) ||
/^\(no webview logs\)$/i.test(normalized) ||
/^Make sure your design supports Tweaks\./i.test(normalized) ||
/^Verifier subagent forked\b/i.test(normalized) ||
/^=== FORK BOUNDARY ===/i.test(normalized)
);
}
function isToolCallContentBlock(block) {
if (!isRecord(block)) {
return false;
}
const type = stringValue(block.type);
return type === "tool_call" || type === "tool_use" || type === "function_call" || isRecord(block.toolCall) || isRecord(block.tool_call);
}
function gatewayToolKind(name) {
if (name === "Read") {
return "read";
}
if (String(name || "").startsWith("mcp__")) {
return "mcp";
}
return "edit";
}
function appendChatExchange(runtime, request, gatewayBody, assistantText, assistantMessageId, contentBlocks) {
const row = getProjectRow(runtime, request.projectId);
if (!row) {
return;
}
const parsedMessages = parseMaybeJson(row.messages_json, []);
const messages = Array.isArray(parsedMessages) ? parsedMessages : [];
const assistantContentBlocks = Array.isArray(contentBlocks) ? contentBlocks : gatewayContentBlocks(assistantText, []);
const now = new Date().toISOString();
const lastUserText = cleanVisibleUserText(textFromMessageContent(lastGatewayUserMessage(gatewayBody)?.content));
if (lastUserText) {
const lastMessage = messages[messages.length - 1];
if (!lastMessage || lastMessage.role !== "user" || lastMessage.chat_id !== request.chatId || lastMessage.content !== lastUserText) {
messages.push({
chat_id: request.chatId,
content: lastUserText,
created_at: now,
id: randomUuid(),
role: "user"
});
}
}
const assistantMessage = {
chat_id: request.chatId,
content: assistantText,
contentBlocks: assistantContentBlocks,
content_blocks: assistantContentBlocks,
created_at: now,
id: assistantMessageId,
role: "assistant"
};
const existingAssistantIndex = messages.findIndex((message) => message?.id === assistantMessageId);
if (existingAssistantIndex >= 0) {
messages[existingAssistantIndex] = assistantMessage;
} else {
messages.push(assistantMessage);
}
runtime.store.database.run("UPDATE claude_design_items SET updated_at = ?, messages_json = ? WHERE collection = ? AND uuid = ?", [
now,
JSON.stringify(messages),
PROJECT_COLLECTION,
row.uuid
]);
appendChatExchangeToProjectData(runtime, row, request, lastUserText, assistantText, assistantMessageId, assistantContentBlocks, now);
runtime.store.persist();
}
function lastGatewayUserMessage(gatewayBody) {
if (!isRecord(gatewayBody) || !Array.isArray(gatewayBody.messages)) {
return undefined;
}
let sawNonHumanTail = false;
for (let index = gatewayBody.messages.length - 1; index >= 0; index--) {
const message = gatewayBody.messages[index];
if (!isRecord(message)) {
continue;
}
const role = stringValue(message.role);
if (role === "user") {
if (isToolResultOnlyMessage(message)) {
sawNonHumanTail = true;
continue;
}
if (sawNonHumanTail) {
return undefined;
}
return message;
}
if (role === "assistant" || role === "tool") {
sawNonHumanTail = true;
}
}
return undefined;
}
function appendChatExchangeToProjectData(runtime, row, request, userText, assistantText, assistantMessageId, contentBlocks, now) {
saveProjectData(runtime, row.uuid, (record, currentRow) => {
const base64 = stringValue(record.project_data_base64);
const decoded = base64 ? parseMaybeJson(Buffer.from(base64, "base64").toString("utf8"), {}) : record;
const normalized = normalizeProjectStoreData(runtime, currentRow || row, decoded);
const chatId = request.chatId || stringValue(normalized.viewState?.activeChatId) || defaultProjectChatId(row.uuid);
const chats = { ...(isRecord(normalized.chats) ? normalized.chats : {}) };
const chat = normalizeProjectChat(chats[chatId] || {}, chatId, normalized.created, now);
const chatMessages = Array.isArray(chat.messages) ? [...chat.messages] : [];
if (userText) {
const lastMessage = chatMessages[chatMessages.length - 1];
if (!lastMessage || lastMessage.role !== "user" || lastMessage.content !== userText) {
chatMessages.push({
content: userText,
id: randomUuid(),
role: "user",
timestamp: now
});
}
}
const assistantMessage = {
content: assistantText,
contentBlocks,
id: assistantMessageId,
role: "assistant",
timestamp: now
};
const existingAssistantIndex = chatMessages.findIndex((message) => message?.id === assistantMessageId);
if (existingAssistantIndex >= 0) {
chatMessages[existingAssistantIndex] = assistantMessage;
} else {
chatMessages.push(assistantMessage);
}
chats[chatId] = {
...chat,
lastOpened: now,
messages: chatMessages
};
const nextProjectData = {
...normalized,
chats,
lastOpened: now,
viewState: {
...normalized.viewState,
activeChatId: chatId
}
};
return {
...record,
project_data_base64: Buffer.from(JSON.stringify(nextProjectData), "utf8").toString("base64")
};
});
}
function estimateTokenCount(body) {
return Math.max(1, Math.ceil(JSON.stringify(body || {}).length / 4));
}
function encodeChatResponseTextDelta(text) {
return protoMessage(2, protoString(1, text));
}
function encodeChatResponseMessageStart(messageId) {
return protoMessage(3, protoString(1, messageId));
}
function encodeChatResponseMessageStop(stopReason) {
return protoMessage(4, protoString(1, stopReason || "end_turn"));
}
function encodeChatResponseError(message) {
return protoMessage(5, protoString(1, message || "Unknown gateway error."));
}
function encodeChatResponseRaw(eventType, payload) {
return protoMessage(6, Buffer.concat([protoString(1, eventType), protoBytes(2, Buffer.from(JSON.stringify(payload), "utf8"), true)]));
}
function connectStreamResponse(messages) {
const body = Buffer.concat([...messages.map((message) => connectEnvelope(0, message)), connectEnvelope(0x02, Buffer.from("{}", "utf8"))]);
return binaryResponse(200, body, {
"cache-control": "no-store",
"connect-content-encoding": "identity",
"connect-protocol-version": "1",
"content-type": "application/connect+proto"
});
}
function connectEnvelope(flags, data) {
const payload = Buffer.isBuffer(data) ? data : Buffer.from(data || []);
const header = Buffer.alloc(5);
header[0] = flags;
header.writeUInt32BE(payload.length, 1);
return Buffer.concat([header, payload]);
}
function decodeConnectEnvelope(buffer) {
if (!Buffer.isBuffer(buffer) || buffer.length < 5) {
return buffer;
}
const frames = [];
let offset = 0;
while (offset + 5 <= buffer.length) {
const flags = buffer[offset];
const length = buffer.readUInt32BE(offset + 1);
const start = offset + 5;
const end = start + length;
if (end > buffer.length) {
return frames[0] || buffer;
}
if ((flags & 0x02) === 0 && length > 0) {
frames.push(buffer.subarray(start, end));
}
offset = end;
}
if (frames.length === 0) {
return Buffer.alloc(0);
}
if (frames.length === 1) {
return frames[0];
}
return Buffer.concat(frames);
}
function omeletteProjectFromRow(row, me) {
const data = parseMaybeJson(row.data_json, {});
const uuid = stringValue(data.project_id) || stringValue(data.uuid) || row.uuid;
const name = stringValue(data.name) || stringValue(data.title) || row.title || "Untitled design";
return {
canEdit: data.can_edit !== false,
createdAt: row.created_at,
description: stringValue(data.description) || "",
designSystems: Array.isArray(data.design_systems) ? data.design_systems : [],
introText: stringValue(data.intro_text) || "",
isFavorite: data.is_favorite === true,
isOwned: data.is_owned !== false,
name,
ownerDisplayName: stringValue(data.owner_display_name) || me.displayName,
ownerEmail: stringValue(data.owner_email) || me.email,
ownerUuid: stringValue(data.owner_uuid) || me.accountUuid,
publishedAt: stringValue(data.published_at) || "",
projectId: uuid,
sharing: isRecord(data.sharing) ? data.sharing : {},
templateTitle: stringValue(data.template_title) || "",
type: normalizeProjectTypeNumber(data.project_type ?? data.type),
updatedAt: row.updated_at,
uuid,
viewedAt: stringValue(data.viewed_at) || row.updated_at
};
}
function decodeCreateProjectRequest(buffer) {
return {
description: decodeProtoStringField(buffer, 5),
introText: decodeProtoStringField(buffer, 6),
name: decodeProtoStringField(buffer, 1),
templateId: decodeProtoStringField(buffer, 9),
templateTitle: decodeProtoStringField(buffer, 4),
type: decodeProtoEnumField(buffer, 3)
};
}
function encodeCreateProjectName(name) {
return protoString(1, name);
}
function encodeCreateProjectResponse(projectId) {
return protoString(1, projectId);
}
function encodeListProjectsResponse(runtime) {
const typeFilter = arguments.length > 1 ? arguments[1] : undefined;
const publishedOnly = arguments.length > 2 ? arguments[2] : undefined;
const items = listOmeletteProjects(runtime, typeFilter, publishedOnly).map((project) => protoMessage(1, encodeProjectListItem(project)));
return Buffer.concat(items);
}
function encodeProjectListItem(project) {
return Buffer.concat([
protoString(1, project.projectId),
protoString(2, project.name),
protoTimestamp(3, project.viewedAt),
protoString(4, project.ownerUuid),
protoString(5, project.ownerEmail),
protoBool(6, project.isOwned),
protoMessage(7, encodeSharing(project.sharing)),
protoEnum(8, project.type),
protoTimestamp(9, project.publishedAt),
protoString(10, project.templateTitle),
protoString(11, project.description),
protoString(12, project.introText),
protoTimestamp(14, project.updatedAt),
protoString(15, project.ownerDisplayName),
protoBool(17, project.isFavorite),
protoBool(18, project.canEdit),
...((Array.isArray(project.designSystems) ? project.designSystems : []).map((binding) => protoMessage(19, encodeDesignSystemBinding(binding))))
]);
}
function encodeGetProjectResponse(project, me, projectDataBytes) {
return Buffer.concat([
protoString(1, project.projectId),
protoString(2, project.name),
protoString(3, project.ownerUuid || me.accountUuid),
protoString(4, project.ownerEmail || me.email),
protoTimestamp(5, project.createdAt),
protoTimestamp(6, project.updatedAt),
protoMessage(7, encodeSharing(project.sharing)),
protoBytes(9, projectDataBytes || Buffer.alloc(0), true),
protoEnum(10, project.type),
protoTimestamp(11, project.publishedAt),
protoString(12, project.templateTitle),
protoString(13, project.description),
protoString(14, project.introText),
protoString(18, project.ownerDisplayName || me.displayName),
...((Array.isArray(project.designSystems) ? project.designSystems : []).map((binding) => protoMessage(19, encodeDesignSystemBinding(binding))))
]);
}
function encodeGetProjectDataResponse(data) {
return protoBytes(1, data || Buffer.alloc(0), true);
}
function encodeSharing(sharing) {
const record = isRecord(sharing) ? sharing : {};
return Buffer.concat([
protoString(1, stringValue(record.view_mode) || stringValue(record.viewMode) || "private"),
protoBool(2, record.team_can_edit === true || record.teamCanEdit === true),
protoBool(3, record.team_can_comment === true || record.teamCanComment === true)
]);
}
function normalizeProjectTypeNumber(value) {
if (typeof value === "string") {
const normalized = value.trim().toLowerCase();
if (normalized === "template") {
return 2;
}
if (normalized === "design_system" || normalized === "design-system") {
return 3;
}
if (normalized === "project") {
return PROJECT_TYPE_PROJECT;
}
}
const number = Number(value);
return [1, 2, 3].includes(number) ? number : PROJECT_TYPE_PROJECT;
}
function protoString(fieldNumber, value) {
const text = stringValue(value);
if (!text) {
return Buffer.alloc(0);
}
return protoBytes(fieldNumber, Buffer.from(text, "utf8"), true);
}
function protoBytes(fieldNumber, value, includeEmpty) {
const payload = Buffer.isBuffer(value) ? value : Buffer.from(value || []);
if (!includeEmpty && payload.length === 0) {
return Buffer.alloc(0);
}
return Buffer.concat([protoTag(fieldNumber, 2), protoVarint(payload.length), payload]);
}
function protoBool(fieldNumber, value) {
return value === true ? Buffer.concat([protoTag(fieldNumber, 0), protoVarint(1)]) : Buffer.alloc(0);
}
function protoEnum(fieldNumber, value) {
const number = Number(value);
return Number.isFinite(number) && number !== 0
? Buffer.concat([protoTag(fieldNumber, 0), protoVarint(number)])
: Buffer.alloc(0);
}
function protoInt32(fieldNumber, value) {
const number = Number(value);
return Number.isFinite(number) && number !== 0
? Buffer.concat([protoTag(fieldNumber, 0), protoVarint(number)])
: Buffer.alloc(0);
}
function protoInt64(fieldNumber, value) {
const number = Number(value);
return Number.isFinite(number) && number !== 0
? Buffer.concat([protoTag(fieldNumber, 0), protoVarint(number)])
: Buffer.alloc(0);
}
function protoMessage(fieldNumber, body) {
const payload = Buffer.isBuffer(body) ? body : Buffer.from(body || []);
return Buffer.concat([protoTag(fieldNumber, 2), protoVarint(payload.length), payload]);
}
function protoTimestamp(fieldNumber, value) {
if (!value) {
return Buffer.alloc(0);
}
const date = new Date(value || Date.now());
const milliseconds = Number.isFinite(date.getTime()) ? date.getTime() : Date.now();
const seconds = Math.floor(milliseconds / 1000);
return protoMessage(fieldNumber, Buffer.concat([protoTag(1, 0), protoVarint(seconds)]));
}
function protoTag(fieldNumber, wireType) {
return protoVarint((Number(fieldNumber) << 3) | Number(wireType));
}
function protoVarint(value) {
let number = typeof value === "bigint" ? value : BigInt(Math.max(0, Math.trunc(Number(value) || 0)));
const bytes = [];
while (number >= 0x80n) {
bytes.push(Number((number & 0x7fn) | 0x80n));
number >>= 7n;
}
bytes.push(Number(number));
return Buffer.from(bytes);
}
function decodeProtoStringField(buffer, fieldNumber) {
const field = readProtoField(buffer, fieldNumber);
return field?.wireType === 2 ? stringValue(field.value.toString("utf8")) : undefined;
}
function decodeProtoStringFields(buffer, fieldNumber) {
return readProtoFields(buffer, fieldNumber)
.filter((field) => field.wireType === 2)
.map((field) => field.value.toString("utf8"))
.filter((value) => value.length > 0);
}
function decodeAllProtoStrings(buffer) {
if (!Buffer.isBuffer(buffer)) {
return [];
}
const values = [];
forEachProtoField(buffer, (field) => {
if (field.wireType !== 2 || !Buffer.isBuffer(field.value) || field.value.length === 0) {
return;
}
const value = field.value.toString("utf8");
if (isPlausibleProtoString(value)) {
values.push({
fieldNumber: field.fieldNumber,
value
});
}
});
return values;
}
function isPlausibleProtoString(value) {
if (!value) {
return false;
}
let printable = 0;
for (const char of value) {
const code = char.charCodeAt(0);
if (code === 9 || code === 10 || code === 13 || (code >= 32 && code !== 127)) {
printable += 1;
}
}
return printable / value.length > 0.85;
}
function firstUuidLikeString(buffer) {
return decodeAllProtoStrings(buffer)
.map((item) => item.value)
.find((value) => /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value));
}
function decodeProtoBytesField(buffer, fieldNumber) {
const field = readProtoField(buffer, fieldNumber);
return field?.wireType === 2 ? field.value : undefined;
}
function decodeProtoMessageFields(buffer, fieldNumber) {
return readProtoFields(buffer, fieldNumber)
.filter((field) => field.wireType === 2)
.map((field) => field.value);
}
function decodeProtoEnumField(buffer, fieldNumber) {
const field = readProtoField(buffer, fieldNumber);
return field?.wireType === 0 ? Number(field.value) : undefined;
}
function decodeProtoIntField(buffer, fieldNumber) {
const field = readProtoField(buffer, fieldNumber);
return field?.wireType === 0 ? Number(field.value) : 0;
}
function decodeProtoBoolField(buffer, fieldNumber) {
const field = readProtoField(buffer, fieldNumber);
return field?.wireType === 0 ? Number(field.value) !== 0 : false;
}
function readProtoFields(buffer, targetFieldNumber) {
if (!Buffer.isBuffer(buffer)) {
return [];
}
const matches = [];
forEachProtoField(buffer, (field) => {
if (field.fieldNumber === targetFieldNumber) {
matches.push({
value: field.value,
wireType: field.wireType
});
}
});
return matches;
}
function readProtoField(buffer, targetFieldNumber) {
return readProtoFields(buffer, targetFieldNumber)[0];
}
function forEachProtoField(buffer, visitor) {
if (!Buffer.isBuffer(buffer)) {
return;
}
let offset = 0;
while (offset < buffer.length) {
const tag = readProtoVarint(buffer, offset);
if (!tag) {
return;
}
offset = tag.offset;
const fieldNumber = Number(tag.value >> 3n);
const wireType = Number(tag.value & 0x07n);
if (wireType === 0) {
const value = readProtoVarint(buffer, offset);
if (!value) {
return;
}
offset = value.offset;
visitor({ fieldNumber, value: value.value, wireType });
continue;
}
if (wireType === 1) {
if (offset + 8 > buffer.length) {
return;
}
const value = buffer.subarray(offset, offset + 8);
offset += 8;
visitor({ fieldNumber, value, wireType });
continue;
}
if (wireType === 2) {
const length = readProtoVarint(buffer, offset);
if (!length) {
return;
}
offset = length.offset;
const end = offset + Number(length.value);
if (end > buffer.length) {
return;
}
const value = buffer.subarray(offset, end);
offset = end;
visitor({ fieldNumber, value, wireType });
continue;
}
if (wireType === 5) {
if (offset + 4 > buffer.length) {
return;
}
const value = buffer.subarray(offset, offset + 4);
offset += 4;
visitor({ fieldNumber, value, wireType });
continue;
}
return;
}
}
function readProtoVarint(buffer, offset) {
let result = 0n;
let shift = 0n;
for (let index = offset; index < buffer.length; index += 1) {
const byte = buffer[index];
result |= BigInt(byte & 0x7f) << shift;
if ((byte & 0x80) === 0) {
return {
offset: index + 1,
value: result
};
}
shift += 7n;
if (shift > 70n) {
return undefined;
}
}
return undefined;
}
function completionPayload(itemUuid) {
return {
completion: {
id: randomUuid(),
item_uuid: itemUuid,
role: "assistant",
text: "Claude Design mock response.",
type: "message"
},
done: true,
id: randomUuid(),
stop_reason: "end_turn"
};
}
function sseCompletionResponse(itemUuid) {
const payload = completionPayload(itemUuid);
return textResponse(
200,
[
`event: message`,
`data: ${JSON.stringify({ type: "message", message: payload.completion })}`,
"",
"event: done",
"data: {}",
"",
""
].join("\n"),
{
"cache-control": "no-cache",
"connection": "keep-alive",
"content-type": "text/event-stream; charset=utf-8"
}
);
}
function readStoredResponse(store, method, path) {
const row = queryRows(
store.database,
"SELECT status, headers_json, body FROM claude_design_responses WHERE method = ? AND path = ? LIMIT 1",
[method, path]
)[0];
if (!row) {
return undefined;
}
const headers = parseMaybeJson(row.headers_json, {});
const contentType = stringValue(headers["content-type"]) || stringValue(headers["Content-Type"]) || "application/json; charset=utf-8";
const bodyText = String(row.body || "");
if (contentType.includes("application/json")) {
return jsonResponse(Number(row.status) || 200, parseMaybeJson(bodyText, {}), headers);
}
return textResponse(Number(row.status) || 200, bodyText, headers);
}
function upsertStoredResponse(store, method, path, status, headers, body) {
const bodyText = typeof body === "string" ? body : JSON.stringify(body);
store.database.run(
"INSERT OR REPLACE INTO claude_design_responses (method, path, status, headers_json, body, updated_at) VALUES (?, ?, ?, ?, ?, ?)",
[method, path, status, JSON.stringify(headers), bodyText, new Date().toISOString()]
);
store.persist();
}
function listStoredResponses(store) {
return queryRows(
store.database,
"SELECT method, path, status, headers_json, body, updated_at FROM claude_design_responses ORDER BY updated_at DESC",
[]
).map((row) => ({
body: parseMaybeJson(row.body, row.body),
headers: parseMaybeJson(row.headers_json, {}),
method: row.method,
path: row.path,
status: row.status,
updatedAt: row.updated_at
}));
}
function readCachedAsset(store, path) {
const row = queryRows(
store.database,
"SELECT path, upstream_url, content_type, body_base64, fetched_at FROM claude_design_assets WHERE path = ? LIMIT 1",
[path]
)[0];
if (!row) {
return undefined;
}
return {
bodyBase64: row.body_base64,
contentType: row.content_type,
fetchedAt: row.fetched_at,
path: row.path,
upstreamUrl: row.upstream_url
};
}
function isFallbackAsset(asset) {
return stringValue(asset?.upstreamUrl)?.startsWith("fallback:") === true;
}
function isReusableFallbackAsset(asset, path) {
const upstreamUrl = stringValue(asset?.upstreamUrl);
if (!upstreamUrl?.startsWith(FALLBACK_ASSET_CACHE_PREFIX) || !isCacheableFallbackAsset(path)) {
return false;
}
const fetchedAt = Date.parse(stringValue(asset?.fetchedAt) || "");
return Number.isFinite(fetchedAt) && Date.now() - fetchedAt < FALLBACK_ASSET_CACHE_TTL_MS;
}
function writeCachedAsset(store, path, upstreamUrl, contentType, body) {
store.database.run(
"INSERT OR REPLACE INTO claude_design_assets (path, upstream_url, content_type, body_base64, fetched_at) VALUES (?, ?, ?, ?, ?)",
[path, upstreamUrl, contentType || guessContentType(path), body.toString("base64"), new Date().toISOString()]
);
store.persist();
}
function deleteCachedAsset(store, path) {
store.database.run("DELETE FROM claude_design_assets WHERE path = ?", [path]);
store.persist();
}
function purgeBadCachedAssets(store) {
store.database.run(`
DELETE FROM claude_design_assets
WHERE (upstream_url LIKE 'fallback:%' AND upstream_url NOT LIKE ?)
OR body_base64 = ''
OR (lower(content_type) LIKE '%text/html%' AND lower(path) NOT LIKE '%.html')
`, [`${FALLBACK_ASSET_CACHE_PREFIX}%`]);
store.persist();
}
function listCachedAssets(store) {
return queryRows(
store.database,
"SELECT path, upstream_url, content_type, length(body_base64) AS body_base64_length, fetched_at FROM claude_design_assets ORDER BY fetched_at DESC",
[]
).map((row) => ({
bodyBase64Length: row.body_base64_length,
contentType: row.content_type,
fetchedAt: row.fetched_at,
path: row.path,
upstreamUrl: row.upstream_url
}));
}
function readLocalAsset(assetDir, requestPath) {
if (!assetDir) {
return undefined;
}
const localRoot = localAssetRootInfo(assetDir);
for (const relativePath of localAssetRelativePathCandidates(localRoot, requestPath)) {
if (!relativePath || relativePath.includes("\0")) {
continue;
}
const file = pathModule.resolve(localRoot.root, relativePath);
if (file !== localRoot.root && !file.startsWith(`${localRoot.root}${pathModule.sep}`)) {
continue;
}
if (!fs.existsSync(file) || !fs.statSync(file).isFile()) {
continue;
}
return {
body: fs.readFileSync(file),
contentType: guessContentType(file),
source: file
};
}
return undefined;
}
function localAssetRelativePathCandidates(localRoot, requestPath) {
const normalizedPath = normalizePath(requestPath);
if (localRoot.kind === "claude-app-ion-dist") {
if (normalizedPath.startsWith("/design/assets/")) {
return [`assets/${normalizedPath.slice("/design/assets/".length)}`];
}
if (normalizedPath.startsWith("/assets/")) {
return [normalizedPath.slice(1)];
}
if (normalizedPath === "/design" || normalizedPath === "/design/") {
return ["index.html"];
}
if (normalizedPath.startsWith("/design/")) {
return [normalizedPath.slice("/design/".length)];
}
if (isClaudeAppStaticRoutePath(normalizedPath)) {
return [normalizedPath.slice(1)];
}
return [];
}
return [
normalizedPath
.replace(/^\/design\/assets\//, "")
.replace(/^\/assets\//, "")
.replace(/^\/design\//, "")
];
}
function isClaudeAppStaticRoutePath(path) {
return path === "/favicon.ico" ||
path === "/manifest.json" ||
path === "/robots.txt" ||
path === "/frame-shell.html" ||
path.startsWith("/assets/") ||
path.startsWith("/images/") ||
path.startsWith("/i18n/") ||
path.startsWith("/captions/") ||
path.startsWith("/descriptions/");
}
function isDesignStaticAsset(path) {
return /^\/design\/[^/?#]+\.(?:avif|gif|html|ico|jpeg|jpg|json|png|svg|webp|woff2?)$/i.test(path);
}
function isClaudeAppSpaRoute(path) {
return CLAUDE_APP_SPA_ROUTE_PATHS.includes(path) ||
CLAUDE_APP_SPA_ROUTE_PATHS.includes(path.replace(/\/$/, ""));
}
function isClaudeAppDesignIframeRequest(url, request) {
if (url.searchParams.get(CLAUDE_APP_DESIGN_IFRAME_QUERY) === "1") {
return true;
}
const fetchDest = (stringValue(headerValue(request?.headers?.["sec-fetch-dest"])) || "").toLowerCase();
if (fetchDest === "iframe" || fetchDest === "frame") {
return true;
}
return refererIsClaudeAppDesignShell(request?.headers?.referer);
}
function refererIsClaudeAppDesignShell(value) {
const referer = stringValue(value);
if (!referer) {
return false;
}
try {
const parsed = new URL(referer, "https://claude.ai");
return isClaudeAppSpaRoute(normalizePath(parsed.pathname)) &&
parsed.searchParams.has(CLAUDE_APP_DESIGN_PATH_QUERY);
} catch {
return false;
}
}
function claudeAppDesignEntrypointRedirectUrl(url) {
const path = normalizePath(url.pathname);
if (!isDesignSpaRoute(path)) {
return "";
}
if (path === "/design" && url.searchParams.get(CLAUDE_APP_DESIGN_IFRAME_QUERY) === "1") {
return "";
}
const pathParam = stringValue(url.searchParams.get(CLAUDE_APP_DESIGN_PATH_QUERY));
if (path === "/design" && pathParam) {
const markedPath = withClaudeAppDesignIframeMarker(pathParam);
return `${CLAUDE_APP_DESIGN_SHELL_PATH}?${CLAUDE_APP_DESIGN_PATH_QUERY}=${encodeURIComponent(markedPath)}`;
}
const targetPath = withClaudeAppDesignIframeMarker(`${path}${url.search || ""}${url.hash || ""}`);
return `${CLAUDE_APP_DESIGN_SHELL_PATH}?${CLAUDE_APP_DESIGN_PATH_QUERY}=${encodeURIComponent(targetPath)}`;
}
function withClaudeAppDesignIframeMarker(value) {
const raw = stringValue(value) || "/design";
let parsed;
try {
parsed = new URL(raw, "https://claude.ai");
} catch {
return raw;
}
const path = normalizePath(parsed.pathname);
if (!isDesignSpaRoute(path)) {
return raw;
}
if (parsed.searchParams.get(CLAUDE_APP_DESIGN_IFRAME_QUERY) !== "1") {
parsed.searchParams.set(CLAUDE_APP_DESIGN_IFRAME_QUERY, "1");
}
return `${parsed.pathname}${parsed.search}${parsed.hash}`;
}
function isDesignSpaRoute(path) {
if (path === "/design" || path === "/design/" || path === "/design/index.html") {
return true;
}
return /^\/design\/(?:p|fromcc)(?:\/|$)/.test(path);
}
function isBootstrapRoutePath(path) {
return BOOTSTRAP_ROUTE_PATHS.includes(path) ||
/^\/(?:api|edge-api)\/bootstrap(?:\/[^/]+\/app_start)?\/?$/.test(path) ||
/^\/_bootstrap(?:\/[^/]+\/app_start)?\/?$/.test(path);
}
function isDesignAuthEscapeRoute(path) {
return AUTH_ESCAPE_ROUTE_PATHS.some((prefix) => path === prefix || path.startsWith(`${prefix}/`));
}
function listRequests(store, limit) {
const safeLimit = Math.max(1, Math.min(Number(limit) || 100, 500));
return queryRows(
store.database,
`SELECT id, created_at, method, path, search, request_headers, request_body, response_status, response_body
FROM claude_design_requests
ORDER BY id DESC
LIMIT ${safeLimit}`,
[]
).map((row) => ({
createdAt: row.created_at,
id: row.id,
method: row.method,
path: row.path,
requestBody: parseMaybeJson(row.request_body, row.request_body),
requestHeaders: parseMaybeJson(row.request_headers, {}),
responseBody: parseMaybeJson(row.response_body, row.response_body),
responseStatus: row.response_status,
search: row.search
}));
}
function logRequest(store, entry) {
const responseBody = bodyToLogText(entry.responseBody);
store.database.run(
"INSERT INTO claude_design_requests (created_at, method, path, search, request_headers, request_body, response_status, response_body) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
[
new Date().toISOString(),
entry.method,
entry.path,
entry.search || "",
truncateText(JSON.stringify(entry.requestHeaders || {})),
truncateText(bodyToLogText(entry.requestBody)),
entry.responseStatus,
truncateText(responseBody)
]
);
store.persist();
}
function queryRows(database, sql, params) {
const statement = database.prepare(sql);
try {
if (params && params.length) {
statement.bind(params);
}
const rows = [];
while (statement.step()) {
rows.push(statement.getAsObject());
}
return rows;
} finally {
statement.free();
}
}
function fetchUpstreamAsset(upstreamUrl, request, redirectsRemaining = MAX_UPSTREAM_ASSET_REDIRECTS) {
return new Promise((resolve, reject) => {
const transport = upstreamUrl.protocol === "http:" ? http : https;
const upstreamRequest = transport.request(
upstreamUrl,
{
headers: upstreamAssetHeaders(upstreamUrl, request),
method: "GET",
timeout: 12000
},
(upstreamResponse) => {
const redirectLocation = headerValue(upstreamResponse.headers.location);
if (
redirectLocation &&
upstreamResponse.statusCode &&
upstreamResponse.statusCode >= 300 &&
upstreamResponse.statusCode < 400 &&
redirectsRemaining > 0
) {
upstreamResponse.resume();
const redirectedUrl = new URL(redirectLocation, upstreamUrl);
resolve(fetchUpstreamAsset(redirectedUrl, request, redirectsRemaining - 1));
return;
}
const chunks = [];
upstreamResponse.on("data", (chunk) => chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)));
upstreamResponse.on("end", () => {
resolve({
body: Buffer.concat(chunks),
contentType: headerValue(upstreamResponse.headers["content-type"]) || guessContentType(upstreamUrl.pathname),
status: upstreamResponse.statusCode || 502,
url: upstreamUrl.toString()
});
});
}
);
upstreamRequest.on("timeout", () => upstreamRequest.destroy(new Error(`Timed out fetching ${upstreamUrl.toString()}`)));
upstreamRequest.on("error", reject);
upstreamRequest.end();
});
}
function fetchUpstreamDesignShell(upstreamUrl, request, redirectsRemaining = MAX_UPSTREAM_ASSET_REDIRECTS) {
return new Promise((resolve, reject) => {
const transport = upstreamUrl.protocol === "http:" ? http : https;
const linkHeaders = [];
const upstreamRequest = transport.request(
upstreamUrl,
{
headers: upstreamDesignShellHeaders(upstreamUrl, request),
method: "GET",
timeout: 4000
},
(upstreamResponse) => {
const redirectLocation = headerValue(upstreamResponse.headers.location);
if (
redirectLocation &&
upstreamResponse.statusCode &&
upstreamResponse.statusCode >= 300 &&
upstreamResponse.statusCode < 400 &&
redirectsRemaining > 0
) {
upstreamResponse.resume();
const redirectedUrl = new URL(redirectLocation, upstreamUrl);
resolve(fetchUpstreamDesignShell(redirectedUrl, request, redirectsRemaining - 1));
return;
}
linkHeaders.push(...headerValues(upstreamResponse.headers.link));
const chunks = [];
let totalBytes = 0;
upstreamResponse.on("data", (chunk) => {
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
totalBytes += buffer.length;
if (totalBytes <= 512 * 1024) {
chunks.push(buffer);
}
});
upstreamResponse.on("end", () => {
resolve({
body: Buffer.concat(chunks).toString("utf8"),
contentType: headerValue(upstreamResponse.headers["content-type"]) || "",
linkHeaders,
status: upstreamResponse.statusCode || 502,
url: upstreamUrl.toString()
});
});
}
);
upstreamRequest.on("information", (info) => {
linkHeaders.push(...headerValues(info.headers?.link));
});
upstreamRequest.on("timeout", () => upstreamRequest.destroy(new Error(`Timed out discovering ${upstreamUrl.toString()}`)));
upstreamRequest.on("error", reject);
upstreamRequest.end();
});
}
function upstreamDesignShellHeaders(upstreamUrl, request) {
const cookie = headerValue(request.headers.cookie);
return {
accept: "*/*",
...(cookie ? { cookie } : {}),
origin: DEFAULT_DESIGN_ORIGIN,
referer: DEFAULT_DESIGN_REFERRER,
"user-agent":
headerValue(request.headers["user-agent"]) ||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36"
};
}
function upstreamAssetHeaders(upstreamUrl, request) {
const cookie = shouldForwardCookieToAsset(upstreamUrl) ? headerValue(request.headers.cookie) : undefined;
return {
accept: headerValue(request.headers.accept) || defaultAssetAccept(upstreamUrl.pathname),
"accept-encoding": "identity",
"accept-language": headerValue(request.headers["accept-language"]) || "en-US,en;q=0.9",
"cache-control": "no-cache",
...(cookie ? { cookie } : {}),
pragma: "no-cache",
referer: DEFAULT_DESIGN_REFERRER,
"sec-fetch-dest": assetFetchDest(upstreamUrl.pathname),
"sec-fetch-mode": "no-cors",
"sec-fetch-site": "same-origin",
"user-agent":
headerValue(request.headers["user-agent"]) ||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36"
};
}
function shouldForwardCookieToAsset(upstreamUrl) {
return upstreamUrl.hostname === DEFAULT_HOST;
}
function defaultAssetAccept(path) {
if (/\.(?:js|mjs)$/i.test(path)) {
return "*/*";
}
if (/\.css$/i.test(path)) {
return "text/css,*/*;q=0.1";
}
if (/\.(?:avif|gif|jpe?g|png|svg|webp|ico)$/i.test(path)) {
return "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8";
}
if (/\.woff2?$/i.test(path)) {
return "*/*";
}
return "*/*";
}
function assetFetchDest(path) {
if (/\.(?:js|mjs)$/i.test(path)) {
return "script";
}
if (/\.css$/i.test(path)) {
return "style";
}
if (/\.(?:avif|gif|jpe?g|png|svg|webp|ico)$/i.test(path)) {
return "image";
}
if (/\.woff2?$/i.test(path)) {
return "font";
}
return "empty";
}
function sendResponse(response, result) {
const headers = result.headers || {};
const body = result.body === undefined || result.body === null ? "" : result.body;
const normalizedBody = Buffer.isBuffer(body) ? body : typeof body === "string" ? body : JSON.stringify(body);
response.writeHead(result.status || 200, {
...headers,
"content-length": Buffer.byteLength(normalizedBody)
});
response.end(normalizedBody);
}
function jsonResponse(status, body, headers) {
return {
body,
headers: corsHeaders({
"cache-control": "no-store",
"content-type": "application/json; charset=utf-8",
...(headers || {})
}),
status
};
}
function htmlResponse(body) {
return {
body,
headers: corsHeaders({
"cache-control": "no-store",
"content-type": "text/html; charset=utf-8"
}),
status: 200
};
}
function textResponse(status, body, headers) {
return {
body,
headers: corsHeaders(headers || { "content-type": "text/plain; charset=utf-8" }),
status
};
}
function redirectResponse(status, location) {
return {
body: "",
headers: corsHeaders({
"cache-control": "no-store",
location
}),
status
};
}
function protoResponse(body, headers) {
return binaryResponse(200, body || Buffer.alloc(0), {
"cache-control": "no-store",
"connect-protocol-version": "1",
"content-type": "application/proto",
...(headers || {})
});
}
function binaryResponse(status, body, headers) {
return {
body,
headers: corsHeaders(headers || { "content-type": "application/octet-stream" }),
status
};
}
function corsHeaders(headers) {
return {
"access-control-allow-headers":
"authorization, connect-protocol-version, content-type, x-client-version, x-omelette-tab-id, x-organization-uuid, x-requested-with",
"access-control-allow-methods": "DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT",
"access-control-allow-origin": "*",
...headers
};
}
function readRequestBody(request) {
return new Promise((resolve, reject) => {
const chunks = [];
request.on("data", (chunk) => chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)));
request.once("end", () => resolve(decodeRequestBodyEncoding(Buffer.concat(chunks), request.headers?.["content-encoding"])));
request.once("error", reject);
});
}
function decodeRequestBodyEncoding(body, encodingValue) {
if (!Buffer.isBuffer(body) || body.length === 0) {
return body;
}
const encodings = stringValue(headerValue(encodingValue))?.toLowerCase()
.split(",")
.map((item) => item.trim())
.filter(Boolean) || [];
if (encodings.length === 0 && body[0] === 0x1f && body[1] === 0x8b) {
encodings.push("gzip");
}
if (encodings.length === 0 || encodings.every((encoding) => encoding === "identity")) {
return body;
}
try {
return encodings
.reverse()
.reduce((current, encoding) => decodeSingleRequestBodyEncoding(current, encoding), body);
} catch {
return body;
}
}
function decodeSingleRequestBodyEncoding(body, encoding) {
if (encoding === "gzip" || encoding === "x-gzip") {
return zlib.gunzipSync(body);
}
if (encoding === "br") {
return zlib.brotliDecompressSync(body);
}
if (encoding === "deflate") {
try {
return zlib.inflateSync(body);
} catch {
return zlib.inflateRawSync(body);
}
}
return body;
}
function renderDesignIndex(me, scriptPath, stylePath, html) {
if (isUsableDesignShellHtml(html)) {
return injectDesignMeIntoHtml(String(html), me);
}
return renderDefaultDesignIndex(me, scriptPath, stylePath);
}
function renderDefaultDesignIndex(me, scriptPath, stylePath, options = {}) {
const defaultAssetPreloads = options.defaultAssetPreloads !== false;
const normalizedScriptPath = normalizePath(scriptPath) || DEFAULT_SCRIPT_PATH;
const normalizedStylePath = normalizePath(stylePath) || (defaultAssetPreloads ? DEFAULT_STYLE_PATH : "");
const criticalModulePreloads = defaultAssetPreloads
? renderModulePreloadLinks(DEFAULT_DESIGN_CRITICAL_MODULE_PRELOAD_PATHS)
: "";
const lazyModulePreloads = defaultAssetPreloads
? renderModulePreloadLinks(DEFAULT_DESIGN_LAZY_MODULE_PRELOAD_PATHS, true)
: "";
const stylePreloads = defaultAssetPreloads
? DEFAULT_DESIGN_STYLE_PRELOAD_PATHS
.map((href) => ` <link rel="preload" as="style" crossorigin fetchpriority="low" href="${escapeHtmlAttribute(href)}">`)
.join("\n")
: "";
const stylesheetLink = normalizedStylePath
? ` <link rel="stylesheet" crossorigin href="${escapeHtmlAttribute(normalizedStylePath)}">`
: "";
return `<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Claude Design</title>
<link rel="icon" type="image/png" href="/design/favicon.png"/>
${claudeAppDesktopFeaturesScript()}
<script>
// FOUC guard: set data-theme before any stylesheet loads so the first
// paint uses the right palette. Mirrors hooks/useTheme.ts. The stored
// value is JSON (usehooks-ts useLocalStorage). When no preference is
// stored we paint light - the dark-mode flag is not known until React
// mounts, and a flag-off user with OS dark mode should not see a dark
// flash. useTheme's effect corrects this on mount for flag-on users.
try {
var raw = localStorage.getItem('om:theme');
// Mirror useTheme's default: usehooks-ts does not persist 'system'
// until the toggle is used, so absent = 'system' (gated on flagOn).
var pref = raw ? JSON.parse(raw) : 'system';
// The flag is not known at first paint. The hook persists its last
// known state here so a user whose flag was turned off after picking
// Dark does not see a dark-to-light flash on every load.
var flagOn = localStorage.getItem('om:dark-mode-enabled') === '1';
var dark = flagOn && (pref === 'dark' || (pref === 'system' && matchMedia('(prefers-color-scheme: dark)').matches));
document.documentElement.dataset.theme = dark ? 'dark' : 'light';
} catch (e) {
document.documentElement.dataset.theme = 'light';
}
</script>
<script type="module" crossorigin src="${escapeHtmlAttribute(normalizedScriptPath)}"></script>
${criticalModulePreloads}
${stylesheetLink}
${lazyModulePreloads}
${stylePreloads}
</head>
<body>
${designMeJsonScript(me)}
${designModelPreferenceResetScript(me)}
${designMeGlobalScript(me)}
<div id="root"></div>
</body>
</html>
`;
}
function renderModulePreloadLinks(paths, lowPriority = false) {
return paths
.map((href) => {
const priority = lowPriority ? " fetchpriority=\"low\"" : "";
return ` <link rel="modulepreload" crossorigin${priority} href="${escapeHtmlAttribute(href)}">`;
})
.join("\n");
}
function injectDesignMeIntoHtml(html, me) {
let nextHtml = html;
const meJsonScript = designMeJsonScript(me);
const meJsonPattern = /<script\b(?=[^>]*\bid=["']omelette-me["'])[^>]*>[\s\S]*?<\/script>/i;
if (meJsonPattern.test(nextHtml)) {
nextHtml = nextHtml.replace(meJsonPattern, meJsonScript);
}
const earlySnippets = [];
const snippets = [];
if (!nextHtml.includes("ccr-claude-app-desktop-features")) {
earlySnippets.push(claudeAppDesktopFeaturesScript());
}
if (!meJsonPattern.test(nextHtml)) {
snippets.push(meJsonScript);
}
if (!nextHtml.includes("ccr-claude-design-model-reset")) {
snippets.push(designModelPreferenceResetScript(me));
}
if (!nextHtml.includes("__OMELETTE_ME__")) {
snippets.push(designMeGlobalScript(me));
}
if (earlySnippets.length) {
nextHtml = injectHtmlAfterHeadOpen(nextHtml, earlySnippets.join("\n "));
}
if (!snippets.length) {
return nextHtml;
}
return injectHtmlAfterBodyOpen(nextHtml, snippets.join("\n "));
}
function injectHtmlAfterHeadOpen(html, snippet) {
if (/<head\b[^>]*>/i.test(html)) {
return html.replace(/<head\b([^>]*)>/i, `<head$1>\n ${snippet}`);
}
return injectHtmlAfterBodyOpen(html, snippet);
}
function injectHtmlAfterBodyOpen(html, snippet) {
if (/<body\b[^>]*>/i.test(html)) {
return html.replace(/<body\b([^>]*)>/i, `<body$1>\n ${snippet}`);
}
return `${snippet}\n${html}`;
}
function designMeJsonScript(me) {
return `<script type="application/json" id="omelette-me">${escapeJsonForScript(designMePayload(me))}</script>`;
}
function designMeGlobalScript(me) {
return `<script>window.__OMELETTE_ME__ = ${escapeJsonForScript(me)}</script>`;
}
function claudeAppDesktopFeaturesScript() {
return `<script id="ccr-claude-app-desktop-features">(function(){try{var root=globalThis;var forced={claudeDesignWindow:{status:'supported'}};function merge(value){return Object.assign({},value||{},forced);}var bootFeatures=merge(root.desktopBootFeatures);try{Object.defineProperty(root,'desktopBootFeatures',{configurable:true,get:function(){return bootFeatures;},set:function(value){bootFeatures=merge(value);}});}catch(e){root.desktopBootFeatures=bootFeatures;}function patch(container){if(!container){return;}var existing=container.AppFeatures||{};if(existing.__ccrClaudeDesignPatched){return;}var previous=existing.getSupportedFeatures;container.AppFeatures=Object.assign({},existing,{__ccrClaudeDesignPatched:true,getSupportedFeatures:function(){if(typeof previous==='function'){return Promise.resolve(previous.call(existing)).then(merge,function(){return merge();});}return Promise.resolve(merge());}});}root["claude.settings"]=root["claude.settings"]||{};patch(root["claude.settings"]);root.claude=root.claude||{};root.claude.settings=root.claude.settings||{};patch(root.claude.settings);}catch(e){}})();</script>`;
}
function designModelPreferenceResetScript(me) {
const defaultModelId = stringValue(me?.defaultModelId) || DEFAULT_GATEWAY_MODEL;
return `<script id="ccr-claude-design-model-reset">(function(){try{var defaultModelId=${escapeJsonForScript(defaultModelId)};if(!defaultModelId||String(defaultModelId).toLowerCase().indexOf('deepseek')!==-1){return;}var marker='ccr:claude-design:model-default-reset:'+defaultModelId;if(localStorage.getItem(marker)==='1'){return;}function shouldClear(key,value){var keyText=String(key||'').toLowerCase();var valueText=String(value||'').toLowerCase();if(valueText.indexOf('deepseek')===-1){return false;}return keyText.indexOf('model')!==-1||keyText.indexOf('omelette')!==-1||keyText.indexOf('om:')===0||keyText.indexOf('claude')!==-1||valueText.indexOf('model')!==-1||valueText.indexOf('deepseek::')!==-1||valueText.indexOf('deepseek/')!==-1;}function clearStorage(storage){if(!storage){return;}var keys=[];for(var i=0;i<storage.length;i++){keys.push(storage.key(i));}for(var j=0;j<keys.length;j++){var key=keys[j];if(shouldClear(key,storage.getItem(key))){storage.removeItem(key);}}}clearStorage(localStorage);clearStorage(sessionStorage);localStorage.setItem(marker,'1');}catch(e){}})();</script>`;
}
function designMePayload(me) {
return {
...me,
canManageBilling: true
};
}
function isUsableDesignShellHtml(value) {
const html = stringValue(value);
if (!html) {
return false;
}
if (!/<html[\s>]/i.test(html) || !/<script\b[^>]*\bsrc=/i.test(html)) {
return false;
}
if (/\bJust a moment\b|cf_chl|Performing security verification|App unavailable in region/i.test(html)) {
return false;
}
const hasAssetReferences = /(?:src|href)=["'][^"']*(?:\/design)?\/assets\//i.test(html);
const hasDesignShellMarkers = /anthropic\.omelette|OmeletteService|__OMELETTE_ME__|\/v1\/design/i.test(html);
const hasClaudeAppShellMarkers = /<div\b[^>]*\bid=["']root["'][^>]*>/i.test(html) &&
/\/assets\/v\d+\/index-[^"']+\.js/i.test(html) &&
/data-build-id=|data-color-version=|Claude is Anthropic/i.test(html);
return hasAssetReferences && (hasDesignShellMarkers || hasClaudeAppShellMarkers);
}
function normalizeMe(value, defaultGatewayModel = DEFAULT_GATEWAY_MODEL, gatewayModelPresets = undefined) {
const overrides = isRecord(value) ? value : {};
const defaultModelId = publicGatewayModelSelector(defaultGatewayModel) || DEFAULT_GATEWAY_MODEL;
const me = {
...DEFAULT_ME,
...overrides
};
if (!stringValue(overrides.defaultModelId)) {
me.defaultModelId = defaultModelId;
}
if (!Array.isArray(me.memberships) || me.memberships.length === 0) {
me.memberships = [
{
name: me.orgName,
uuid: me.organizationUuid
}
];
}
if (!Array.isArray(overrides.modelPresets)) {
me.modelPresets = Array.isArray(gatewayModelPresets) && gatewayModelPresets.length > 0
? gatewayModelPresets
: defaultClaudeDesignModelPresets(me.defaultModelId);
} else if (!me.modelPresets.some((preset) => isRecord(preset) && stringValue(preset.id) === me.defaultModelId)) {
me.modelPresets = [defaultClaudeDesignModelPreset(me.defaultModelId), ...me.modelPresets];
}
return me;
}
function defaultClaudeDesignModelPresets(defaultModelId) {
const presets = DEFAULT_ME.modelPresets.map((preset) => ({ ...preset }));
if (defaultModelId === DEFAULT_GATEWAY_MODEL) {
return presets;
}
return [
defaultClaudeDesignModelPreset(defaultModelId),
...presets.filter((preset) => preset.id !== defaultModelId)
];
}
function defaultClaudeDesignModelPreset(defaultModelId) {
if (defaultModelId === DEFAULT_GATEWAY_MODEL) {
return { ...DEFAULT_ME.modelPresets[0] };
}
return {
id: defaultModelId,
label: "Default Gateway Model",
maxTokens: 1000000,
supportsAdaptiveThinking: true,
description: "Uses the CCR gateway default"
};
}
function stringArray(value) {
if (!Array.isArray(value)) {
return undefined;
}
const values = value.map((item) => normalizePath(String(item || ""))).filter(Boolean);
return values.length ? values : undefined;
}
function parseJsonBody(buffer) {
return parseMaybeJson(buffer.toString("utf8") || "{}", {});
}
function parseMaybeJson(value, fallback) {
if (typeof value !== "string") {
return value === undefined ? fallback : value;
}
try {
return JSON.parse(value);
} catch {
return fallback;
}
}
function bodyToLogText(value) {
if (Buffer.isBuffer(value)) {
const text = value.toString("utf8");
return looksBinary(text) ? `<binary ${value.length} bytes>` : text;
}
if (typeof value === "string") {
return value;
}
return JSON.stringify(value);
}
function looksBinary(value) {
return value.includes("\u0000");
}
function truncateText(value) {
const text = String(value || "");
return text.length > MAX_LOG_BODY_CHARS ? `${text.slice(0, MAX_LOG_BODY_CHARS)}...<truncated>` : text;
}
function normalizePath(value) {
if (!value) {
return "";
}
const trimmed = String(value).trim();
if (!trimmed) {
return "";
}
return trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
}
function parseAbsoluteHttpUrl(value) {
const raw = stringValue(value);
if (!raw) {
return undefined;
}
try {
const parsed = new URL(raw);
return parsed.protocol === "http:" || parsed.protocol === "https:" ? parsed : undefined;
} catch {
return undefined;
}
}
function stringValue(value) {
return typeof value === "string" && value.trim() ? value.trim() : undefined;
}
function configuredGatewayApiKey(config) {
if (!isRecord(config)) {
return undefined;
}
const legacy = stringValue(config.APIKEY);
if (legacy) {
return legacy;
}
const apiKeys = Array.isArray(config.APIKEYS) ? config.APIKEYS : [];
for (const apiKey of apiKeys) {
if (isRecord(apiKey)) {
const key = stringValue(apiKey.key);
if (key) {
return key;
}
}
}
return undefined;
}
function numberValue(value) {
const number = Number(value);
return Number.isFinite(number) ? number : undefined;
}
function headerValue(value) {
if (Array.isArray(value)) {
return value[0];
}
return typeof value === "string" ? value : undefined;
}
function headerValues(value) {
if (Array.isArray(value)) {
return value.filter((item) => typeof item === "string" && item);
}
return typeof value === "string" && value ? [value] : [];
}
function headerIncludes(value, token) {
return Boolean(headerValue(value)?.toLowerCase().includes(token.toLowerCase()));
}
function randomUuid() {
return crypto.randomUUID();
}
function guessContentType(path) {
if (path.endsWith(".html") || path.endsWith(".htm")) {
return "text/html; charset=utf-8";
}
if (path.endsWith(".css")) {
return "text/css; charset=utf-8";
}
if (path.endsWith(".jsx")) {
return "text/jsx; charset=utf-8";
}
if (path.endsWith(".js") || path.endsWith(".mjs")) {
return "application/javascript; charset=utf-8";
}
if (path.endsWith(".png")) {
return "image/png";
}
if (path.endsWith(".svg")) {
return "image/svg+xml";
}
if (path.endsWith(".json")) {
return "application/json; charset=utf-8";
}
if (path.endsWith(".woff2")) {
return "font/woff2";
}
return "application/octet-stream";
}
function escapeJsonForScript(value) {
return JSON.stringify(value, null, 16)
.replace(/</g, "\\u003c")
.replace(/>/g, "\\u003e")
.replace(/&/g, "\\u0026")
.replace(/\u2028/g, "\\u2028")
.replace(/\u2029/g, "\\u2029");
}
function escapeHtmlAttribute(value) {
return String(value).replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;");
}
function escapeRegExp(value) {
return String(value).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function isRecord(value) {
return typeof value === "object" && value !== null && !Array.isArray(value);
}
const FAVICON_PNG_BASE64 =
"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGElEQVR4nGP8z8Dwn4ECwESJ5lEDRgYGAGz1A/2B4p8yAAAAAElFTkSuQmCC";