"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 = `\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("\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, /]*\bsrc=["']([^"']+)["'][^>]*>/gi, 5), stylesheets: firstMatches(html, /]*rel=["']stylesheet["'][^>]*href=["']([^"']+)["'][^>]*>/gi, 5), title: firstMatch(html, /]*>([^<]*)<\/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 = ``; if (/]*>/i.test(source)) { return source.replace(/]*)>/i, (tag) => `${tag}\n${scriptTag}`); } if (/]*>/i.test(source)) { return source.replace(/]*)>/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(/]*)>/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 ``; }); } 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(/]*>[\s\S]*?<\/system-info>/gi, "\n") .replace(/]*>[\s\S]*?<\/default\s+aesthetic_system_instructions>/gi, "\n") .replace(/]*>[\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) => ` `) .join("\n") : ""; const stylesheetLink = normalizedStylePath ? ` ` : ""; return ` Claude Design ${claudeAppDesktopFeaturesScript()} ${criticalModulePreloads} ${stylesheetLink} ${lazyModulePreloads} ${stylePreloads} ${designMeJsonScript(me)} ${designModelPreferenceResetScript(me)} ${designMeGlobalScript(me)}
`; } function renderModulePreloadLinks(paths, lowPriority = false) { return paths .map((href) => { const priority = lowPriority ? " fetchpriority=\"low\"" : ""; return ` `; }) .join("\n"); } function injectDesignMeIntoHtml(html, me) { let nextHtml = html; const meJsonScript = designMeJsonScript(me); const meJsonPattern = /]*\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 (/]*>/i.test(html)) { return html.replace(/]*)>/i, `\n ${snippet}`); } return injectHtmlAfterBodyOpen(html, snippet); } function injectHtmlAfterBodyOpen(html, snippet) { if (/]*>/i.test(html)) { return html.replace(/]*)>/i, `\n ${snippet}`); } return `${snippet}\n${html}`; } function designMeJsonScript(me) { return ``; } function designMeGlobalScript(me) { return ``; } function claudeAppDesktopFeaturesScript() { return ``; } function designModelPreferenceResetScript(me) { const defaultModelId = stringValue(me?.defaultModelId) || DEFAULT_GATEWAY_MODEL; return ``; } function designMePayload(me) { return { ...me, canManageBilling: true }; } function isUsableDesignShellHtml(value) { const html = stringValue(value); if (!html) { return false; } if (!/]/i.test(html) || !/]*\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 = /]*\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) ? `` : 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)}...` : 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, "\\u003e") .replace(/&/g, "\\u0026") .replace(/\u2028/g, "\\u2028") .replace(/\u2029/g, "\\u2029"); } function escapeHtmlAttribute(value) { return String(value).replace(/&/g, "&").replace(/"/g, """).replace(/