1048 lines
132 KiB
TypeScript
1048 lines
132 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { spawnSync } from "node:child_process";
|
|
import { existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, realpathSync, writeFileSync } from "node:fs";
|
|
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
import { allowedReactions } from "./local-ipc-protocol.js";
|
|
import { pickReactionMessage, reactionMessagePools } from "./reaction-messages.js";
|
|
|
|
const distDir = dirname(fileURLToPath(import.meta.url));
|
|
const appDir = dirname(distDir);
|
|
const repoRoot = resolve(appDir, "../..");
|
|
const packageJson = JSON.parse(readFileSync(join(appDir, "package.json"), "utf8")) as { scripts?: Record<string, string>; dependencies?: Record<string, string>; devDependencies?: Record<string, string>; description?: string; author?: string };
|
|
const rootPackageJson = JSON.parse(readFileSync(join(repoRoot, "package.json"), "utf8")) as { scripts?: Record<string, string> };
|
|
const workspaceConfig = readFileSync(join(repoRoot, "pnpm-workspace.yaml"), "utf8");
|
|
const builderConfigPath = join(appDir, "electron-builder.yml");
|
|
const builderConfig = readFileSync(builderConfigPath, "utf8");
|
|
|
|
assert.equal(packageJson.description, "Create your Familiar. A local-first AI companion with memory, voice, knowledge, and tools.");
|
|
assert.equal(packageJson.author, "FamiliarOS");
|
|
assert.match(packageJson.scripts?.["dev:debug"] ?? "", /FAMILIAROS_LOG_LEVEL=debug FAMILIAROS_LOG_CONSOLE=1 pnpm dev/, "desktop debug dev script must enable verbose log mirroring.");
|
|
assert.match(packageJson.scripts?.package ?? "", /node scripts\/clean-package-output\.cjs && electron-builder/);
|
|
assert.match(packageJson.scripts?.["package:dir"] ?? "", /node scripts\/clean-package-output\.cjs && electron-builder --dir/);
|
|
assert.equal(rootPackageJson.scripts?.["package:desktop:dir"], "pnpm build && pnpm --filter @familiaros/desktop package:dir");
|
|
assert.equal(packageJson.dependencies?.["@familiaros/claude"], "workspace:*");
|
|
assert.equal(packageJson.dependencies?.["@familiaros/cli"], "workspace:*");
|
|
assert.equal(packageJson.dependencies?.["@familiaros/cursor"], "workspace:*");
|
|
assert.equal(packageJson.dependencies?.["@familiaros/mcp"], "workspace:*");
|
|
assert.equal(packageJson.dependencies?.["@familiaros/opencode"], "workspace:*");
|
|
assert.equal(packageJson.dependencies?.["@familiaros/agent-events"], "workspace:*");
|
|
assert.equal(packageJson.dependencies?.["@img/sharp-win32-x64"], undefined, "sharp platform binaries must stay optional transitive deps, not direct host-breaking dependencies.");
|
|
assert.match(workspaceConfig, /supportedArchitectures:[\s\S]*?os:[\s\S]*?- darwin[\s\S]*?- win32[\s\S]*?- linux/, "pnpm must install optional sharp binaries for desktop release OS targets.");
|
|
assert.match(workspaceConfig, /supportedArchitectures:[\s\S]*?cpu:[\s\S]*?- x64[\s\S]*?- arm64/, "pnpm must install optional sharp binaries for desktop release CPU targets.");
|
|
assert.match(workspaceConfig, /supportedArchitectures:[\s\S]*?libc:[\s\S]*?- glibc/, "pnpm must install optional sharp binaries for Linux glibc release targets.");
|
|
assert.match(packageJson.devDependencies?.["electron-builder"] ?? "", /^\^26\.(?:9|[1-9]\d)\./, "desktop AppImage packaging must use electron-builder 26.9+ for conditional Linux sandbox handling.");
|
|
assert.match(builderConfig, /appId:\s*dev\.familiaros\.app/);
|
|
assert.match(builderConfig, /productName:\s*FamiliarOS/);
|
|
assert.match(builderConfig, /executableName:\s*familiaros/, "desktop packages must use a safe executable name for the stricter AppImage toolset.");
|
|
assert.match(builderConfig, /output:\s*dist-electron/);
|
|
assert.match(builderConfig, /linux:[\s\S]*?target:[\s\S]*?- AppImage[\s\S]*?- deb[\s\S]*?- rpm[\s\S]*?- tar\.gz/, "desktop Linux packaging must include AppImage, deb, rpm, and tar.gz targets.");
|
|
assert.match(builderConfig, /publish:\s*null/);
|
|
assert.doesNotMatch(builderConfig, /no-sandbox/, "desktop packaging must not force --no-sandbox for every Linux AppImage launch.");
|
|
assert.match(builderConfig, /toolsets:\s*\n\s*appimage:\s*1\.0\.3/, "desktop AppImage packaging must use the AppImage 1.0.3 toolset so the launcher can conditionally fall back when Linux sandboxing is unavailable.");
|
|
assert.match(builderConfig, /asar:\s*true/);
|
|
assert.match(builderConfig, /asarUnpack:/);
|
|
assert.match(builderConfig, /node_modules\/\*\*/);
|
|
assert.match(builderConfig, /dist\/\*\*/);
|
|
assert.match(builderConfig, /control-center-preload\.cjs/);
|
|
assert.match(builderConfig, /familiar-preload\.cjs/);
|
|
assert.match(builderConfig, /prompt-window-preload\.cjs/);
|
|
assert.match(builderConfig, /plugin-sdk-preload\.cjs/);
|
|
assert.match(builderConfig, /plugin-command-form-preload\.cjs/);
|
|
assert.match(builderConfig, /assets\/\*\*/);
|
|
assert.match(builderConfig, /extraResources:[\s\S]*from:\s*\.\.\/\.\.\/plugins\/official[\s\S]*to:\s*plugins\/official/, "desktop packages must include bundled official plugins as extra resources.");
|
|
assert.match(builderConfig, /icon:\s*assets\/app-icon\.icns/);
|
|
|
|
assert.ok(existsSync(join(appDir, "control-center-preload.cjs")), "control-center-preload.cjs must exist for Control Center IPC.");
|
|
assert.ok(existsSync(join(appDir, "familiar-preload.cjs")), "familiar-preload.cjs must exist for familiar window motion state updates.");
|
|
assert.ok(existsSync(join(appDir, "prompt-window-preload.cjs")), "prompt-window-preload.cjs must exist for the floating prompt window IPC.");
|
|
assert.ok(existsSync(join(appDir, "plugin-sdk-preload.cjs")), "plugin-sdk-preload.cjs must exist for JavaScript plugin SDK hosting.");
|
|
assert.ok(existsSync(join(appDir, "plugin-command-form-preload.cjs")), "plugin-command-form-preload.cjs must exist for plugin command forms.");
|
|
assert.ok(existsSync(join(appDir, "assets", "tray-icon.png")), "tray icon must exist for packaging.");
|
|
assert.ok(existsSync(join(appDir, "assets", "app-icon.icns")), "app icon must exist for packaging.");
|
|
assert.ok(existsSync(join(appDir, "assets", "app-icon.ico")), "Windows app icon must exist for packaging.");
|
|
assertNonEmptyFile(join(appDir, "assets", "default-familiar-spritesheet.webp"), "default familiar spritesheet must exist for packaging.");
|
|
assertNonEmptyFile(join(appDir, "assets", "default-familiar-thumbnail.png"), "default familiar thumbnail must exist for Familiar Manager preview.");
|
|
for (const icon of ["claude.svg", "cursor.svg", "opencode.svg", "pi.svg", "vscode.svg", "windsurf.svg", "zed.svg"]) {
|
|
assertSafeBundledSvg(join(appDir, "assets", "integrations", icon), `integration icon must be safe and packaged: ${icon}`);
|
|
}
|
|
assert.match(readFileSync(join(appDir, "src", "assets.ts"), "utf8"), /assets["']?,\s*["']tray-icon\.png|join\("assets", "tray-icon\.png"\)/, "tray icon code must keep using assets/tray-icon.png.");
|
|
const petWindowSource = readFileSync(join(appDir, "src", "familiar-window.ts"), "utf8");
|
|
const petWindowHostSource = readFileSync(join(appDir, "src", "familiar-window-host.ts"), "utf8");
|
|
const familiarInstallationSource = readFileSync(join(appDir, "src", "familiar-installation.ts"), "utf8");
|
|
const familiarInstallationArchiveSource = readFileSync(join(appDir, "src", "familiar-installation-archive.ts"), "utf8");
|
|
const catalogSource = readFileSync(join(appDir, "src", "catalog.ts"), "utf8");
|
|
const catalogRemoteSource = readFileSync(join(appDir, "src", "catalog-remote.ts"), "utf8");
|
|
const petWindowContentSource = readFileSync(join(appDir, "src", "familiar-window-content.ts"), "utf8");
|
|
const petWindowInteractionSource = readFileSync(join(appDir, "src", "familiar-window-interactions.ts"), "utf8");
|
|
const petWindowMouseInteropSource = readFileSync(join(appDir, "src", "familiar-window-mouse-interop.ts"), "utf8");
|
|
const petWindowMouseInteropRuntimeSource = readFileSync(join(appDir, "src", "familiar-window-mouse-interop-runtime.ts"), "utf8");
|
|
const petWindowMouseInteropRuntimeLifecycleSource = readFileSync(join(appDir, "src", "familiar-window-mouse-interop-runtime-lifecycle.ts"), "utf8");
|
|
const petWindowMouseInteropSupportSource = readFileSync(join(appDir, "src", "familiar-window-mouse-interop-support.ts"), "utf8");
|
|
const petWindowPluginMenuSource = readFileSync(join(appDir, "src", "familiar-window-plugin-menu.ts"), "utf8");
|
|
const petWindowRendererBridgeSource = readFileSync(join(appDir, "src", "familiar-window-renderer-bridge.ts"), "utf8");
|
|
const petWindowRenderSource = readFileSync(join(appDir, "src", "familiar-window-render.ts"), "utf8");
|
|
const petWindowRenderBubblesSource = readFileSync(join(appDir, "src", "familiar-window-render-bubbles.ts"), "utf8");
|
|
const petWindowLayoutSource = readFileSync(join(appDir, "src", "familiar-window-layout.ts"), "utf8");
|
|
const controlCenterPreloadSource = readFileSync(join(appDir, "control-center-preload.cjs"), "utf8");
|
|
const controlCenterRendererSource = readFileSync(join(appDir, "src", "renderer", "src", "main.tsx"), "utf8");
|
|
const controlCenterRouteLoadersSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "route-loaders.tsx"), "utf8");
|
|
const controlCenterRouteDataLoadersSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "route-data-loaders.ts"), "utf8");
|
|
const controlCenterCoreServicesSource = readFileSync(join(appDir, "src", "control-center-core-services.ts"), "utf8");
|
|
const controlCenterStateSource = readFileSync(join(appDir, "src", "control-center-state.ts"), "utf8");
|
|
const controlCenterStateValidationSource = readFileSync(join(appDir, "src", "control-center-state-validation.ts"), "utf8");
|
|
const controlCenterSharedSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "shared.tsx"), "utf8");
|
|
const controlCenterSharedUiSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "shared-ui.tsx"), "utf8");
|
|
const controlCenterSharedUiIconsSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "shared-ui-icons.tsx"), "utf8");
|
|
const controlCenterSharedUiNavigationSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "shared-ui-navigation.tsx"), "utf8");
|
|
const controlCenterSharedOpenApiSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "shared-openapi.ts"), "utf8");
|
|
const mcpToolkitCatalogSource = readFileSync(join(appDir, "src", "renderer", "src", "mcp-toolkit-catalog.ts"), "utf8");
|
|
const mcpToolkitCatalogDataSource = readFileSync(join(appDir, "src", "renderer", "src", "mcp-toolkit-catalog-data.ts"), "utf8");
|
|
const mcpToolkitCatalogStarterSource = readFileSync(join(appDir, "src", "renderer", "src", "mcp-toolkit-catalog-starter.ts"), "utf8");
|
|
const mcpToolkitCatalogSystemSource = readFileSync(join(appDir, "src", "renderer", "src", "mcp-toolkit-catalog-system.ts"), "utf8");
|
|
const mcpToolkitCatalogAdvancedSource = readFileSync(join(appDir, "src", "renderer", "src", "mcp-toolkit-catalog-advanced.ts"), "utf8");
|
|
const mcpToolkitCatalogTypesSource = readFileSync(join(appDir, "src", "renderer", "src", "mcp-toolkit-catalog-types.ts"), "utf8");
|
|
const controlCenterPluginsViewConfigHelpersSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "plugins-view-config-helpers.ts"), "utf8");
|
|
const controlCenterPluginsViewSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "plugins-view.tsx"), "utf8");
|
|
const controlCenterPluginsViewPresentationSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "plugins-view-presentation.tsx"), "utf8");
|
|
const controlCenterPluginsViewStateSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "plugins-view-state.ts"), "utf8");
|
|
const controlCenterPluginsViewConfigFieldsSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "plugins-view-config-fields.tsx"), "utf8");
|
|
const controlCenterPluginsViewConfigListEditorSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "plugins-view-config-list-editor.tsx"), "utf8");
|
|
const controlCenterSettingsSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "settings-view.tsx"), "utf8");
|
|
const controlCenterSettingsGeneralTabSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "settings-general-tab.tsx"), "utf8");
|
|
const controlCenterSettingsReactionsTabSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "settings-reactions-tab.tsx"), "utf8");
|
|
const controlCenterSettingsPluginsTabSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "settings-plugins-tab.tsx"), "utf8");
|
|
const controlCenterSettingsPluginsTypesSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "settings-plugins-tab-types.ts"), "utf8");
|
|
const controlCenterSettingsOpenApiGroupSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "settings-openapi-group.tsx"), "utf8");
|
|
const controlCenterSettingsOpenApiGroupProviderSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "settings-openapi-group-provider.tsx"), "utf8");
|
|
const controlCenterSettingsTtsGroupSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "settings-tts-group.tsx"), "utf8");
|
|
const controlCenterSettingsTtsGroupRowsSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "settings-tts-group-rows.tsx"), "utf8");
|
|
const controlCenterSettingsTtsGroupTypesSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "settings-tts-group-types.ts"), "utf8");
|
|
const controlCenterSettingsPluginPlatformGroupSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "settings-plugin-platform-group.tsx"), "utf8");
|
|
const controlCenterSettingsKnowledgeTabSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "settings-knowledge-tab.tsx"), "utf8");
|
|
const controlCenterSettingsStateSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "settings-view-state.ts"), "utf8");
|
|
const controlCenterSettingsStateActionsSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "settings-view-state-actions.ts"), "utf8");
|
|
const controlCenterSettingsStateActionTypesSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "settings-view-state-action-types.ts"), "utf8");
|
|
const controlCenterSettingsStatePluginActionsSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "settings-view-state-plugin-actions.ts"), "utf8");
|
|
const controlCenterSettingsStateKnowledgeActionsSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "settings-view-state-knowledge-actions.ts"), "utf8");
|
|
const controlCenterSettingsActionsSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "settings-view-actions.ts"), "utf8");
|
|
const controlCenterSettingsActionCoreSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "settings-view-action-core.ts"), "utf8");
|
|
const controlCenterSettingsTtsActionsSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "settings-view-tts-actions.ts"), "utf8");
|
|
const controlCenterSettingsSystemActionsSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "settings-view-system-actions.ts"), "utf8");
|
|
const controlCenterFamiliarsSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "familiars-view.tsx"), "utf8");
|
|
const controlCenterFamiliarsDetailDialogSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "familiars-view-detail-dialog.tsx"), "utf8");
|
|
const controlCenterFamiliarsStateSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "familiars-view-state.ts"), "utf8");
|
|
const controlCenterFamiliarsHelpersSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "familiars-view-helpers.ts"), "utf8");
|
|
const controlCenterFamiliarsEffectsSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "familiars-view-effects.ts"), "utf8");
|
|
const controlCenterIntegrationsSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "integrations-view.tsx"), "utf8");
|
|
const controlCenterIntegrationsDetailDialogSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "integrations-view-detail-dialog.tsx"), "utf8");
|
|
const controlCenterIntegrationsAgentSectionsSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "integrations-view-agent-sections.tsx"), "utf8");
|
|
const controlCenterIntegrationsStateSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "integrations-view-state.ts"), "utf8");
|
|
const controlCenterIntegrationsSharedSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "integrations-view-shared.tsx"), "utf8");
|
|
const controlCenterIntegrationsToolingSectionsSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "integrations-view-tooling-sections.tsx"), "utf8");
|
|
const controlCenterIntegrationsMcpToolkitSectionSource = readFileSync(join(appDir, "src", "renderer", "src", "control-center", "integrations-view-mcp-toolkit-section.tsx"), "utf8");
|
|
const openApiChatSource = readFileSync(join(appDir, "src", "openapi-chat.ts"), "utf8");
|
|
const openApiChatPromptFlowsSource = readFileSync(join(appDir, "src", "openapi-chat-prompt-flows.ts"), "utf8");
|
|
const openApiChatPromptPlainFlowSource = readFileSync(join(appDir, "src", "openapi-chat-prompt-plain-flow.ts"), "utf8");
|
|
const openApiChatProviderSource = readFileSync(join(appDir, "src", "openapi-chat-provider.ts"), "utf8");
|
|
const openApiChatProviderSupportSource = readFileSync(join(appDir, "src", "openapi-chat-provider-support.ts"), "utf8");
|
|
const openApiChatProviderRoutingSource = readFileSync(join(appDir, "src", "openapi-chat-provider-routing.ts"), "utf8");
|
|
const openApiChatProviderPayloadsSource = readFileSync(join(appDir, "src", "openapi-chat-provider-payloads.ts"), "utf8");
|
|
const openApiChatSettingsSource = readFileSync(join(appDir, "src", "openapi-chat-settings.ts"), "utf8");
|
|
const openApiChatModelSource = readFileSync(join(appDir, "src", "openapi-chat-model.ts"), "utf8");
|
|
const openApiChatPresentationSource = readFileSync(join(appDir, "src", "openapi-chat-presentation.ts"), "utf8");
|
|
const openApiChatToolLoopSource = readFileSync(join(appDir, "src", "openapi-chat-tool-loop.ts"), "utf8");
|
|
const openApiChatConfigStoreSource = readFileSync(join(appDir, "src", "openapi-chat-config-store.ts"), "utf8");
|
|
const openApiChatRequestHelpersSource = readFileSync(join(appDir, "src", "openapi-chat-request-helpers.ts"), "utf8");
|
|
const openApiChatConversationStoreSource = readFileSync(join(appDir, "src", "openapi-chat-conversation-store.ts"), "utf8");
|
|
const promptWindowRenderSource = readFileSync(join(appDir, "src", "prompt-window-render.ts"), "utf8");
|
|
const promptWindowRenderScriptSource = readFileSync(join(appDir, "src", "prompt-window-render-script.ts"), "utf8");
|
|
const promptWindowRenderScriptSectionsSource = readFileSync(join(appDir, "src", "prompt-window-render-script-sections.ts"), "utf8");
|
|
const promptWindowRenderScriptRenderSectionSource = readFileSync(join(appDir, "src", "prompt-window-render-script-render-section.ts"), "utf8");
|
|
const promptWindowRenderScriptLayoutSectionSource = readFileSync(join(appDir, "src", "prompt-window-render-script-layout-section.ts"), "utf8");
|
|
const promptWindowRenderScriptInteractionsSource = readFileSync(join(appDir, "src", "prompt-window-render-script-interactions.ts"), "utf8");
|
|
const promptWindowBoundsSource = readFileSync(join(appDir, "src", "prompt-window-bounds.ts"), "utf8");
|
|
const promptWindowIpcSource = readFileSync(join(appDir, "src", "prompt-window-ipc.ts"), "utf8");
|
|
const petPreloadSource = readFileSync(join(appDir, "familiar-preload.cjs"), "utf8");
|
|
const promptWindowPreloadSource = readFileSync(join(appDir, "prompt-window-preload.cjs"), "utf8");
|
|
const reactionMessagesSource = readFileSync(join(appDir, "src", "reaction-messages.ts"), "utf8");
|
|
const displaySource = readFileSync(join(appDir, "src", "display.ts"), "utf8");
|
|
const updateCheckerSource = readFileSync(join(appDir, "src", "update-checker.ts"), "utf8");
|
|
const mcpToolkitInstallerSource = readFileSync(join(appDir, "src", "mcp-toolkit-installer.ts"), "utf8");
|
|
const mcpToolkitInstallerSupportSource = readFileSync(join(appDir, "src", "mcp-toolkit-installer-support.ts"), "utf8");
|
|
const mcpToolkitInstallerCommandsSource = readFileSync(join(appDir, "src", "mcp-toolkit-installer-commands.ts"), "utf8");
|
|
const traySource = readFileSync(join(appDir, "src", "tray.ts"), "utf8");
|
|
const enCatalogSource = readFileSync(join(appDir, "src", "i18n", "locales", "en.ts"), "utf8");
|
|
const windowsSource = readFileSync(join(appDir, "src", "windows.ts"), "utf8");
|
|
const controlCenterIpcSource = readFileSync(join(appDir, "src", "control-center-ipc.ts"), "utf8");
|
|
const controlCenterIpcAgentSource = readFileSync(join(appDir, "src", "control-center-ipc-agent.ts"), "utf8");
|
|
const controlCenterIpcKnowledgeSource = readFileSync(join(appDir, "src", "control-center-ipc-knowledge.ts"), "utf8");
|
|
const controlCenterIpcPetSource = readFileSync(join(appDir, "src", "control-center-ipc-pets.ts"), "utf8");
|
|
const controlCenterIpcPluginSource = readFileSync(join(appDir, "src", "control-center-ipc-plugin.ts"), "utf8");
|
|
const controlCenterIpcSettingsSource = readFileSync(join(appDir, "src", "control-center-ipc-settings.ts"), "utf8");
|
|
const controlCenterIpcSharedSource = readFileSync(join(appDir, "src", "control-center-ipc-shared.ts"), "utf8");
|
|
const promptWindowSource = readFileSync(join(appDir, "src", "prompt-window.ts"), "utf8");
|
|
const familiarosMemorySource = readFileSync(join(appDir, "src", "familiaros-memory.ts"), "utf8");
|
|
const familiarosMemoryStoreSource = readFileSync(join(appDir, "src", "familiaros-memory-store.ts"), "utf8");
|
|
const familiarosMemorySearchSource = readFileSync(join(appDir, "src", "familiaros-memory-search.ts"), "utf8");
|
|
const knowledgeStoreSource = readFileSync(join(appDir, "src", "knowledge-store.ts"), "utf8");
|
|
const knowledgeStoreCoreSource = readFileSync(join(appDir, "src", "knowledge-store-core.ts"), "utf8");
|
|
const knowledgeStoreCoreSupportSource = readFileSync(join(appDir, "src", "knowledge-store-core-support.ts"), "utf8");
|
|
const knowledgeStoreCoreHelpersSource = readFileSync(join(appDir, "src", "knowledge-store-core-helpers.ts"), "utf8");
|
|
const appStateSource = readFileSync(join(appDir, "src", "app-state.ts"), "utf8");
|
|
const appStateNormalizationSource = readFileSync(join(appDir, "src", "app-state-normalization.ts"), "utf8");
|
|
const appStatePreferencesSource = readFileSync(join(appDir, "src", "app-state-preferences.ts"), "utf8");
|
|
const appStateAnalyticsSource = readFileSync(join(appDir, "src", "app-state-analytics.ts"), "utf8");
|
|
const appStateFamiliarRecordsSource = readFileSync(join(appDir, "src", "app-state-familiar-records.ts"), "utf8");
|
|
const appStateStorageSource = readFileSync(join(appDir, "src", "app-state-storage.ts"), "utf8");
|
|
const agentSetupSource = readFileSync(join(appDir, "src", "agent-setup.ts"), "utf8");
|
|
const agentSetupClaudeStatusSource = readFileSync(join(appDir, "src", "agent-setup-claude-status.ts"), "utf8");
|
|
const agentSetupActionsSource = readFileSync(join(appDir, "src", "agent-setup-actions.ts"), "utf8");
|
|
const agentSetupActionsClaudeMcpSource = readFileSync(join(appDir, "src", "agent-setup-actions-claude-mcp.ts"), "utf8");
|
|
const agentSetupActionsToolingSource = readFileSync(join(appDir, "src", "agent-setup-actions-tooling.ts"), "utf8");
|
|
const agentSetupCommandRunnerSource = readFileSync(join(appDir, "src", "agent-setup-command-runner.ts"), "utf8");
|
|
const agentSetupCommandContextSource = readFileSync(join(appDir, "src", "agent-setup-command-context.ts"), "utf8");
|
|
const agentSetupEditorToolsSource = readFileSync(join(appDir, "src", "agent-setup-editor-tools.ts"), "utf8");
|
|
const agentSetupEditorToolsOpenCodeSource = readFileSync(join(appDir, "src", "agent-setup-editor-tools-opencode.ts"), "utf8");
|
|
const agentSetupEditorToolsCursorSource = readFileSync(join(appDir, "src", "agent-setup-editor-tools-cursor.ts"), "utf8");
|
|
const agentSetupSupportSource = readFileSync(join(appDir, "src", "agent-setup-support.ts"), "utf8");
|
|
const pluginFamiliarRegistrySource = readFileSync(join(appDir, "src", "plugin-familiar-registry.ts"), "utf8");
|
|
const pluginFamiliarRegistryActionsSource = readFileSync(join(appDir, "src", "plugin-familiar-registry-actions.ts"), "utf8");
|
|
const pluginSdkBridgeSource = readFileSync(join(appDir, "src", "plugin-sdk-bridge.ts"), "utf8");
|
|
const pluginSdkApiBuilderSource = readFileSync(join(appDir, "src", "plugin-sdk-api-builder.ts"), "utf8");
|
|
const pluginSdkApiBuilderPlatformSource = readFileSync(join(appDir, "src", "plugin-sdk-api-builder-platform.ts"), "utf8");
|
|
const pluginSdkApiBuilderPlatformFilesSource = readFileSync(join(appDir, "src", "plugin-sdk-api-builder-platform-files.ts"), "utf8");
|
|
const pluginSdkFamiliarSurfaceSource = readFileSync(join(appDir, "src", "plugin-sdk-familiar-surface.ts"), "utf8");
|
|
const pluginSdkValidatorsSource = readFileSync(join(appDir, "src", "plugin-sdk-validators.ts"), "utf8");
|
|
const pluginSdkBubbleSupportSource = readFileSync(join(appDir, "src", "plugin-sdk-bubble-support.ts"), "utf8");
|
|
const pluginSdkBubbleValidationSource = readFileSync(join(appDir, "src", "plugin-sdk-bubble-validation.ts"), "utf8");
|
|
const pluginSdkCommandValidationSource = readFileSync(join(appDir, "src", "plugin-sdk-command-validation.ts"), "utf8");
|
|
const pluginSdkBridgeRuntimeStateSource = readFileSync(join(appDir, "src", "plugin-sdk-bridge-runtime-state.ts"), "utf8");
|
|
const pluginSdkRuntimeHelpersSource = readFileSync(join(appDir, "src", "plugin-sdk-runtime-helpers.ts"), "utf8");
|
|
const pluginSdkStorageStoresSource = readFileSync(join(appDir, "src", "plugin-sdk-storage-stores.ts"), "utf8");
|
|
const pluginJsHostSource = readFileSync(join(appDir, "src", "plugin-js-host.ts"), "utf8");
|
|
const pluginJsHostSdkDispatchSource = readFileSync(join(appDir, "src", "plugin-js-host-sdk-dispatch.ts"), "utf8");
|
|
const pluginServiceSource = readFileSync(join(appDir, "src", "plugin-service.ts"), "utf8");
|
|
const pluginServiceAppSource = readFileSync(join(appDir, "src", "plugin-service-app.ts"), "utf8");
|
|
const pluginServiceActionsSource = readFileSync(join(appDir, "src", "plugin-service-actions.ts"), "utf8");
|
|
const pluginServiceSupportSource = readFileSync(join(appDir, "src", "plugin-service-support.ts"), "utf8");
|
|
const pluginServiceGuardHelpersSource = readFileSync(join(appDir, "src", "plugin-service-guard-helpers.ts"), "utf8");
|
|
const pluginServiceBundledLifecycleSource = readFileSync(join(appDir, "src", "plugin-service-bundled-lifecycle.ts"), "utf8");
|
|
const pluginServiceBundledSupportSource = readFileSync(join(appDir, "src", "plugin-service-bundled-support.ts"), "utf8");
|
|
const pluginServiceCatalogSupportSource = readFileSync(join(appDir, "src", "plugin-service-catalog-support.ts"), "utf8");
|
|
const pluginServiceDefaultPetSource = readFileSync(join(appDir, "src", "plugin-service-default-pet.ts"), "utf8");
|
|
const pluginServiceDialogSupportSource = readFileSync(join(appDir, "src", "plugin-service-dialog-support.ts"), "utf8");
|
|
const pluginServiceLocalSupportSource = readFileSync(join(appDir, "src", "plugin-service-local-support.ts"), "utf8");
|
|
const pluginServiceSnapshotSource = readFileSync(join(appDir, "src", "plugin-service-snapshot.ts"), "utf8");
|
|
const pluginServiceTextHelpersSource = readFileSync(join(appDir, "src", "plugin-service-text-helpers.ts"), "utf8");
|
|
const pluginManifestSource = readFileSync(join(appDir, "src", "plugin-manifest.ts"), "utf8");
|
|
const pluginManifestConfigValidationSource = readFileSync(join(appDir, "src", "plugin-manifest-config-validation.ts"), "utf8");
|
|
const pluginManifestShapeValidationSource = readFileSync(join(appDir, "src", "plugin-manifest-shape-validation.ts"), "utf8");
|
|
const loggerSource = readFileSync(join(appDir, "src", "logger.ts"), "utf8");
|
|
const mainSource = readFileSync(join(appDir, "src", "main.ts"), "utf8");
|
|
const localIpcSourceForLogging = readFileSync(join(appDir, "src", "local-ipc.ts"), "utf8");
|
|
const localIpcSocketSource = readFileSync(join(appDir, "src", "local-ipc-socket.ts"), "utf8");
|
|
const localIpcRequestHandlersSource = readFileSync(join(appDir, "src", "local-ipc-request-handlers.ts"), "utf8");
|
|
const localIpcPathsSource = readFileSync(join(appDir, "src", "local-ipc-paths.ts"), "utf8");
|
|
const leaseManagerSource = readFileSync(join(appDir, "src", "lease-manager.ts"), "utf8");
|
|
const defaultPetControllerSource = readFileSync(join(appDir, "src", "default-familiar-controller.ts"), "utf8");
|
|
const defaultPetControllerExternalSource = readFileSync(join(appDir, "src", "default-familiar-controller-external.ts"), "utf8");
|
|
const defaultPetControllerBubbleSource = readFileSync(join(appDir, "src", "default-familiar-controller-bubbles.ts"), "utf8");
|
|
const defaultPetControllerDisplaySource = readFileSync(join(appDir, "src", "default-familiar-controller-display.ts"), "utf8");
|
|
const defaultPetControllerDisplaySupportSource = readFileSync(join(appDir, "src", "default-familiar-controller-display-support.ts"), "utf8");
|
|
const defaultPetControllerMotionSource = readFileSync(join(appDir, "src", "default-familiar-controller-motion.ts"), "utf8");
|
|
const defaultPetControllerWindowSource = readFileSync(join(appDir, "src", "default-familiar-controller-window.ts"), "utf8");
|
|
const agentPetControllerSourceForLogging = readFileSync(join(appDir, "src", "agent-familiar-controller.ts"), "utf8");
|
|
const mappingDoc = readFileSync(join(repoRoot, "docs", "mapping.md"), "utf8");
|
|
const controlCenterInternalUiSource = [
|
|
windowsSource,
|
|
controlCenterIpcSource,
|
|
controlCenterIpcAgentSource,
|
|
controlCenterIpcKnowledgeSource,
|
|
controlCenterIpcPetSource,
|
|
controlCenterIpcPluginSource,
|
|
controlCenterIpcSettingsSource,
|
|
].join("\n");
|
|
const agentSetupCombinedSource = `${agentSetupSource}\n${agentSetupActionsSource}\n${agentSetupEditorToolsSource}`;
|
|
const controlCenterIntegrationsCombinedSource = [
|
|
controlCenterIntegrationsSource,
|
|
controlCenterIntegrationsDetailDialogSource,
|
|
controlCenterIntegrationsAgentSectionsSource,
|
|
controlCenterIntegrationsSharedSource,
|
|
controlCenterIntegrationsToolingSectionsSource,
|
|
controlCenterIntegrationsMcpToolkitSectionSource,
|
|
].join("\n");
|
|
const controlCenterSharedCombinedSource = [
|
|
controlCenterSharedSource,
|
|
controlCenterSharedUiSource,
|
|
controlCenterSharedOpenApiSource,
|
|
].join("\n");
|
|
const controlCenterSettingsCombinedSource = [
|
|
controlCenterSettingsSource,
|
|
controlCenterSettingsGeneralTabSource,
|
|
controlCenterSettingsReactionsTabSource,
|
|
controlCenterSettingsPluginsTabSource,
|
|
controlCenterSettingsPluginsTypesSource,
|
|
controlCenterSettingsOpenApiGroupSource,
|
|
controlCenterSettingsOpenApiGroupProviderSource,
|
|
controlCenterSettingsTtsGroupSource,
|
|
controlCenterSettingsPluginPlatformGroupSource,
|
|
controlCenterSettingsKnowledgeTabSource,
|
|
controlCenterSharedCombinedSource,
|
|
].join("\n");
|
|
const petWindowRenderCombinedSource = `${petWindowSource}\n${petWindowRenderSource}\n${petWindowRenderBubblesSource}`;
|
|
const petWindowContentCombinedSource = `${petWindowSource}\n${petWindowContentSource}`;
|
|
const petWindowRuntimeCombinedSource = `${petWindowSource}\n${petWindowHostSource}\n${petWindowInteractionSource}\n${petWindowMouseInteropSource}\n${petWindowMouseInteropRuntimeSource}\n${petWindowMouseInteropRuntimeLifecycleSource}\n${petWindowMouseInteropSupportSource}`;
|
|
assert.match(loggerSource, /familiaros\.log/, "desktop logger must write a user-sendable familiaros.log file.");
|
|
assert.match(loggerSource, /familiaros\.previous\.log/, "desktop logger must retain a previous log file for bug reports.");
|
|
assert.match(loggerSource, /FAMILIAROS_LOG_LEVEL/, "desktop logger must support verbose dev logging via environment.");
|
|
assert.match(loggerSource, /normalizeLogLevel\(process\.env\.FAMILIAROS_LOG_LEVEL\) \?\? "debug"/, "desktop production logger must default to debug for user-sendable diagnostics.");
|
|
assert.match(loggerSource, /redacted-token/, "desktop logger must redact token-looking values.");
|
|
assert.match(mainSource, /initializeLogger\(\)/, "desktop startup must initialize logging before subsystem startup.");
|
|
assert.match(mainSource, /process\.platform === "linux"[\s\S]*?appendSwitch\("ozone-platform", "x11"\)/, "Linux desktop familiars must prefer X11/Xwayland because GNOME Wayland blocks always-on-top and programmatic window dragging.");
|
|
assert.match(mainSource, /hasSwitch\("ozone-platform"\)/, "Linux X11 preference must let users override Electron's Ozone backend explicitly.");
|
|
assert.match(traySource, /t\("tray\.openLogsFolder"\)/, "desktop tray must expose user-sendable logs for bug reports.");
|
|
assert.match(enCatalogSource, /Open Logs Folder/, "English catalog must keep the user-sendable logs label.");
|
|
assert.match(localIpcSourceForLogging, /request received/, "desktop IPC must log request methods for diagnostics.");
|
|
assert.match(localIpcPathsSource, /FAMILIAROS_IPC_BIND[\s\S]*?FAMILIAROS_IPC_ENDPOINT[\s\S]*?validateBindHost[\s\S]*?validateAdvertisedHost/, "WSL NAT IPC must separate bind and advertised endpoints with validation.");
|
|
assert.match(localIpcPathsSource, /FAMILIAROS_IPC_ENDPOINT only controls the advertised discovery endpoint[\s\S]*?FAMILIAROS_IPC_BIND to opt into TCP IPC listening/, "FAMILIAROS_IPC_ENDPOINT-only mode must not start TCP listening; FAMILIAROS_IPC_BIND is the explicit TCP opt-in.");
|
|
assert.match(localIpcPathsSource, /FAMILIAROS_IPC_ENDPOINT must use the same port as FAMILIAROS_IPC_BIND unless FAMILIAROS_IPC_BIND uses port 0/, "WSL NAT advertised endpoint must not silently override mismatched ports.");
|
|
assert.match(appStateSource, /from "\.\/app-state-preferences(?:\.js)?"/, "App state must import the extracted preferences seam.");
|
|
assert.match(appStateSource, /from "\.\/app-state-analytics(?:\.js)?"/, "App state must import the extracted analytics seam.");
|
|
assert.match(appStateSource, /from "\.\/app-state-normalization(?:\.js)?"/, "App state must import the extracted normalization seam.");
|
|
assert.match(appStateSource, /from "\.\/app-state-storage(?:\.js)?"/, "App state must import the extracted storage seam.");
|
|
assert.match(appStateNormalizationSource, /export function normalizeState/, "App state normalization seam must export state normalization.");
|
|
assert.match(appStateNormalizationSource, /export function createDefaultState/, "App state normalization seam must export the default state factory.");
|
|
assert.match(appStateNormalizationSource, /export function cloneState/, "App state normalization seam must export state cloning.");
|
|
assert.match(appStateNormalizationSource, /from "\.\/app-state-familiar-records(?:\.js)?"/, "App state normalization seam must import the extracted familiar-records seam.");
|
|
assert.match(appStatePreferencesSource, /export function normalizePreferences/, "App state preferences seam must export state preference normalization.");
|
|
assert.match(appStatePreferencesSource, /export function normalizeOpenApiChatEndpoint/, "App state preferences seam must export OpenAPI endpoint normalization.");
|
|
assert.match(appStateAnalyticsSource, /export function normalizeAnalytics/, "App state analytics seam must export analytics normalization.");
|
|
assert.match(appStateAnalyticsSource, /export function buildRecordedActivityAnalytics/, "App state analytics seam must export analytics activity accumulation.");
|
|
assert.match(appStateFamiliarRecordsSource, /export function normalizeInstalledPetRecord/, "App state familiar-records seam must export installed familiar normalization.");
|
|
assert.match(appStateFamiliarRecordsSource, /export function validateInstalledPetFiles/, "App state familiar-records seam must export familiar file validation.");
|
|
assert.match(appStateStorageSource, /export function migrateLegacyStateFilenames/, "App state storage seam must export legacy migration helpers.");
|
|
assert.match(appStateStorageSource, /export function readStateJsonFile/, "App state storage seam must export JSON read helpers.");
|
|
assert.match(appStateStorageSource, /export function writeStateJsonFile/, "App state storage seam must export atomic JSON write helpers.");
|
|
assert.match(appStateStorageSource, /export function acquireStartupInstallLock/, "App state storage seam must export startup install lock helpers.");
|
|
assert.match(leaseManagerSource, /acquired/, "lease manager must log lease acquisition details.");
|
|
assert.match(defaultPetControllerSource, /show requested/, "default familiar controller must log show lifecycle events.");
|
|
assert.match(agentPetControllerSourceForLogging, /show requested/, "agent familiar controller must log show lifecycle events.");
|
|
assert.ok(petWindowContentCombinedSource.includes("defaultPetSprite.frameWidth * defaultPetSprite.columns") && petWindowContentCombinedSource.includes("defaultPetSprite.frameHeight * defaultPetSprite.rows"), "familiar renderer must derive universal spritesheet dimensions from frame size and row/column counts.");
|
|
for (const reaction of ["idle", "thinking", "working", "editing", "running", "testing", "waiting", "waving", "success", "error", "celebrating"]) {
|
|
assert.match(reactionMessagesSource, new RegExp(`${reaction}:\\s*\\[`), `reaction messages must define a pool for: ${reaction}`);
|
|
}
|
|
assert.match(reactionMessagesSource, /satisfies Record<FamiliarOSReaction, readonly string\[\]>/, "reaction-only bubble message pools must be exhaustive over FamiliarOSReaction.");
|
|
assert.match(petWindowSource, /pickReactionMessage\(display\.reaction\b/, "reaction-only bubbles must render randomized messages instead of raw lowercase reaction ids.");
|
|
assert.match(petWindowSource, /function preparePetTransientDisplay/, "reaction-only bubbles must prepare a stable random message before rerenders.");
|
|
assert.match(petWindowSource, /function mergePetTransientDisplay/, "reaction-only events must not replace an active explicit message bubble.");
|
|
assert.match(petWindowSource, /return \{ \.\.\.current, reaction: next\.reaction, dismissToken: next\.dismissToken \?\? current\.dismissToken \}/, "reaction-only updates merged into an active message must carry the latest dismiss token.");
|
|
assert.match(petWindowSource, /function getTransientDisplayDurationMs[\s\S]*?12_000[\s\S]*?message\.length \* 70/, "speech bubbles must stay visible longer for longer messages without becoming permanent.");
|
|
assert.match(defaultPetControllerDisplaySource, /getTransientDisplayDurationMs\(state\.transientDisplay\)/, "default familiar speech bubble timeout must be length-aware.");
|
|
assert.match(agentPetControllerSourceForLogging, /getTransientDisplayDurationMs\(preparedDisplay\)/, "agent familiar speech bubble timeout must be length-aware.");
|
|
assert.match(petWindowSource, /function getTransientReactionAnimationMs/, "finite reaction animations must expose their own shorter lifetime.");
|
|
assert.match(petWindowSource, /function clearTransientReaction/, "finite reaction animations must be clearable while the bubble remains visible.");
|
|
assert.match(petWindowRendererBridgeSource, /sendWindowChannel\(window, "familiaros:familiar-reaction-state"/, "finite reaction animations must clear sprite state without reloading the bubble.");
|
|
assert.match(petPreloadSource, /familiaros:familiar-reaction-state/, "familiar preload must accept in-place reaction state updates.");
|
|
assert.match(petWindowMouseInteropSupportSource, /webContents:\s*window\.webContents/, "mouse interop support must capture webContents once so cleanup can avoid touching a destroyed BrowserWindow.");
|
|
assert.match(petWindowMouseInteropRuntimeLifecycleSource, /const removeListeners = \(\): void => \{[\s\S]*?if \(!context\.webContents\.isDestroyed\(\)\)/, "mouse interop cleanup must avoid touching destroyed webContents objects.");
|
|
assert.match(petWindowMouseInteropRuntimeLifecycleSource, /context\.window\.on\("close", removeListeners\);\s*context\.window\.once\("closed", removeListeners\);/, "familiar window cleanup must run before and after close so agent lease release and Cmd\/Ctrl\+W are idempotent.");
|
|
assert.match(displaySource, /width:\s*220/, "familiar windows must stay tightly bounded around familiar and bubble.");
|
|
assert.match(displaySource, /height:\s*320/, "familiar windows must be tall enough for adaptive long message bubbles at large familiar scale without becoming a huge click shield.");
|
|
assert.match(petWindowRenderCombinedSource, /function getBubbleClassName|export function getBubbleClassName/, "familiar bubbles must classify explicit messages by length.");
|
|
assert.match(petWindowRenderCombinedSource, /is-long-message/, "familiar bubbles must have a long-message layout.");
|
|
assert.match(petWindowRenderCombinedSource, /is-very-long-message/, "familiar bubbles must have a very-long-message layout for 140-character say messages.");
|
|
assert.match(petWindowRenderCombinedSource, /body \{ -webkit-app-region: no-drag; pointer-events: none; \}/, "transparent familiar window background must not capture clicks or drags.");
|
|
assert.match(petWindowSource, /from "\.\/familiar-window-interactions(?:\.js)?"/, "familiar-window must import the extracted interaction helper module.");
|
|
assert.match(petWindowSource, /from "\.\/familiar-window-host(?:\.js)?"/, "familiar-window must import the extracted host helper module.");
|
|
assert.match(petWindowSource, /from "\.\/familiar-window-content(?:\.js)?"/, "familiar-window must import the extracted content helper module.");
|
|
assert.match(petWindowSource, /from "\.\/familiar-window-layout(?:\.js)?"/, "familiar-window must import the extracted layout helper module.");
|
|
assert.match(petWindowSource, /from "\.\/familiar-window-plugin-menu(?:\.js)?"/, "familiar-window must import the extracted plugin menu helper module.");
|
|
assert.match(petWindowSource, /from "\.\/familiar-window-renderer-bridge(?:\.js)?"/, "familiar-window must import the extracted renderer bridge helper module.");
|
|
assert.match(petWindowHostSource, /export function createBasePetWindow/, "familiar-window-host must export the shared BrowserWindow shell.");
|
|
assert.match(petWindowHostSource, /export function allocateWindowLoadSequence/, "familiar-window-host must export the familiar reload sequence helper.");
|
|
assert.match(petWindowHostSource, /export async function loadPetHtmlFile/, "familiar-window-host must export the familiar HTML loader seam.");
|
|
assert.match(familiarInstallationSource, /from "\.\/familiar-installation-archive(?:\.js)?"/, "familiar-installation must import the extracted archive seam.");
|
|
assert.match(familiarInstallationArchiveSource, /export async function downloadPetZip/, "familiar-installation archive seam must export remote ZIP download.");
|
|
assert.match(familiarInstallationArchiveSource, /export async function extractPetZip/, "familiar-installation archive seam must export yauzl extraction.");
|
|
assert.match(familiarInstallationArchiveSource, /redirect:\s*"error"/, "familiar-installation archive downloads must reject HTTP redirects.");
|
|
assert.match(familiarInstallationArchiveSource, /validateZipUrl\(response\.url\)/, "familiar-installation archive downloads must re-validate the final URL.");
|
|
assert.match(familiarInstallationArchiveSource, /strictFileNames:\s*true/, "familiar-installation archive extraction must keep yauzl strict file-name validation.");
|
|
assert.match(familiarInstallationArchiveSource, /safePath\.relativeOutputPath === "spritesheet\.webp"[\s\S]*?maxCodexSpritesheetBytes/, "familiar-installation archive extraction must cap spritesheets at the codex size limit.");
|
|
assert.match(catalogSource, /from "\.\/catalog-remote(?:\.js)?"/, "catalog must import the extracted remote helper seam.");
|
|
assert.match(catalogSource, /export \{ catalogUrl, catalogV3Url \} from "\.\/catalog-remote(?:\.js)?"/, "catalog must re-export catalog URLs through the extracted remote seam.");
|
|
assert.match(catalogRemoteSource, /export async function tryLoadRemoteCatalogV3Index/, "catalog remote seam must export the v3 index loader.");
|
|
assert.match(catalogRemoteSource, /export async function tryLoadRemoteCatalogV3Page/, "catalog remote seam must export the v3 page loader.");
|
|
assert.match(catalogRemoteSource, /export async function tryLoadRemoteCatalog/, "catalog remote seam must export the v2 remote loader.");
|
|
assert.match(catalogRemoteSource, /export async function tryLoadFixtureCatalog/, "catalog remote seam must export the fixture loader.");
|
|
assert.match(catalogRemoteSource, /export async function getRemoteCatalogV3Index/, "catalog remote seam must export the v3 index fetch/cache helper.");
|
|
assert.match(catalogRemoteSource, /export async function getRemoteCatalogV3Page/, "catalog remote seam must export the v3 page fetch/cache helper.");
|
|
assert.match(catalogRemoteSource, /export async function getRemoteCatalogV3Search/, "catalog remote seam must export the v3 search fetch/cache helper.");
|
|
assert.match(catalogRemoteSource, /export async function getV2CatalogOrFixture/, "catalog remote seam must export the v2/fixture fallback helper.");
|
|
assert.match(petWindowContentSource, /export async function createDefaultPetRender/, "familiar-window content helper must export default familiar rendering.");
|
|
assert.match(petWindowContentSource, /export async function createInstalledPetRender/, "familiar-window content helper must export installed familiar rendering.");
|
|
assert.match(petWindowLayoutSource, /export function getBasePetWindowSize/, "familiar-window-layout must export base window sizing.");
|
|
assert.match(petWindowLayoutSource, /export function getPetWindowSize/, "familiar-window-layout must export bubble layout sizing.");
|
|
assert.match(petWindowLayoutSource, /export function estimateWrappedLineCount/, "familiar-window-layout must export wrapped line estimation.");
|
|
assert.match(petWindowPluginMenuSource, /export function installPetContextMenu/, "plugin menu helper must export the pet context-menu seam.");
|
|
assert.match(petWindowRendererBridgeSource, /export function sendBubbleLayout/, "renderer bridge helper must export bubble layout IPC.");
|
|
assert.match(petWindowRendererBridgeSource, /export function setPetReactionState/, "renderer bridge helper must export reaction IPC.");
|
|
assert.match(petWindowRendererBridgeSource, /export function playPetWindowAudio/, "renderer bridge helper must export audio IPC.");
|
|
assert.match(petWindowInteractionSource, /from "\.\/familiar-window-mouse-interop(?:\.js)?"/, "interaction helper must re-export the extracted mouse interop runtime.");
|
|
assert.match(petWindowMouseInteropSource, /export function installMousePassthroughAndDrag/, "mouse interop runtime must export the mouse passthrough seam.");
|
|
assert.match(petWindowMouseInteropSource, /from "\.\/familiar-window-mouse-interop-runtime(?:\.js)?"/, "mouse interop shell must compose the extracted runtime seam.");
|
|
assert.match(petWindowMouseInteropRuntimeSource, /export function attachPetWindowMouseInteropRuntime/, "mouse interop runtime seam must export listener and IPC attachment.");
|
|
assert.match(petWindowMouseInteropRuntimeSource, /from "\.\/familiar-window-mouse-interop-runtime-lifecycle(?:\.js)?"/, "mouse interop runtime seam must compose the extracted lifecycle seam.");
|
|
assert.match(petWindowMouseInteropRuntimeSource, /attachPetWindowMouseInteropLifecycle\(context, maps, \{/, "mouse interop runtime seam must delegate listener lifecycle attachment.");
|
|
assert.match(petWindowRuntimeCombinedSource, /setIgnoreMouseEvents\(true, \{ forward: true \}\)/, "transparent familiar window background must use OS-level mouse passthrough.");
|
|
assert.match(petWindowRuntimeCombinedSource, /setIgnoreMouseEvents\(false\)/, "visible familiar and bubble hit targets must re-enable mouse handling.");
|
|
assert.match(petWindowRuntimeCombinedSource, /familiaros:familiar-ready/, "familiar windows must resync passthrough after each renderer reload.");
|
|
assert.match(petWindowMouseInteropSupportSource, /export function rearmPassthrough[\s\S]*?process\.platform !== "win32"[\s\S]*?rearmWindowsMouseForwarding\(context, reason\)/, "Windows familiar reloads must toggle forwarded mouse passthrough to re-register hover and drag tracking.");
|
|
assert.match(petWindowMouseInteropSupportSource, /scheduleWindowsMouseForwardingRearm\(context, `\$\{reason\}\+75ms`, 75\);[\s\S]*?scheduleWindowsMouseForwardingRearm\(context, `\$\{reason\}\+175ms`, 175\);/, "Windows familiar reloads must retry mouse forwarding rearm after load settles.");
|
|
assert.match(petWindowRuntimeCombinedSource, /familiaros:familiar-probe-hit-test/, "Windows familiar reloads must probe current cursor hit target when mousemove forwarding is stale.");
|
|
assert.match(petWindowMouseInteropSource, /export function recoverPetMouseInterop/, "familiar windows must expose a controlled mouse interop recovery hook for OS display and resume events.");
|
|
assert.match(petWindowMouseInteropRuntimeLifecycleSource, /export function attachPetWindowMouseInteropLifecycle/, "mouse interop lifecycle seam must export listener lifecycle attachment.");
|
|
assert.match(petWindowMouseInteropRuntimeLifecycleSource, /recovery\.set\(context\.window, \(reason\) =>[\s\S]*?scheduleMouseInteropRecovery\(context, reason\)/, "familiar windows must register their mouse interop recovery callback.");
|
|
assert.match(petWindowMouseInteropSupportSource, /export function scheduleWindowsForwardingWatch[\s\S]*?rearmWindowsMouseForwarding\(context, reason, false\)[\s\S]*?scheduleWindowsForwardingWatch\(context, reason\)/, "Windows familiar passthrough must keep rearming while idle so hover and drag recover after familiar reloads without noisy logs.");
|
|
assert.match(petPreloadSource, /familiaros:familiar-probe-hit-test[\s\S]*?elementFromPoint\(clientX, clientY\)[\s\S]*?reportInteractiveHit/, "familiar preload must answer main-process cursor hit-test probes.");
|
|
assert.match(petWindowMouseInteropRuntimeLifecycleSource, /did-finish-load", rearmAfterLoad/, "familiar windows must re-arm mouse passthrough after every content load.");
|
|
assert.match(petWindowMouseInteropRuntimeLifecycleSource, /did-fail-load", handleLoadFailure/, "familiar windows must restore passthrough after failed content loads.");
|
|
assert.match(petWindowHostSource, /window\.setIgnoreMouseEvents\(false\);[\s\S]*?await window\.loadFile/, "familiar reloads must reset OS mouse passthrough before navigation.");
|
|
assert.match(petWindowHostSource, /export function allocateWindowLoadSequence/, "familiar content reloads must allocate request sequence before async rendering.");
|
|
assert.match(petWindowSource, /tryUpdateLoadedPetContent\(window, render, "default", sequence\)/, "default familiar transient updates must avoid BrowserWindow reloads when the familiar document is already loaded.");
|
|
assert.match(petPreloadSource, /familiaros:familiar-content-state[\s\S]*?document\.body\.innerHTML/, "familiar preload must accept sanitized in-place content updates for transient bubbles and badges.");
|
|
assert.match(petWindowHostSource, /windowLoadChains\.set\(window, next\)/, "familiar content reloads must serialize loadFile calls per BrowserWindow.");
|
|
assert.match(petWindowHostSource, /next\.catch\(\(\) => \{\}\)\.finally/, "familiar content reload chain cleanup must not create unhandled rejections.");
|
|
assert.match(petWindowHostSource, /destroyed-after-write/, "familiar content reloads must re-check destroyed windows after writing HTML.");
|
|
assert.match(petWindowPluginMenuSource, /getDefaultPetPluginCommands/, "default familiar plugin commands must stay in the extracted context-menu helper seam.");
|
|
assert.match(petWindowPluginMenuSource, /plugin-command-form-preload\.cjs/, "plugin command forms must keep the preload bridge in the extracted context-menu helper seam.");
|
|
assert.match(petWindowRenderCombinedSource, /process\.platform === "win32" \? "none" : "drop-shadow/, "Windows familiar windows must avoid CSS drop-shadow on transparent layered windows.");
|
|
assert.match(petWindowRenderCombinedSource, /process\.platform === "win32" \? "none" : "blur\(10px\)"/, "Windows familiar windows must avoid backdrop-filter on transparent layered windows.");
|
|
assert.match(petWindowRenderCombinedSource, /\.bubble\.is-pinned \{[\s\S]*?backdrop-filter: \$\{process\.platform === "win32" \? "none" : "blur\(8px\)"\}/, "Windows pinned bubbles must avoid backdrop-filter on transparent layered windows.");
|
|
assert.match(petWindowRenderCombinedSource, /\.familiar-shell[\s\S]*?-webkit-app-region: no-drag; cursor: grab;/, "familiar dragging must avoid Electron draggable regions so right-click context menus work.");
|
|
assert.match(petPreloadSource, /familiaros:familiar-hit-test/, "familiar preload must report visible familiar and bubble hit testing for passthrough.");
|
|
assert.match(petPreloadSource, /familiaros:familiar-ready/, "familiar preload must report readiness after installing mouse handlers.");
|
|
assert.match(petPreloadSource, /familiaros:familiar-drag-start/, "familiar preload must start controlled familiar dragging from the sprite.");
|
|
assert.match(petPreloadSource, /familiaros:familiar-open-prompt/, "familiar preload must request the floating prompt window on double-click.");
|
|
assert.match(defaultPetControllerSource, /from "\.\/default-familiar-controller-display(?:\.js)?"/, "default familiar controller must compose the extracted display seam.");
|
|
assert.match(defaultPetControllerSource, /from "\.\/default-familiar-controller-display-support(?:\.js)?"/, "default familiar controller must compose the extracted display-support seam.");
|
|
assert.match(defaultPetControllerSource, /from "\.\/default-familiar-controller-external(?:\.js)?"/, "default familiar controller must compose the extracted external-action seam.");
|
|
assert.match(defaultPetControllerSource, /from "\.\/default-familiar-controller-motion(?:\.js)?"/, "default familiar controller must compose the extracted motion seam.");
|
|
assert.match(defaultPetControllerSource, /from "\.\/default-familiar-controller-bubbles(?:\.js)?"/, "default familiar controller must compose the extracted bubble seam.");
|
|
assert.match(defaultPetControllerSource, /from "\.\/default-familiar-controller-window(?:\.js)?"/, "default familiar controller must compose the extracted window seam.");
|
|
assert.match(defaultPetControllerExternalSource, /export function applyExternalPetReactionAction/, "default familiar external seam must export reaction orchestration.");
|
|
assert.match(defaultPetControllerExternalSource, /export function applyExternalPetSayAction/, "default familiar external seam must export say orchestration.");
|
|
assert.match(defaultPetControllerExternalSource, /export function applyInternalPetMessageAction/, "default familiar external seam must export internal message orchestration.");
|
|
assert.match(defaultPetControllerExternalSource, /export function applyExternalPetMoveToHomeAction/, "default familiar external seam must export move-home orchestration.");
|
|
assert.match(defaultPetControllerBubbleSource, /export function createDefaultPetBubbleRuntime/, "default familiar bubble seam must export the bubble runtime factory.");
|
|
assert.match(defaultPetControllerBubbleSource, /showDefaultPetForExternalEvent\(\)/, "default familiar bubble seam must request the familiar to show when plugin bubbles appear.");
|
|
assert.match(defaultPetControllerBubbleSource, /refreshDefaultPetContent\(\)/, "default familiar bubble seam must refresh the familiar surface when plugin bubbles change.");
|
|
assert.match(defaultPetControllerDisplaySource, /export function setDefaultPetTransientDisplay/, "default familiar display seam must export transient display handling.");
|
|
assert.match(defaultPetControllerDisplaySource, /export function setDefaultPetStatusBadge/, "default familiar display seam must export status badge handling.");
|
|
assert.match(defaultPetControllerDisplaySource, /export function getDefaultPetDismissToken/, "default familiar display seam must export dismiss token shaping.");
|
|
assert.match(defaultPetControllerDisplaySupportSource, /export function createDefaultPetDisplaySupport/, "default familiar display-support seam must export the controller helper factory.");
|
|
assert.match(defaultPetControllerMotionSource, /export async function moveDefaultPetWindowBy/, "default familiar motion seam must export bounded plugin window movement.");
|
|
assert.match(defaultPetControllerMotionSource, /export function getPetMovementBlockedReason/, "default familiar motion seam must export movement block-state resolution.");
|
|
assert.match(defaultPetControllerWindowSource, /export function loadDefaultPetWindowContent/, "default familiar window seam must export content reload handling.");
|
|
assert.match(defaultPetControllerWindowSource, /export function installDefaultPetWindowDisplayHandlers/, "default familiar window seam must export display and resume handler installation.");
|
|
assert.match(defaultPetControllerWindowSource, /powerMonitor\.on\("resume", recoverAfterResume\)/, "default familiar must recover mouse interop after Windows sleep or resume.");
|
|
assert.match(defaultPetControllerWindowSource, /recoverMouseInterop\("display-change"\)/, "default familiar must recover mouse interop after monitor topology changes.");
|
|
assert.match(controlCenterInternalUiSource, /recoverDefaultPetMouseInterop\("default-familiar-changed"\)/, "changing default familiar must recover mouse interop for dragging without app restart.");
|
|
assert.match(petWindowPluginMenuSource, /function installPetContextMenu/, "familiar windows must install a native right-click context menu.");
|
|
assert.match(petWindowPluginMenuSource, /webContents\.on\("context-menu"/, "familiar context menu must be handled in the Electron main process.");
|
|
assert.match(petWindowPluginMenuSource, /Menu\.buildFromTemplate/, "familiar context menu must use a small native Electron menu.");
|
|
assert.doesNotMatch(petPreloadSource, /setIgnoreMouseEvents/, "familiar preload must not call Electron window APIs directly.");
|
|
const agentPetControllerSource = readFileSync(join(appDir, "src", "agent-familiar-controller.ts"), "utf8");
|
|
const localIpcSource = readFileSync(join(appDir, "src", "local-ipc.ts"), "utf8");
|
|
assert.match(agentPetControllerSource, /dismissedAgentPets = new Set<string>/, "agent familiars must remember manual close while leases remain active.");
|
|
assert.match(agentPetControllerSource, /dismissAgentPetForActiveLease/, "agent familiar context-menu close must dismiss the familiar for the active lease.");
|
|
assert.match(agentPetControllerSource, /dismissedAgentPets\.has\(petId\)/, "dismissed agent familiars must not reopen on later same-lease reactions.");
|
|
assert.match(agentPetControllerSource, /function clearAgentPetLeaseState/, "agent familiar lease cleanup must clear dismissal, timers, and hidden transient state.");
|
|
assert.match(localIpcSource, /handleLastExplicitLease/, "agent familiar dismissal must clear when the explicit lease group ends.");
|
|
assert.match(localIpcSource, /from "\.\/local-ipc-socket(?:\.js)?"/, "local-ipc must compose the extracted socket helper seam.");
|
|
assert.match(localIpcSource, /from "\.\/local-ipc-request-handlers(?:\.js)?"/, "local-ipc must compose the extracted request-handler seam.");
|
|
assert.match(localIpcSocketSource, /export function handleLocalIpcSocket/, "local-ipc socket seam must export the client socket handler.");
|
|
assert.match(localIpcSocketSource, /export function getLocalIpcListeningEndpoint/, "local-ipc socket seam must export advertised endpoint resolution.");
|
|
assert.match(localIpcSocketSource, /export function isAllowedRemoteAddress/, "local-ipc socket seam must export remote-address filtering.");
|
|
assert.match(localIpcRequestHandlersSource, /export async function handleLocalIpcRequest/, "local-ipc request-handler seam must export the async dispatcher.");
|
|
assert.match(localIpcRequestHandlersSource, /export function buildHelloResponse/, "local-ipc request-handler seam must export the hello response builder.");
|
|
assert.match(localIpcRequestHandlersSource, /export function buildFamiliarsListResponse/, "local-ipc request-handler seam must export familiar list response shaping.");
|
|
assert.match(localIpcSource, /clearAgentPetLeaseState\(petId\)/, "last explicit lease cleanup must reset dismissed agent familiar state.");
|
|
assert.match(localIpcRequestHandlersSource, /reason: applied\.reason/, "IPC responses must report dismissed explicit familiar events as not shown.");
|
|
assert.match(updateCheckerSource, /alvinunreal\/familiaros/, "GitHub release notice must check the public FamiliarOS repository.");
|
|
assert.match(updateCheckerSource, /api\.github\.com\/repos\/\$\{githubRepository\}\/releases\/latest/, "update checker must use GitHub latest release API.");
|
|
assert.match(updateCheckerSource, /shell\.openExternal\(url\)/, "update action must open the GitHub release page externally.");
|
|
assert.match(traySource, /t\("tray\.updateAvailable"/, "tray menu must surface available updates.");
|
|
assert.match(enCatalogSource, /Update available:/, "English catalog must keep the update-available label.");
|
|
assert.match(controlCenterInternalUiSource, /familiaros:check-for-updates/, "settings window must be able to trigger update checks.");
|
|
assert.match(controlCenterInternalUiSource, /familiaros:get-reaction-animation-settings/, "settings window must be able to load reaction animation metadata.");
|
|
assert.match(controlCenterInternalUiSource, /reactionAnimationOverrides/, "settings window must be able to persist reaction animation overrides.");
|
|
assert.match(controlCenterInternalUiSource, /familiaros-familiar-preview/, "settings reaction preview must use a scoped internal familiar preview protocol.");
|
|
assert.match(controlCenterInternalUiSource, /familiaros:open-update-release-page/, "settings window must be able to open the release page.");
|
|
assert.match(controlCenterCoreServicesSource, /from "\.\/control-center-state-validation(?:\.js)?"/, "Control Center core services barrel must export the extracted state validation seam.");
|
|
assert.match(controlCenterStateValidationSource, /export function validatePreferencePatch/, "Control Center state validation seam must export preferences validation.");
|
|
assert.match(controlCenterStateValidationSource, /export function validateExternalUrl/, "Control Center state validation seam must export external URL validation.");
|
|
assert.doesNotMatch(controlCenterStateSource, /export function validatePreferencePatch/, "Control Center state snapshot surface must no longer own preferences validation directly.");
|
|
assert.match(controlCenterPreloadSource, /checkForUpdates/, "Control Center preload must expose update checks.");
|
|
assert.match(controlCenterPreloadSource, /getReactionAnimationSettings/, "Control Center preload must expose reaction animation settings metadata.");
|
|
assert.match(controlCenterPreloadSource, /saveOpenApiCredential/, "Control Center preload must expose OpenAPI chat credential management.");
|
|
assert.match(promptWindowPreloadSource, /submitPrompt/, "Prompt window preload must expose prompt submission.");
|
|
assert.match(promptWindowSource, /from "\.\/prompt-window-bounds(?:\.js)?"/, "Prompt window must import the extracted bounds seam.");
|
|
assert.match(promptWindowSource, /from "\.\/prompt-window-ipc(?:\.js)?"/, "Prompt window must import the extracted IPC seam.");
|
|
assert.match(promptWindowIpcSource, /sendOpenApiChatPrompt/, "Prompt window IPC seam must submit prompts through the main-process OpenAPI chat service.");
|
|
assert.match(promptWindowIpcSource, /openControlCenterWindow\("settings"\)/, "Prompt window IPC seam must be able to open Settings when the API key is missing.");
|
|
assert.match(familiarosMemorySource, /from "\.\/familiaros-memory-store(?:\.js)?"/, "familiaros-memory must import the extracted store seam.");
|
|
assert.match(familiarosMemorySource, /from "\.\/familiaros-memory-search(?:\.js)?"/, "familiaros-memory must import the extracted search seam.");
|
|
assert.match(familiarosMemoryStoreSource, /export function getFamiliarOSMemoryStore/, "familiaros-memory store seam must export cached store reads.");
|
|
assert.match(familiarosMemoryStoreSource, /export function commitFamiliarOSMemoryStore/, "familiaros-memory store seam must export store commits.");
|
|
assert.match(familiarosMemoryStoreSource, /export function normalizeMemoryTags/, "familiaros-memory store seam must export tag normalization.");
|
|
assert.match(familiarosMemoryStoreSource, /export function pruneMemoryEntries/, "familiaros-memory store seam must export retention pruning.");
|
|
assert.match(familiarosMemorySearchSource, /export function searchMemoryEntries/, "familiaros-memory search seam must export scored memory search.");
|
|
assert.match(familiarosMemorySearchSource, /export function buildRelevantMemoryContextBlock/, "familiaros-memory search seam must export relevant memory context shaping.");
|
|
assert.match(familiarosMemorySearchSource, /export function scoreRetentionPriority/, "familiaros-memory search seam must export retention scoring.");
|
|
assert.match(knowledgeStoreSource, /from "\.\/knowledge-store-core(?:\.js)?"/, "knowledge-store facade must continue routing through the core class seam.");
|
|
assert.match(knowledgeStoreCoreSource, /from "\.\/knowledge-store-core-support(?:\.js)?"/, "knowledge-store core must import the extracted support seam.");
|
|
assert.match(knowledgeStoreCoreSupportSource, /export function createKnowledgeFileEntry/, "knowledge-store support must own stored-file validation and shaping.");
|
|
assert.match(knowledgeStoreCoreSupportSource, /from "\.\/knowledge-store-core-helpers(?:\.js)?"/, "knowledge-store support must import the extracted helper seam.");
|
|
assert.match(knowledgeStoreCoreSupportSource, /export function searchKnowledgeFiles/, "knowledge-store support must own file search scoring.");
|
|
assert.match(knowledgeStoreCoreSupportSource, /export function buildRelevantKnowledgeContext/, "knowledge-store support must own relevant-context assembly.");
|
|
assert.match(knowledgeStoreCoreHelpersSource, /export function normalizeKnowledgeFileEntry/, "knowledge-store helpers must own index-entry normalization.");
|
|
assert.match(knowledgeStoreCoreHelpersSource, /export function scoreKnowledgeFile/, "knowledge-store helpers must own low-level search scoring.");
|
|
assert.match(promptWindowRenderSource, /from "\.\/prompt-window-render-script(?:\.js)?"/, "Prompt window render helper must import the extracted inline script seam.");
|
|
assert.match(promptWindowBoundsSource, /export const promptWindowRenderConfig/, "Prompt window bounds seam must export render sizing config.");
|
|
assert.match(promptWindowBoundsSource, /export function getPromptWindowBounds/, "Prompt window bounds seam must export prompt window placement.");
|
|
assert.match(promptWindowBoundsSource, /export function normalizeResizeRequest/, "Prompt window bounds seam must export resize normalization.");
|
|
assert.match(promptWindowIpcSource, /export function installPromptWindowIpcHandlers/, "Prompt window IPC seam must export IPC route installation.");
|
|
assert.match(promptWindowIpcSource, /export function resolvePromptWindowStoredFile/, "Prompt window IPC seam must export stored-file normalization.");
|
|
assert.match(promptWindowIpcSource, /from "\.\/prompt-window-bounds(?:\.js)?"/, "Prompt window IPC seam must compose the extracted bounds helpers.");
|
|
assert.match(promptWindowRenderScriptSource, /from "\.\/prompt-window-render-script-sections(?:\.js)?"/, "Prompt window render script seam must import the extracted script section builders.");
|
|
assert.match(promptWindowRenderScriptSource, /from "\.\/prompt-window-render-script-interactions(?:\.js)?"/, "Prompt window render script seam must import the extracted interaction builder.");
|
|
assert.match(promptWindowRenderScriptSectionsSource, /export \{ buildPromptWindowScriptRenderSection \} from "\.\/prompt-window-render-script-render-section(?:\.js)?"/, "Prompt window render script sections seam must re-export the extracted render section builder.");
|
|
assert.match(promptWindowRenderScriptSectionsSource, /export \{ buildPromptWindowScriptLayoutSection \} from "\.\/prompt-window-render-script-layout-section(?:\.js)?"/, "Prompt window render script sections seam must re-export the extracted layout section builder.");
|
|
assert.match(promptWindowRenderScriptRenderSectionSource, /export function buildPromptWindowScriptRenderSection/, "Prompt window render script render seam must export render and attachment helpers.");
|
|
assert.match(promptWindowRenderScriptLayoutSectionSource, /export function buildPromptWindowScriptLayoutSection/, "Prompt window render script layout seam must export the layout section builder.");
|
|
assert.match(promptWindowRenderScriptInteractionsSource, /export function buildPromptWindowScriptInteractionSection/, "Prompt window render script interactions seam must export interaction handlers.");
|
|
assert.match(promptWindowRenderScriptSource, /export function buildPromptWindowScript/, "Prompt window render script seam must export the inline script builder.");
|
|
assert.match(controlCenterSettingsOpenApiGroupSource, /from "\.\/settings-openapi-group-provider"/, "Control Center OpenAPI settings shell must import the extracted provider seam.");
|
|
assert.match(controlCenterSettingsOpenApiGroupProviderSource, /export function OpenApiProviderSettingsRows/, "Control Center OpenAPI provider seam must export the provider row bundle.");
|
|
assert.match(controlCenterSettingsCombinedSource, /API key or token|OpenAI API key/, "Control Center settings must expose OpenAPI chat credential setup.");
|
|
assert.match(pluginServiceSource, /from "\.\/plugin-service-support(?:\.js)?"/, "plugin-service must import the extracted support barrel seam.");
|
|
assert.match(pluginServiceSource, /from "\.\/plugin-service-guard-helpers(?:\.js)?"/, "plugin-service must import the extracted guard helper seam.");
|
|
assert.match(pluginServiceSource, /from "\.\/plugin-service-app(?:\.js)?"/, "plugin-service must re-export the extracted app singleton seam.");
|
|
assert.match(pluginServiceAppSource, /export function initializePluginService/, "plugin-service app seam must export singleton initialization.");
|
|
assert.match(pluginServiceAppSource, /export function getPluginService/, "plugin-service app seam must export singleton access.");
|
|
assert.match(pluginServiceAppSource, /buildDefaultPetPluginCommands/, "plugin-service app seam must delegate default familiar command shaping.");
|
|
assert.match(pluginServiceAppSource, /buildDefaultPetPluginMenuItems/, "plugin-service app seam must delegate default familiar menu shaping.");
|
|
assert.match(pluginServiceGuardHelpersSource, /export function safeError/, "plugin-service guard seam must export manifest-safe errors.");
|
|
assert.match(pluginServiceGuardHelpersSource, /export function safeSoundError/, "plugin-service guard seam must export sound-safe errors.");
|
|
assert.match(pluginServiceGuardHelpersSource, /export function isPermissionSubset/, "plugin-service guard seam must export permission subset helpers.");
|
|
assert.match(pluginServiceGuardHelpersSource, /export function isStringSubset/, "plugin-service guard seam must export string subset helpers.");
|
|
assert.match(pluginJsHostSource, /from "\.\/plugin-js-host-sdk-dispatch(?:\.js)?"/, "plugin JS host must import the extracted SDK dispatch seam.");
|
|
assert.match(pluginJsHostSource, /installPluginJsSdkHandler\(/, "plugin JS host must delegate SDK IPC installation through the extracted seam.");
|
|
assert.match(pluginJsHostSdkDispatchSource, /export function installPluginJsSdkHandler/, "plugin JS host SDK dispatch seam must export IPC handler installation.");
|
|
assert.match(pluginJsHostSdkDispatchSource, /export const sdkCallHandlers/, "plugin JS host SDK dispatch seam must own the SDK route handler table.");
|
|
assert.match(pluginJsHostSdkDispatchSource, /__familiarOSError/, "plugin JS host SDK dispatch seam must preserve FamiliarOS sandbox error shaping.");
|
|
assert.match(pluginJsHostSdkDispatchSource, /__familiarOSRunCallback \?\? globalThis\.__openPetsRunCallback/, "plugin JS host SDK dispatch seam must preserve legacy callback bridging.");
|
|
assert.match(pluginServiceSource, /seedBundledPluginRecords\(this\.\#getBundledLifecycleContext\(\)\)/, "plugin-service must delegate bundled seeding through the extracted lifecycle seam.");
|
|
assert.match(pluginServiceSource, /pruneStaleBundledPluginRecords\(this\.\#getBundledLifecycleContext\(\)\)/, "plugin-service must delegate bundled stale pruning through the extracted lifecycle seam.");
|
|
assert.match(pluginSdkApiBuilderSource, /from "\.\/plugin-sdk-bubble-validation(?:\.js)?"/, "plugin-sdk API builder must import the extracted bubble validation seam.");
|
|
assert.match(pluginSdkValidatorsSource, /from "\.\/plugin-sdk-command-validation(?:\.js)?"/, "plugin-sdk-validators must import the extracted command validation seam.");
|
|
assert.match(pluginSdkValidatorsSource, /from "\.\/plugin-sdk-bubble-support(?:\.js)?"/, "plugin-sdk-validators must import the extracted bubble support seam.");
|
|
assert.match(pluginSdkBubbleSupportSource, /export function validateBubbleInput/, "plugin-sdk bubble support must export input validation.");
|
|
assert.match(pluginSdkBubbleSupportSource, /export function validateDynamicText/, "plugin-sdk bubble support must export dynamic text validation.");
|
|
assert.match(pluginSdkBridgeSource, /from "\.\/plugin-sdk-api-builder(?:\.js)?"/, "plugin-sdk-bridge must import the extracted API builder seam.");
|
|
assert.match(pluginSdkApiBuilderSource, /from "\.\/plugin-sdk-runtime-helpers(?:\.js)?"/, "plugin-sdk API builder must import the extracted runtime helper seam.");
|
|
assert.match(pluginSdkBridgeSource, /from "\.\/plugin-sdk-bridge-runtime-state(?:\.js)?"/, "plugin-sdk-bridge must import the extracted runtime-state seam.");
|
|
assert.match(pluginSdkBridgeSource, /from "\.\/plugin-sdk-storage-stores(?:\.js)?"/, "plugin-sdk-bridge must import the extracted storage-store seam.");
|
|
assert.match(pluginSdkApiBuilderSource, /export function createPluginSdkApi/, "plugin-sdk API builder seam must export the bridge API builder.");
|
|
assert.match(pluginSdkApiBuilderSource, /export const pluginEventNames/, "plugin-sdk API builder seam must export plugin event names.");
|
|
assert.match(pluginSdkApiBuilderSource, /from "\.\/plugin-sdk-api-builder-platform(?:\.js)?"/, "plugin-sdk API builder seam must compose the extracted platform seam.");
|
|
assert.match(pluginSdkApiBuilderPlatformSource, /export function createPluginSdkPlatformApis/, "plugin-sdk platform seam must export the platform namespace builder.");
|
|
assert.match(pluginSdkApiBuilderPlatformSource, /from "\.\/plugin-sdk-api-builder-platform-files(?:\.js)?"/, "plugin-sdk platform seam must compose the extracted files namespace builder.");
|
|
assert.match(pluginSdkApiBuilderSource, /createPluginFamiliarSurface/, "plugin-sdk API builder seam must compose the extracted familiar surface seam.");
|
|
assert.match(pluginSdkApiBuilderPlatformSource, /from "\.\/plugin-sdk-network(?:\.js)?"/, "plugin-sdk platform seam must import the extracted network guard helpers.");
|
|
assert.match(pluginSdkApiBuilderPlatformSource, /from "\.\/plugin-sdk-validators(?:\.js)?"/, "plugin-sdk platform seam must import the extracted validator helpers.");
|
|
assert.match(pluginSdkApiBuilderPlatformSource, /from "\.\/plugin-sdk-support(?:\.js)?"/, "plugin-sdk platform seam must import the extracted locale and text support helpers.");
|
|
assert.match(pluginSdkApiBuilderPlatformFilesSource, /export function createPluginSdkFilesApi/, "plugin-sdk platform files seam must export the files namespace builder.");
|
|
assert.match(pluginSdkFamiliarSurfaceSource, /export function createPluginFamiliarSurface/, "plugin-sdk familiar surface seam must export familiar, assets, and schedule surface assembly.");
|
|
assert.match(pluginSdkBubbleValidationSource, /export function createBubbleSpecValidator/, "plugin-sdk bubble validation seam must export the bubble validator factory.");
|
|
assert.match(pluginSdkCommandValidationSource, /export function validateCommandSpec/, "plugin-sdk command validation seam must export command metadata validation.");
|
|
assert.match(pluginSdkCommandValidationSource, /export function validateCommandFormValuesSpec/, "plugin-sdk command validation seam must export command form value validation.");
|
|
assert.match(pluginSdkBridgeRuntimeStateSource, /export function buildPluginRuntimePublicState/, "plugin-sdk bridge runtime-state seam must export public state shaping.");
|
|
assert.match(pluginSdkBridgeRuntimeStateSource, /export async function executePluginCommand/, "plugin-sdk bridge runtime-state seam must export command execution helpers.");
|
|
assert.match(pluginSdkBridgeRuntimeStateSource, /export function clearPluginRuntimeState/, "plugin-sdk bridge runtime-state seam must export cleanup helpers.");
|
|
assert.match(pluginSdkRuntimeHelpersSource, /export function nextScheduleDelayMs/, "plugin-sdk runtime helper seam must own schedule delay calculation.");
|
|
assert.match(pluginSdkStorageStoresSource, /export class JsonPluginStorageStore/, "plugin-sdk storage-store seam must export the JSON-backed store.");
|
|
assert.match(pluginSdkStorageStoresSource, /export class MemoryPluginStorageStore/, "plugin-sdk storage-store seam must export the in-memory store.");
|
|
assert.match(agentSetupSource, /from "\.\/agent-setup-actions\.js"/, "agent-setup must import the extracted action seam.");
|
|
assert.match(agentSetupSource, /from "\.\/agent-setup-claude-status(?:\.js)?"/, "agent-setup must import the extracted Claude status seam.");
|
|
assert.match(agentSetupClaudeStatusSource, /from "\.\/agent-setup-command-runner(?:\.js)?"/, "agent-setup Claude status seam must import the extracted command-runner seam.");
|
|
assert.match(agentSetupCommandRunnerSource, /export async function runAgentSetupCommand/, "agent-setup command-runner seam must export command execution.");
|
|
assert.match(agentSetupCommandRunnerSource, /export function createAgentSetupCommandEnv/, "agent-setup command-runner seam must export PATH environment shaping.");
|
|
assert.match(agentSetupCommandRunnerSource, /export function summarizeAgentSetupCommandResult/, "agent-setup command-runner seam must export command result summarization.");
|
|
assert.match(agentSetupActionsSource, /export async function executeAgentSetupResolvedAction/, "agent-setup action seam must export action execution.");
|
|
assert.match(agentSetupActionsSource, /from "\.\/agent-setup-actions-tooling(?:\.js)?"/, "agent-setup action seam must import the extracted tooling seam.");
|
|
assert.match(agentSetupActionsSource, /from "\.\/agent-setup-actions-claude-mcp(?:\.js)?"/, "agent-setup action seam must import the extracted Claude MCP action seam.");
|
|
assert.match(agentSetupActionsClaudeMcpSource, /export async function runClaudeMcpAction/, "agent-setup Claude MCP seam must export add/configure/replace orchestration.");
|
|
assert.match(agentSetupActionsClaudeMcpSource, /export async function runRemoveOnlyClaudeMcpAction/, "agent-setup Claude MCP seam must export the remove-only orchestration.");
|
|
assert.match(agentSetupClaudeStatusSource, /export function sanitizeAgentSetupOutput/, "agent-setup Claude status seam must export output sanitization.");
|
|
assert.match(agentSetupClaudeStatusSource, /export function safeBuildClaudeMcpPreview/, "agent-setup Claude status seam must export preview shaping.");
|
|
assert.match(agentSetupClaudeStatusSource, /export function safeDoctorClaudeHooks/, "agent-setup Claude status seam must export hook doctor wrappers.");
|
|
assert.match(agentSetupClaudeStatusSource, /export async function detectClaudeCodeStatus/, "agent-setup Claude status seam must export Claude detection.");
|
|
assert.match(agentSetupClaudeStatusSource, /export function createBundledResourceErrorStatus/, "agent-setup Claude status seam must export packaged-resource error shaping.");
|
|
assert.match(agentSetupClaudeStatusSource, /export function createHookErrorStatus/, "agent-setup Claude status seam must export hook error shaping.");
|
|
assert.match(agentSetupClaudeStatusSource, /export async function runClaudeCommand/, "agent-setup Claude status seam must export Claude command execution.");
|
|
assert.match(agentSetupClaudeStatusSource, /export function runCommand/, "agent-setup Claude status seam must export generic command execution.");
|
|
assert.match(agentSetupClaudeStatusSource, /export function summarizeCommandResult/, "agent-setup Claude status seam must export command result summarization.");
|
|
assert.match(agentSetupClaudeStatusSource, /export function formatUserPath/, "agent-setup Claude status seam must export user-path formatting.");
|
|
assert.match(agentSetupActionsToolingSource, /export async function getOpenCodeSetup/, "agent-setup tooling seam must export OpenCode setup loading.");
|
|
assert.match(agentSetupActionsToolingSource, /export async function getCursorSetup/, "agent-setup tooling seam must export Cursor setup loading.");
|
|
assert.match(agentSetupActionsToolingSource, /export async function runAgentSetupMcpServerHealthCheck/, "agent-setup tooling seam must export MCP server health checks.");
|
|
assert.match(agentSetupEditorToolsSource, /from "\.\/agent-setup-editor-tools-opencode(?:\.js)?"/, "agent-setup editor helper barrel must compose the extracted OpenCode seam.");
|
|
assert.match(agentSetupEditorToolsSource, /from "\.\/agent-setup-editor-tools-cursor(?:\.js)?"/, "agent-setup editor helper barrel must compose the extracted Cursor seam.");
|
|
assert.match(agentSetupEditorToolsOpenCodeSource, /export function buildOpenCodeSetupSnapshot/, "agent-setup OpenCode helper seam must export setup snapshot building.");
|
|
assert.match(agentSetupEditorToolsOpenCodeSource, /export function installOpenCodeGlobalConfig/, "agent-setup OpenCode helper seam must export config writes.");
|
|
assert.match(agentSetupEditorToolsCursorSource, /export function buildCursorSetupSnapshot/, "agent-setup Cursor helper seam must export setup snapshot building.");
|
|
assert.match(agentSetupEditorToolsCursorSource, /export function replaceCursorGlobalConfig/, "agent-setup Cursor helper seam must export replace writes.");
|
|
assert.match(pluginServiceDefaultPetSource, /export async function buildDefaultPetPluginCommands/, "plugin-service default-pet helper must export default familiar command shaping.");
|
|
assert.match(pluginFamiliarRegistrySource, /from "\.\/plugin-familiar-registry-actions(?:\.js)?"/, "plugin familiar registry must compose the extracted action seam.");
|
|
assert.match(pluginFamiliarRegistryActionsSource, /export function reactPluginPetAction/, "plugin familiar action seam must export reaction routing.");
|
|
assert.match(pluginFamiliarRegistryActionsSource, /export async function movePluginPetByAction/, "plugin familiar action seam must export bounded motion routing.");
|
|
assert.match(pluginFamiliarRegistryActionsSource, /export function getPluginPetArbiterAction/, "plugin familiar action seam must export bubble arbiter routing.");
|
|
assert.match(pluginServiceSupportSource, /from "\.\/plugin-service-actions(?:\.js)?"/, "plugin-service support barrel must re-export the service action seam.");
|
|
assert.match(pluginServiceActionsSource, /export async function buildPluginCatalogSnapshot/, "plugin-service action seam must export catalog snapshot shaping.");
|
|
assert.match(pluginServiceActionsSource, /export async function installOrUpdateCatalogPlugin/, "plugin-service action seam must export catalog install and update flows.");
|
|
assert.match(pluginServiceActionsSource, /export async function pickPluginConfigSound/, "plugin-service action seam must export config sound picker flows.");
|
|
assert.match(pluginServiceActionsSource, /export async function uninstallPlugin/, "plugin-service action seam must export uninstall flows.");
|
|
assert.match(pluginServiceSupportSource, /buildDefaultPetPluginCommands/, "plugin-service support barrel must expose default-pet helpers.");
|
|
assert.match(pluginServiceSupportSource, /buildPluginCatalogSnapshot/, "plugin-service support barrel must expose service action helpers.");
|
|
assert.match(pluginServiceSupportSource, /from "\.\/plugin-service-bundled-lifecycle(?:\.js)?"/, "plugin-service support barrel must re-export the bundled lifecycle seam.");
|
|
assert.match(pluginServiceSupportSource, /bundledOfficialPluginIds/, "plugin-service support barrel must expose bundled plugin ids.");
|
|
assert.match(pluginServiceSupportSource, /pruneStaleBundledPluginRecords/, "plugin-service support barrel must expose bundled stale-prune orchestration.");
|
|
assert.match(pluginServiceSupportSource, /seedBundledPluginRecords/, "plugin-service support barrel must expose bundled seeding orchestration.");
|
|
assert.match(pluginServiceSupportSource, /seedBundledPluginSourceToInstall/, "plugin-service support barrel must expose bundled install helpers.");
|
|
assert.match(pluginServiceSupportSource, /updateCatalogPluginRecord/, "plugin-service support barrel must expose catalog metadata helpers.");
|
|
assert.match(pluginServiceSupportSource, /buildSafePluginRecord/, "plugin-service support barrel must expose snapshot helpers.");
|
|
assert.match(pluginServiceSupportSource, /safeCommandError/, "plugin-service support barrel must expose text helpers.");
|
|
assert.match(pluginServiceSupportSource, /defaultConfirmPermissions/, "plugin-service support barrel must expose dialog permission helpers.");
|
|
assert.match(pluginServiceSupportSource, /resolveSoundDialogOptions/, "plugin-service support barrel must expose dialog picker helpers.");
|
|
assert.match(pluginServiceSupportSource, /loadLocalPluginPath/, "plugin-service support barrel must expose local plugin path loading helpers.");
|
|
assert.match(pluginServiceSupportSource, /loadLocalPluginRoots/, "plugin-service support barrel must expose local plugin root scanning helpers.");
|
|
assert.match(pluginServiceBundledLifecycleSource, /export async function pruneStaleBundledPluginRecords/, "plugin-service bundled lifecycle seam must export stale-prune orchestration.");
|
|
assert.match(pluginServiceBundledLifecycleSource, /export async function seedBundledPluginRecords/, "plugin-service bundled lifecycle seam must export bundled seeding orchestration.");
|
|
assert.match(pluginServiceBundledLifecycleSource, /export const bundledOfficialPluginIds/, "plugin-service bundled lifecycle seam must own the official bundled plugin id list.");
|
|
assert.match(pluginServiceBundledLifecycleSource, /from "\.\/plugin-service-bundled-support(?:\.js)?"/, "plugin-service bundled lifecycle seam must compose the bundled support helper seam.");
|
|
assert.match(pluginServiceBundledSupportSource, /export function findBundledPluginSourceFolder/, "plugin-service bundled support seam must export bundled source lookups.");
|
|
assert.match(pluginServiceBundledSupportSource, /export async function seedBundledPluginSourceToInstall/, "plugin-service bundled support seam must export bundled install seeding.");
|
|
assert.match(pluginServiceCatalogSupportSource, /export function isCatalogEntryCompatible/, "plugin-service catalog support seam must export version compatibility checks.");
|
|
assert.match(pluginServiceCatalogSupportSource, /export function updateCatalogPluginRecord/, "plugin-service catalog support seam must export catalog metadata record updates.");
|
|
assert.match(pluginServiceDialogSupportSource, /export async function defaultOpenDialog/, "plugin-service dialog support seam must export the default folder picker.");
|
|
assert.match(pluginServiceDialogSupportSource, /export async function defaultConfirmPermissions/, "plugin-service dialog support seam must export the permission confirmation picker.");
|
|
assert.match(pluginServiceDialogSupportSource, /export function resolveSoundDialogOptions/, "plugin-service dialog support seam must export sound dialog option resolution.");
|
|
assert.match(pluginServiceLocalSupportSource, /export async function loadLocalPluginPath/, "plugin-service local support seam must export direct local plugin loads.");
|
|
assert.match(pluginServiceLocalSupportSource, /export async function loadLocalPluginRoots/, "plugin-service local support seam must export plugin root scanning.");
|
|
assert.match(pluginServiceLocalSupportSource, /export async function pruneStaleDevLocalPlugins/, "plugin-service local support seam must export stale dev-plugin pruning.");
|
|
assert.match(pluginServiceSnapshotSource, /export async function buildSafePluginRecord/, "plugin-service snapshot helper must export safe plugin snapshot shaping.");
|
|
assert.match(pluginServiceSnapshotSource, /export function validatePluginServiceConfigReplacement/, "plugin-service snapshot helper must export config validation delegation.");
|
|
assert.match(pluginServiceTextHelpersSource, /export async function ensurePluginCommandLocale/, "plugin-service text helper must export plugin locale preloading.");
|
|
assert.match(pluginServiceTextHelpersSource, /export function safeCommandError/, "plugin-service text helper must export command error sanitization.");
|
|
assert.match(pluginManifestSource, /from "\.\/plugin-manifest-config-validation(?:\.js)?"/, "plugin-manifest must import the extracted config/action validation seam.");
|
|
assert.match(pluginManifestSource, /from "\.\/plugin-manifest-shape-validation(?:\.js)?"/, "plugin-manifest must import the extracted shape validation seam.");
|
|
assert.match(pluginManifestConfigValidationSource, /export function validateConfigSchema/, "plugin-manifest config validation seam must export config schema validation.");
|
|
assert.match(pluginManifestConfigValidationSource, /export function validateTriggers/, "plugin-manifest config validation seam must export trigger/action validation.");
|
|
assert.match(pluginManifestShapeValidationSource, /export function validateAssets/, "plugin-manifest shape validation seam must export asset validation.");
|
|
assert.match(pluginManifestShapeValidationSource, /export function validatePanels/, "plugin-manifest shape validation seam must export panel validation.");
|
|
assert.match(pluginManifestShapeValidationSource, /export function validatePluginIcon/, "plugin-manifest shape validation seam must export icon validation.");
|
|
assert.match(pluginManifestShapeValidationSource, /export function validateEntryPath/, "plugin-manifest shape validation seam must export entry path validation.");
|
|
assert.match(pluginManifestShapeValidationSource, /export function validateNetwork/, "plugin-manifest shape validation seam must export network validation.");
|
|
assert.match(pluginManifestShapeValidationSource, /export function validatePermissions/, "plugin-manifest shape validation seam must export shared permission validation.");
|
|
assert.match(pluginManifestShapeValidationSource, /export function rejectUnknownFields/, "plugin-manifest shape validation seam must export unknown-field rejection.");
|
|
assert.match(pluginManifestShapeValidationSource, /export function validateString/, "plugin-manifest shape validation seam must export string validation.");
|
|
assert.match(pluginManifestShapeValidationSource, /export function addError/, "plugin-manifest shape validation seam must export manifest error shaping.");
|
|
assert.match(pluginManifestShapeValidationSource, /export function isRecord/, "plugin-manifest shape validation seam must export record guards.");
|
|
assert.match(controlCenterPluginsViewSource, /from "\.\/plugins-view-presentation"/, "plugins route must import the extracted presentation seam.");
|
|
assert.match(controlCenterPluginsViewSource, /from "\.\/plugins-view-config-helpers"/, "plugins route must import the extracted config helper seam.");
|
|
assert.match(controlCenterPluginsViewSource, /from "\.\/plugins-view-state"/, "plugins route must import the extracted route state seam.");
|
|
assert.match(controlCenterPluginsViewSource, /from "\.\/plugins-view-config-fields"/, "plugins route must import the extracted config-field seam.");
|
|
assert.match(controlCenterPluginsViewPresentationSource, /export const pluginFilterLabelKeys/, "plugins presentation seam must export filter labels.");
|
|
assert.match(controlCenterPluginsViewPresentationSource, /export const pluginPermissionLabelKeys/, "plugins presentation seam must export permission labels.");
|
|
assert.match(controlCenterPluginsViewPresentationSource, /export function PluginIconImage/, "plugins presentation seam must export icon rendering helpers.");
|
|
assert.match(controlCenterPluginsViewPresentationSource, /export function pluginPrimaryLabel/, "plugins presentation seam must export primary badge labels.");
|
|
assert.match(controlCenterPluginsViewPresentationSource, /export function pluginDescription/, "plugins presentation seam must export plugin description shaping.");
|
|
assert.match(controlCenterPluginsViewStateSource, /export function usePluginsViewState/, "plugins route state seam must export the route state hook.");
|
|
assert.match(controlCenterPluginsViewStateSource, /export function mergePluginEntries/, "plugins route state seam must export merged plugin entry shaping.");
|
|
assert.match(controlCenterPluginsViewStateSource, /from "\.\/route-data-loaders"/, "plugins route state seam must load route data through shared route loaders.");
|
|
assert.match(controlCenterPluginsViewConfigFieldsSource, /from "\.\/plugins-view-config-list-editor"/, "plugins config-field seam must import the extracted list editor seam.");
|
|
assert.match(controlCenterPluginsViewConfigListEditorSource, /export function ConfigListFieldEditor/, "plugins config list seam must export the list editor.");
|
|
assert.match(controlCenterPluginsViewStateSource, /installCatalogEntry/, "plugins route state seam must own catalog install actions.");
|
|
assert.match(controlCenterPluginsViewStateSource, /updateCatalogEntry/, "plugins route state seam must own catalog update actions.");
|
|
assert.match(controlCenterPluginsViewStateSource, /loadLocalPlugin/, "plugins route state seam must own local plugin import actions.");
|
|
assert.match(controlCenterPluginsViewConfigHelpersSource, /export function materializeConfigDraft/, "plugins config helper seam must export config draft materialization.");
|
|
assert.match(controlCenterPluginsViewConfigFieldsSource, /export function ConfigFieldEditor/, "plugins config-field seam must export the config editor.");
|
|
assert.match(controlCenterPluginsViewConfigListEditorSource, /from "\.\/plugins-view-config-helpers"/, "plugins config list seam must consume the extracted config helpers.");
|
|
assert.match(controlCenterRendererSource, /from "\.\/control-center\/route-loaders"/, "Control Center shell must load the shared route loader module.");
|
|
assert.match(controlCenterRouteLoadersSource, /await import\("\.\/settings-view"\)/, "Control Center route loaders must lazy-load the settings page module.");
|
|
assert.match(controlCenterRouteLoadersSource, /React\.lazy\(\(\) => loadControlCenterRoute\("settings"\)\)/, "Control Center route shell must expose a lazy settings route component.");
|
|
assert.match(controlCenterSharedSource, /export \* from "\.\/shared-ui"/, "Control Center shared barrel must re-export the extracted shared UI surface.");
|
|
assert.match(controlCenterSharedSource, /export \* from "\.\/shared-openapi"/, "Control Center shared barrel must re-export the extracted OpenAPI/theme helper surface.");
|
|
assert.match(controlCenterSharedUiSource, /from "\.\/shared-ui-icons"/, "Shared UI shell must import the extracted icon seam.");
|
|
assert.match(controlCenterSharedUiSource, /from "\.\/shared-ui-navigation"/, "Shared UI shell must import the extracted navigation seam.");
|
|
assert.match(controlCenterSharedUiSource, /export const navTabs/, "Shared UI shell must keep exporting navigation tabs through the shared barrel.");
|
|
assert.match(controlCenterSharedUiSource, /export function Button/, "Shared UI shell must keep exporting the shared button atom.");
|
|
assert.match(controlCenterSharedUiSource, /export function SearchInput/, "Shared UI shell must keep exporting the translated shared search input.");
|
|
assert.match(controlCenterSharedUiIconsSource, /export const DashboardIcon/, "Shared UI icon seam must export dashboard navigation icons.");
|
|
assert.match(controlCenterSharedUiIconsSource, /export const Spinner/, "Shared UI icon seam must export the shared spinner.");
|
|
assert.match(controlCenterSharedUiNavigationSource, /export const navTabs/, "Shared UI navigation seam must export Control Center navigation metadata.");
|
|
assert.match(controlCenterSharedUiNavigationSource, /export function initialControlCenterRoute/, "Shared UI navigation seam must export initial route resolution.");
|
|
assert.match(controlCenterSharedOpenApiSource, /from "\.\.\/mcp-toolkit-catalog"/, "Control Center shared OpenAPI helpers must compose the toolkit catalog surface.");
|
|
assert.match(mcpToolkitCatalogSource, /from "\.\/mcp-toolkit-catalog-data(?:\.js)?"/, "MCP toolkit catalog surface must import the extracted data seam.");
|
|
assert.match(mcpToolkitCatalogSource, /from "\.\/mcp-toolkit-catalog-types(?:\.js)?"/, "MCP toolkit catalog surface must import the shared toolkit type seam.");
|
|
assert.match(mcpToolkitCatalogSource, /export function getMcpToolkitEntry/, "MCP toolkit catalog surface must keep exporting the entry lookup helper.");
|
|
assert.match(mcpToolkitCatalogDataSource, /from "\.\/mcp-toolkit-catalog-starter(?:\.js)?"/, "MCP toolkit catalog data seam must import the starter tier seam.");
|
|
assert.match(mcpToolkitCatalogDataSource, /from "\.\/mcp-toolkit-catalog-system(?:\.js)?"/, "MCP toolkit catalog data seam must import the system tier seam.");
|
|
assert.match(mcpToolkitCatalogDataSource, /from "\.\/mcp-toolkit-catalog-advanced(?:\.js)?"/, "MCP toolkit catalog data seam must import the advanced tier seam.");
|
|
assert.match(mcpToolkitCatalogStarterSource, /export const mcpToolkitStarterEntries/, "MCP toolkit starter seam must export the starter entry group.");
|
|
assert.match(mcpToolkitCatalogSystemSource, /export const mcpToolkitSystemEntries/, "MCP toolkit system seam must export the system entry group.");
|
|
assert.match(mcpToolkitCatalogAdvancedSource, /export const mcpToolkitAdvancedEntries/, "MCP toolkit advanced seam must export the advanced entry group.");
|
|
assert.match(mcpToolkitCatalogTypesSource, /export type McpToolkitEntry/, "MCP toolkit catalog types seam must export the toolkit entry contract.");
|
|
assert.match(mcpToolkitCatalogDataSource, /export const mcpToolkitEntries/, "MCP toolkit catalog data seam must export the curated toolkit entries.");
|
|
assert.match(mcpToolkitCatalogStarterSource, /export const vectorShellStarterIds/, "MCP toolkit starter seam must export the curated starter ids.");
|
|
assert.match(controlCenterFamiliarsSource, /from "\.\/familiars-view-state"/, "Control Center familiars route must import the extracted familiars state seam.");
|
|
assert.match(controlCenterFamiliarsSource, /from "\.\/familiars-view-detail-dialog"/, "Control Center familiars route must import the extracted detail dialog seam.");
|
|
assert.match(controlCenterFamiliarsDetailDialogSource, /export function FamiliarDetailDialog/, "Control Center familiars detail dialog seam must export the selected-familiar overlay.");
|
|
assert.match(controlCenterFamiliarsStateSource, /export function useFamiliarsViewState/, "Control Center familiars state seam must export the route hook.");
|
|
assert.match(controlCenterFamiliarsStateSource, /from "\.\/route-data-loaders"/, "Control Center familiars state seam must compose shared route data loaders.");
|
|
assert.match(controlCenterFamiliarsStateSource, /from "\.\/familiars-view-effects"/, "Control Center familiars state seam must compose the extracted familiars effects seam.");
|
|
assert.match(controlCenterFamiliarsStateSource, /from "\.\/familiars-view-helpers"/, "Control Center familiars state seam must compose the extracted familiars helper seam.");
|
|
assert.match(controlCenterFamiliarsStateSource, /loadPetsData/, "Control Center familiars state seam must own route data refresh.");
|
|
assert.match(controlCenterFamiliarsStateSource, /loadCatalogPage/, "Control Center familiars state seam must own paged catalog loading.");
|
|
assert.match(controlCenterFamiliarsStateSource, /installCatalogPet/, "Control Center familiars state seam must own catalog install actions.");
|
|
assert.match(controlCenterFamiliarsStateSource, /importCodexPet/, "Control Center familiars state seam must own Codex import actions.");
|
|
assert.match(controlCenterFamiliarsStateSource, /setDefaultPet/, "Control Center familiars state seam must own default familiar actions.");
|
|
assert.match(controlCenterFamiliarsStateSource, /removePet/, "Control Center familiars state seam must own removal actions.");
|
|
assert.match(controlCenterFamiliarsStateSource, /petDetailDialogRef/, "Control Center familiars state seam must own the detail dialog focus trap.");
|
|
assert.match(controlCenterFamiliarsHelpersSource, /export function buildVisibleFamiliars/, "Control Center familiars helper seam must export visible familiar shaping.");
|
|
assert.match(controlCenterFamiliarsHelpersSource, /export function collectNeededCatalogPages/, "Control Center familiars helper seam must export remote page planning.");
|
|
assert.match(controlCenterFamiliarsHelpersSource, /export function installFamiliarsDetailFocusTrap/, "Control Center familiars helper seam must export the detail focus trap.");
|
|
assert.match(controlCenterFamiliarsHelpersSource, /export function getFamiliarStatusText/, "Control Center familiars helper seam must export familiar status text shaping.");
|
|
assert.match(controlCenterFamiliarsEffectsSource, /export function useFamiliarsDetailFocusTrap/, "Control Center familiars effects seam must export the detail focus trap hook.");
|
|
assert.match(controlCenterFamiliarsEffectsSource, /export function useSelectedFamiliarLogging/, "Control Center familiars effects seam must export selected familiar logging.");
|
|
assert.match(controlCenterFamiliarsEffectsSource, /export function useCatalogSearchIndex/, "Control Center familiars effects seam must export catalog search index hydration.");
|
|
assert.match(controlCenterFamiliarsEffectsSource, /export function useCatalogSearchPrefetch/, "Control Center familiars effects seam must export catalog search prefetch behavior.");
|
|
assert.match(controlCenterRouteDataLoadersSource, /getPetsState/, "Control Center shared route data loaders must bridge familiars state APIs.");
|
|
assert.match(controlCenterIntegrationsSource, /export function IntegrationsView\(\)/, "Control Center must include the integrations page.");
|
|
assert.match(controlCenterIntegrationsSource, /from "\.\/integrations-view-detail-dialog"/, "Control Center integrations route must import the extracted detail dialog seam.");
|
|
assert.match(controlCenterIntegrationsSource, /from "\.\/integrations-view-state"/, "Control Center integrations route must import the extracted integrations state seam.");
|
|
assert.match(controlCenterIntegrationsSource, /from "\.\/integrations-view-shared"/, "Control Center integrations route must import the extracted integrations shared seam.");
|
|
assert.match(controlCenterIntegrationsDetailDialogSource, /export function IntegrationsDetailDialog/, "Control Center integrations detail dialog seam must export the selected-integration overlay.");
|
|
assert.match(controlCenterIntegrationsDetailDialogSource, /from "\.\/integrations-view-agent-sections"/, "Control Center integrations detail dialog seam must import the extracted agent sections.");
|
|
assert.match(controlCenterIntegrationsAgentSectionsSource, /export function IntegrationCommandModeSection/, "Control Center integrations agent seam must export the command-mode notice.");
|
|
assert.match(controlCenterIntegrationsDetailDialogSource, /from "\.\/integrations-view-tooling-sections"/, "Control Center integrations detail dialog seam must import the extracted integrations tooling seam.");
|
|
assert.match(controlCenterIntegrationsStateSource, /export function useIntegrationsViewState/, "Control Center integrations state seam must export the route hook.");
|
|
assert.match(controlCenterIntegrationsStateSource, /from "\.\/route-data-loaders"/, "Control Center integrations state seam must compose shared route data loaders.");
|
|
assert.match(controlCenterIntegrationsStateSource, /from "\.\/integrations-view-shared"/, "Control Center integrations state seam must compose the extracted integrations shared helpers.");
|
|
assert.match(controlCenterIntegrationsToolingSectionsSource, /from "\.\/integrations-view-mcp-toolkit-section"/, "Control Center integrations tooling shell must import the extracted MCP toolkit section seam.");
|
|
assert.match(controlCenterIntegrationsToolingSectionsSource, /export function McpToolkitSection/, "Control Center integrations tooling seam must own the curated toolkit panel.");
|
|
assert.match(controlCenterIntegrationsMcpToolkitSectionSource, /export function McpToolkitSection/, "Control Center integrations toolkit seam must export the curated toolkit panel.");
|
|
assert.match(controlCenterIntegrationsMcpToolkitSectionSource, /buildPersistentToolkitBundle/, "Control Center integrations toolkit seam must own persistent bundle composition.");
|
|
assert.match(controlCenterIntegrationsCombinedSource, /Persistent Full Access/, "Control Center integrations seam must retain the persistent toolkit install path.");
|
|
assert.match(controlCenterIntegrationsCombinedSource, /pi install npm:@familiaros\/pi/, "Control Center integrations seam must retain Pi install guidance.");
|
|
assert.match(mcpToolkitInstallerSource, /from "\.\/mcp-toolkit-installer-support(?:\.js)?"/, "MCP toolkit installer must import the extracted support seam.");
|
|
assert.match(mcpToolkitInstallerSource, /from "\.\/mcp-toolkit-installer-commands(?:\.js)?"/, "MCP toolkit installer must import the extracted command seam.");
|
|
assert.match(mcpToolkitInstallerSupportSource, /export function getSupportedPersistentServers/, "MCP toolkit installer support seam must export managed server definitions.");
|
|
assert.match(mcpToolkitInstallerSupportSource, /export function buildInstallCommands/, "MCP toolkit installer support seam must export bundle command shaping.");
|
|
assert.match(mcpToolkitInstallerCommandsSource, /export function runCommand/, "MCP toolkit installer command seam must export process execution helpers.");
|
|
assert.match(mcpToolkitInstallerCommandsSource, /export async function runCommandWithCandidates/, "MCP toolkit installer command seam must export command discovery helpers.");
|
|
assert.match(openApiChatSource, /from "\.\/openapi-chat-settings(?:\.js)?"/, "OpenAPI chat must import the extracted settings seam.");
|
|
assert.match(openApiChatSource, /from "\.\/openapi-chat-prompt-flows(?:\.js)?"/, "OpenAPI chat must import the extracted prompt-flow seam.");
|
|
assert.match(openApiChatPromptFlowsSource, /from "\.\/openapi-chat-prompt-plain-flow(?:\.js)?"/, "OpenAPI chat prompt-flow seam must compose the extracted plain-flow seam.");
|
|
assert.match(openApiChatPromptFlowsSource, /export \{[\s\S]*?sendOpenApiChatPromptPlainFlow[\s\S]*?\} from "\.\/openapi-chat-prompt-plain-flow(?:\.js)?"/, "OpenAPI chat prompt-flow seam must re-export the plain prompt runner.");
|
|
assert.match(openApiChatPromptFlowsSource, /export async function sendOpenApiChatPromptWithToolsFlow/, "OpenAPI chat prompt-flow seam must export the tool-enabled prompt runner.");
|
|
assert.match(openApiChatPromptFlowsSource, /export \{[\s\S]*?buildOpenApiInstructions[\s\S]*?\} from "\.\/openapi-chat-prompt-plain-flow(?:\.js)?"/, "OpenAPI chat prompt-flow seam must re-export instruction building.");
|
|
assert.match(openApiChatPromptFlowsSource, /from "\.\/openapi-chat-presentation(?:\.js)?"/, "OpenAPI chat prompt flows must import the extracted presentation seam.");
|
|
assert.match(openApiChatPromptFlowsSource, /from "\.\/openapi-chat-provider(?:\.js)?"/, "OpenAPI chat prompt flows must import the extracted provider seam.");
|
|
assert.match(openApiChatPromptFlowsSource, /from "\.\/openapi-chat-tool-loop(?:\.js)?"/, "OpenAPI chat prompt flows must import the extracted tool loop seam.");
|
|
assert.match(openApiChatProviderSource, /export \* from "\.\/openapi-chat-provider-support(?:\.js)?"/, "OpenAPI chat provider barrel must re-export the extracted provider support seam.");
|
|
assert.match(openApiChatProviderSupportSource, /export \* from "\.\/openapi-chat-provider-routing(?:\.js)?"/, "OpenAPI chat provider support seam must re-export routing helpers.");
|
|
assert.match(openApiChatProviderSupportSource, /export \* from "\.\/openapi-chat-provider-payloads(?:\.js)?"/, "OpenAPI chat provider support seam must re-export payload helpers.");
|
|
assert.match(openApiChatProviderRoutingSource, /export function buildRequestAttempts/, "OpenAPI chat provider routing seam must export request-attempt routing.");
|
|
assert.match(openApiChatProviderRoutingSource, /export async function readProviderError/, "OpenAPI chat provider routing seam must export provider error normalization.");
|
|
assert.match(openApiChatProviderPayloadsSource, /export function parseResponsesPayload/, "OpenAPI chat provider payload seam must export responses payload parsing.");
|
|
assert.match(openApiChatProviderPayloadsSource, /export function parseChatCompletionsPayload/, "OpenAPI chat provider payload seam must export chat-completions payload parsing.");
|
|
assert.match(openApiChatProviderPayloadsSource, /export function extractChatCompletionsChoice/, "OpenAPI chat provider payload seam must export tool-call extraction.");
|
|
assert.match(openApiChatPromptPlainFlowSource, /export async function sendOpenApiChatPromptPlainFlow/, "OpenAPI chat plain-flow seam must export the plain prompt runner.");
|
|
assert.match(openApiChatPromptPlainFlowSource, /export function buildOpenApiInstructions/, "OpenAPI chat plain-flow seam must export instruction building.");
|
|
assert.match(openApiChatPromptPlainFlowSource, /from "\.\/openapi-chat-presentation(?:\.js)?"/, "OpenAPI chat plain-flow seam must import the extracted presentation seam.");
|
|
assert.match(openApiChatPromptPlainFlowSource, /from "\.\/openapi-chat-provider(?:\.js)?"/, "OpenAPI chat plain-flow seam must import the extracted provider seam.");
|
|
assert.match(openApiChatPromptPlainFlowSource, /from "\.\/openapi-chat-request-helpers(?:\.js)?"/, "OpenAPI chat plain-flow seam must import the extracted request helper seam.");
|
|
assert.match(openApiChatSettingsSource, /from "\.\/openapi-chat-config-store(?:\.js)?"/, "OpenAPI chat settings seam must import the extracted config-store seam.");
|
|
assert.match(openApiChatSettingsSource, /export function getOpenApiChatSettingsSnapshot/, "OpenAPI chat settings seam must export the settings snapshot helper.");
|
|
assert.match(openApiChatSettingsSource, /export function storeOpenApiCredential/, "OpenAPI chat settings seam must export credential persistence.");
|
|
assert.match(openApiChatSettingsSource, /from "\.\/openapi-chat-model(?:\.js)?"/, "OpenAPI chat settings seam must import the extracted model normalization helper.");
|
|
assert.match(openApiChatSettingsSource, /export \{ normalizeChatModel \} from "\.\/openapi-chat-model(?:\.js)?"/, "OpenAPI chat settings seam must re-export model normalization.");
|
|
assert.match(openApiChatModelSource, /export function normalizeChatModel/, "OpenAPI chat model seam must export model normalization.");
|
|
assert.match(openApiChatPresentationSource, /export function handleOpenApiFailure/, "OpenAPI chat presentation seam must export failure presentation.");
|
|
assert.match(openApiChatPresentationSource, /export function speakAssistantResponse/, "OpenAPI chat presentation seam must export assistant TTS presentation.");
|
|
assert.match(openApiChatConfigStoreSource, /export function readStoredOpenApiChatConfig/, "OpenAPI chat config-store seam must export config reads.");
|
|
assert.match(openApiChatConfigStoreSource, /export function writeStoredOpenApiChatConfig/, "OpenAPI chat config-store seam must export config writes.");
|
|
assert.match(openApiChatConfigStoreSource, /export function getStoredOpenApiCredential/, "OpenAPI chat config-store seam must export credential reads.");
|
|
assert.match(openApiChatToolLoopSource, /export async function runToolEnabledOpenApiChatLoop/, "OpenAPI chat tool loop seam must export the async tool runner.");
|
|
assert.match(openApiChatToolLoopSource, /export function buildOpenApiInstructionsWithTools/, "OpenAPI chat tool loop seam must export tool-aware instruction shaping.");
|
|
assert.match(openApiChatRequestHelpersSource, /export function buildRequestBody/, "OpenAPI chat request helper seam must export request body shaping.");
|
|
assert.match(openApiChatSource, /from "\.\/openapi-chat-conversation-store(?:\.js)?"/, "OpenAPI chat must import the extracted conversation store seam.");
|
|
assert.match(openApiChatConversationStoreSource, /export function createOpenApiChatConversationStore/, "OpenAPI chat conversation store must export the extracted store factory.");
|
|
assert.match(controlCenterRouteDataLoadersSource, /getIntegrationsState/, "Control Center shared route data loaders must bridge integrations state APIs.");
|
|
assert.match(petWindowRenderCombinedSource, /--bubble-max-width:/, "familiar window must define a scalable bubble max-width variable.");
|
|
assert.match(petWindowRenderCombinedSource, /\.bubble\.is-very-long-message \{[\s\S]*?max-width:\s*min\([^)]*var\(--bubble-max-width\)/, "very long message bubbles must stay capped within the familiar window while allowing larger familiars.");
|
|
assert.match(petWindowRenderCombinedSource, /\.bubble-body \{[\s\S]*?overflow-y:\s*auto/, "bubble bodies must be scrollable for long messages.");
|
|
assert.match(petWindowContentCombinedSource, /createSpriteStateCss\("\.sprite"\)/, "built-in sprite CSS must react to reaction state.");
|
|
assert.match(petWindowContentCombinedSource, /createSpriteStateCss\("\.installed-sprite"\)/, "installed sprite CSS must react to reaction state.");
|
|
assert.match(petWindowRenderCombinedSource, /html\[data-motion-state=\"\$\{motion\}\"\] \$\{selector\}/, "sprite CSS must let drag motion override reaction state.");
|
|
assert.match(petWindowRenderCombinedSource, /\.sprite, \.installed-sprite, \.bubble/, "reduced-motion CSS must include built-in and installed sprites.");
|
|
assert.match(petWindowSource, /function createAgentPetWindow[\s\S]*?installMotionStatePublisher\(window\)/, "agent familiar windows must publish motion state so dragged non-default familiars run.");
|
|
assert.match(petWindowSource, /loadExplicitPetContent[\s\S]*?state\.preferences\.petScale/, "explicit agent familiar windows must use the saved familiar scale preference.");
|
|
assert.match(petWindowSource, /interface AgentPetWindowOptions[\s\S]*?readonly scale: PetScaleValue/, "new agent familiar windows must receive the current familiar scale explicitly for their first render.");
|
|
assert.match(petWindowSource, /loadExplicitPetContent\(window, options\.petId, options\.display, options\.badge, dismissToken, options\.scale\)/, "agent familiar first render must not fall back to the medium default scale.");
|
|
assert.match(agentPetControllerSourceForLogging, /function getPreferredPetScale\(\): PetScaleValue/, "agent familiar reloads must share one explicit saved scale helper.");
|
|
assert.match(agentPetControllerSourceForLogging, /loadExplicitPetContent\(window, petId, display, badge, getCurrentDismissToken\(petId, display, badge\), scale\)/, "agent familiar refreshes must pass the saved familiar scale explicitly.");
|
|
assert.match(agentPetControllerSourceForLogging, /loadExplicitPetContent\(window, petId, preparedDisplay, statusBadges\.get\(petId\) \?\? null, preparedDisplay\.dismissToken, getPreferredPetScale\(\)\)/, "agent familiar transient updates must pass the saved familiar scale explicitly.");
|
|
assert.match(mappingDoc, /\| 3 \| `waving` \| `waving`, Claude `Notification`\. \|/, "mapping docs must describe waving animation row and notification mapping.");
|
|
assert.match(mappingDoc, /reaction-animation-mapping\.ts/, "mapping docs must reference the shared reaction animation mapping source of truth.");
|
|
assert.match(mappingDoc, /overrid/i, "mapping docs must mention that reaction animation defaults can be overridden in Settings.");
|
|
assert.doesNotMatch(mappingDoc, /bubble-only|currently \*\*bubble states\*\*/i, "mapping docs must not describe reactions as bubble-only.");
|
|
for (const reaction of allowedReactions) {
|
|
const pool = reactionMessagePools[reaction];
|
|
assert.ok(pool.length >= 8, `reaction message pool must include clear variants for: ${reaction}`);
|
|
for (const message of pool) {
|
|
assert.match(message, /^[A-Z]/, `reaction message must start uppercase: ${message}`);
|
|
assert.doesNotMatch(message, /[\r\n]/, `reaction message must be single-line: ${message}`);
|
|
assert.ok(message.length <= 36, `reaction message must stay bubble-friendly: ${message}`);
|
|
}
|
|
}
|
|
assert.equal(pickReactionMessage("success", () => 0), reactionMessagePools.success[0], "reaction message picking must be deterministic when random is injected.");
|
|
assert.doesNotMatch(controlCenterRendererSource, /OnboardingView|getOnboardingSnapshot|completeOnboarding/, "Control Center must not include the removed onboarding route.");
|
|
assert.match(controlCenterRouteLoadersSource, /await import\("\.\/integrations-view"\)/, "Control Center route loaders must include the integrations module.");
|
|
assert.match(controlCenterIpcSource, /from "\.\/control-center-ipc-agent\.js"/, "Control Center IPC installer must import the extracted agent route seam.");
|
|
assert.match(controlCenterIpcSource, /from "\.\/control-center-ipc-plugin\.js"/, "Control Center IPC installer must import the extracted plugin route seam.");
|
|
assert.match(controlCenterIpcSharedSource, /export function assertAllowedSender/, "Control Center IPC shared helper must export sender validation.");
|
|
assert.match(controlCenterIpcPluginSource, /export function installControlCenterPluginHandlers/, "Control Center plugin IPC seam must export its installer.");
|
|
assert.match(controlCenterIpcSettingsSource, /export function installControlCenterSettingsHandlers/, "Control Center settings IPC seam must export its installer.");
|
|
assert.match(controlCenterSettingsActionsSource, /from "\.\/settings-view-action-core"/, "Settings action barrel must import the extracted shared action core seam.");
|
|
assert.match(controlCenterSettingsActionsSource, /from "\.\/settings-view-tts-actions"/, "Settings action barrel must re-export the extracted TTS action seam.");
|
|
assert.match(controlCenterSettingsActionsSource, /from "\.\/settings-view-system-actions"/, "Settings action barrel must re-export the extracted system action seam.");
|
|
assert.match(controlCenterSettingsSource, /from "\.\/settings-view-state"/, "Settings route shell must import the extracted settings state seam.");
|
|
assert.match(controlCenterSettingsStateSource, /export function useSettingsViewState/, "Settings route state seam must export the route hook.");
|
|
assert.match(controlCenterSettingsStateSource, /from "\.\/settings-view-state-actions"/, "Settings route state seam must import the extracted state action seam.");
|
|
assert.match(controlCenterSettingsStateActionsSource, /from "\.\/settings-view-state-plugin-actions"/, "Settings route state action shell must import the plugin action seam.");
|
|
assert.match(controlCenterSettingsStateActionsSource, /from "\.\/settings-view-state-knowledge-actions"/, "Settings route state action shell must import the knowledge action seam.");
|
|
assert.match(controlCenterSettingsStateActionsSource, /export function createSettingsStateActions/, "Settings route state action seam must export the route action factory.");
|
|
assert.match(controlCenterSettingsStateActionsSource, /export function createSettingsKnowledgeActions/, "Settings route state action seam must export the knowledge action factory.");
|
|
assert.match(controlCenterSettingsStatePluginActionsSource, /export function createSettingsPluginActions/, "Settings route state plugin seam must export the plugin action factory.");
|
|
assert.match(controlCenterSettingsStateKnowledgeActionsSource, /export function createSettingsKnowledgeActions/, "Settings route state knowledge seam must export the knowledge action factory.");
|
|
assert.match(controlCenterSettingsStateActionTypesSource, /export type SettingsStateActionOptions/, "Settings route state action type seam must export route action options.");
|
|
assert.match(controlCenterSettingsStateActionTypesSource, /export type SettingsKnowledgeActionOptions/, "Settings route state action type seam must export knowledge action options.");
|
|
assert.match(controlCenterSettingsActionCoreSource, /export async function runSettingsAction/, "Settings action core must export the shared busy action runner.");
|
|
assert.match(controlCenterSettingsTtsActionsSource, /export function saveTtsPreferenceAction/, "Settings TTS action seam must own TTS preference saving.");
|
|
assert.match(controlCenterSettingsSystemActionsSource, /export function updateReactionOverrideAction/, "Settings system action seam must own reaction override mutations.");
|
|
assert.match(controlCenterSettingsTtsGroupSource, /from "\.\/settings-tts-group-rows"/, "Settings TTS group shell must import the extracted row seam.");
|
|
assert.match(controlCenterSettingsTtsGroupSource, /from "\.\/settings-tts-group-types"/, "Settings TTS group shell must import the extracted prop-type seam.");
|
|
assert.match(controlCenterSettingsTtsGroupRowsSource, /export function TtsProviderRow/, "Settings TTS row seam must export the provider row.");
|
|
assert.match(controlCenterSettingsTtsGroupRowsSource, /export function TtsTestRow/, "Settings TTS row seam must export the test row.");
|
|
assert.match(controlCenterSettingsTtsGroupTypesSource, /export type TtsSettingsGroupProps/, "Settings TTS type seam must export the route group props.");
|
|
assert.match(enCatalogSource, /Claude Code/, "Control Center integrations must include Claude Code.");
|
|
assert.match(enCatalogSource, /OpenCode/, "Control Center integrations must include OpenCode.");
|
|
assert.match(enCatalogSource, /Cursor/, "Control Center integrations must include Cursor.");
|
|
assert.match(enCatalogSource, /Pi/, "Control Center integrations must include Pi.");
|
|
assert.match(agentSetupSource, /from "\.\/agent-setup-command-context\.js"/, "Agent setup must import the extracted command context helper seam.");
|
|
assert.match(agentSetupClaudeStatusSource, /getPreferredNodeCommand/, "Agent setup Claude status seam must resolve preferred Node.js command paths through the extracted command context helper.");
|
|
assert.match(agentSetupActionsToolingSource, /from "\.\/agent-setup-editor-tools\.js"/, "Agent setup tooling seam must import the extracted editor setup helper seam.");
|
|
assert.match(agentSetupSource, /agent-setup-support\.js/, "Agent setup must import and re-export the extracted support helper seam.");
|
|
assert.match(agentSetupActionsToolingSource, /getCliPackageVersion/, "Agent setup tooling seam must compose package version metadata through the command context helper.");
|
|
assert.match(agentSetupActionsToolingSource, /buildOpenCodeSetupSnapshot/, "Agent setup tooling seam must delegate OpenCode previews through the editor setup helper.");
|
|
assert.match(agentSetupActionsClaudeMcpSource, /writeActionJournal/, "Agent setup Claude MCP seam must delegate journal writes through the support helper.");
|
|
assert.match(petWindowRenderSource, /from "\.\/familiar-window-render-bubbles(?:\.js)?"/, "familiar-window-render must compose the extracted bubble helper seam.");
|
|
assert.match(petWindowRenderBubblesSource, /export function createBubbleMarkup/, "familiar-window render bubble seam must export the shared bubble markup builder.");
|
|
assert.match(petWindowRenderBubblesSource, /export function getBubbleClassName/, "familiar-window render bubble seam must export bubble length classification.");
|
|
assert.match(petWindowRenderBubblesSource, /export function pluginBubblesCacheKey/, "familiar-window render bubble seam must export stable plugin bubble cache keys.");
|
|
assert.match(agentSetupCommandContextSource, /export function updateStoredAgentSetupCommandPaths/, "Agent setup command context helper must own command path validation and writes.");
|
|
assert.match(agentSetupSupportSource, /export function writeActionJournal/, "Agent setup support helper must own bounded action journal writes.");
|
|
assert.doesNotMatch(agentSetupCombinedSource, /JSON\.parse\(prepared\.configWrite\.content\)/, "OpenCode desktop preview must parse JSONC planned config safely, not JSON.parse.");
|
|
assert.match(controlCenterInternalUiSource, /refreshDefaultPetContent\(\);\s*refreshAgentPetContent\(\);/, "familiar scale preference changes must refresh default and agent familiar windows.");
|
|
assert.ok(existsSync(join(appDir, "scripts", "clean-package-output.cjs")), "package output cleanup helper must exist.");
|
|
assert.ok(existsSync(join(distDir, "main.js")), "desktop main build output must exist before packaging checks run.");
|
|
assert.ok(existsSync(join(repoRoot, "packages", "claude", "dist", "index.js")), "@familiaros/claude must be built before packaging.");
|
|
assert.ok(existsSync(join(repoRoot, "packages", "client", "dist", "index.js")), "@familiaros/client must be built before packaging.");
|
|
assert.ok(existsSync(join(repoRoot, "packages", "mcp", "dist", "index.js")), "@familiaros/mcp must be built before packaging.");
|
|
assert.ok(existsSync(join(repoRoot, "packages", "cli", "dist", "index.js")), "@familiaros/cli must be built before packaging.");
|
|
assert.ok(existsSync(join(repoRoot, "packages", "opencode", "dist", "plugin.js")), "@familiaros/opencode plugin must be built before packaging.");
|
|
assert.ok(existsSync(join(repoRoot, "packages", "agent-events", "dist", "index.js")), "@familiaros/agent-events must be built before packaging.");
|
|
|
|
if (process.argv.includes("--output")) {
|
|
checkPackageOutput();
|
|
} else {
|
|
checkCleanupHelper();
|
|
}
|
|
|
|
console.error("Packaging contract validation passed.");
|
|
|
|
function checkPackageOutput(): void {
|
|
const outputDir = join(appDir, "dist-electron");
|
|
assert.ok(existsSync(outputDir), "dist-electron output must exist after packaging.");
|
|
assertNoForbiddenOutput(outputDir);
|
|
assertNoEscapingSymlinks(outputDir);
|
|
|
|
const appResourceDir = findPackagedAppResourceDir(outputDir);
|
|
assert.ok(appResourceDir, "packaged app resources directory was not found.");
|
|
assert.ok(existsSync(join(appResourceDir, "app.asar")), "packaged app.asar is missing.");
|
|
assertBundledOfficialPlugins(appResourceDir);
|
|
const appContents = join(appResourceDir, "app.asar.unpacked");
|
|
assert.ok(existsSync(appContents), "packaged app.asar.unpacked resources are missing.");
|
|
assert.ok(existsSync(join(appContents, "node_modules", "@familiaros", "claude", "dist", "index.js")), "packaged @familiaros/claude runtime is missing.");
|
|
assert.ok(existsSync(join(appContents, "node_modules", "@familiaros", "claude", "dist", "cli.js")), "packaged @familiaros/claude CLI runtime is missing.");
|
|
assert.ok(existsSync(join(appContents, "node_modules", "@familiaros", "claude", "package.json")), "packaged @familiaros/claude package metadata is missing.");
|
|
assert.ok(existsSync(join(appContents, "node_modules", "@familiaros", "client", "dist", "index.js")), "packaged @familiaros/client runtime is missing.");
|
|
assert.ok(existsSync(join(appContents, "node_modules", "@familiaros", "client", "package.json")), "packaged @familiaros/client package metadata is missing.");
|
|
assert.ok(existsSync(join(appContents, "node_modules", "@familiaros", "mcp", "dist", "index.js")), "packaged @familiaros/mcp runtime is missing.");
|
|
assert.ok(existsSync(join(appContents, "node_modules", "@familiaros", "mcp", "package.json")), "packaged @familiaros/mcp package metadata is missing.");
|
|
assert.ok(existsSync(join(appContents, "node_modules", "@familiaros", "cli", "dist", "index.js")), "packaged @familiaros/cli runtime is missing.");
|
|
assert.ok(existsSync(join(appContents, "node_modules", "@familiaros", "opencode", "dist", "plugin.js")), "packaged @familiaros/opencode plugin runtime is missing.");
|
|
assert.ok(existsSync(join(appContents, "node_modules", "@familiaros", "opencode", "package.json")), "packaged @familiaros/opencode package metadata is missing.");
|
|
assert.ok(existsSync(join(appContents, "node_modules", "@familiaros", "cursor", "dist", "index.js")), "packaged @familiaros/cursor runtime is missing.");
|
|
assert.ok(existsSync(join(appContents, "node_modules", "@familiaros", "cursor", "package.json")), "packaged @familiaros/cursor package metadata is missing.");
|
|
assert.ok(existsSync(join(appContents, "node_modules", "@familiaros", "agent-events", "dist", "index.js")), "packaged @familiaros/agent-events runtime is missing.");
|
|
assert.ok(existsSync(join(appContents, "node_modules", "@modelcontextprotocol", "sdk")), "packaged MCP SDK runtime dependency is missing.");
|
|
assert.ok(existsSync(join(appContents, "node_modules", "zod", "index.cjs")), "packaged zod runtime dependency is missing.");
|
|
assert.ok(existsSync(join(appContents, "node_modules", "yauzl", "index.js")), "packaged yauzl runtime dependency is missing.");
|
|
assert.ok(existsSync(join(appContents, "node_modules", "yauzl", "fd-slicer.js")), "packaged yauzl fd-slicer helper is missing.");
|
|
assert.ok(existsSync(join(appContents, "node_modules", "buffer-crc32", "index.js")), "packaged yauzl transitive dependency buffer-crc32 is missing.");
|
|
assert.ok(existsSync(join(appContents, "node_modules", "pend", "index.js")), "packaged yauzl transitive dependency pend is missing.");
|
|
assert.ok(existsSync(join(appContents, "node_modules", "sharp", "lib", "index.js")), "packaged sharp runtime is missing.");
|
|
assert.ok(existsSync(join(appContents, "node_modules", "@img", "sharp-win32-x64", "lib", "sharp-win32-x64.node")), "packaged Windows x64 sharp native binary is missing.");
|
|
assert.ok(existsSync(join(appContents, "node_modules", "@img", "sharp-linux-x64", "lib", "sharp-linux-x64.node")), "packaged Linux x64 sharp native binary is missing.");
|
|
assert.ok(existsSync(join(appContents, "node_modules", "@img", "sharp-darwin-x64", "lib", "sharp-darwin-x64.node")), "packaged macOS x64 sharp native binary is missing.");
|
|
assertRegularNonSymlink(join(appContents, "node_modules", "@familiaros", "mcp", "dist", "index.js"));
|
|
assertRegularNonSymlink(join(appContents, "node_modules", "@familiaros", "cli", "dist", "index.js"));
|
|
assertRegularNonSymlink(join(appContents, "node_modules", "@familiaros", "opencode", "dist", "plugin.js"));
|
|
assertRegularNonSymlink(join(appContents, "node_modules", "@familiaros", "claude", "dist", "cli.js"));
|
|
assertCommandSmoke(appContents);
|
|
}
|
|
|
|
function findPackagedAppResourceDir(outputDir: string): string | null {
|
|
const candidates: string[] = [];
|
|
collectDirectories(outputDir, candidates, 4);
|
|
|
|
for (const dir of candidates) {
|
|
if (existsSync(join(dir, "app.asar")) || existsSync(join(dir, "app", "dist", "main.js"))) {
|
|
return dir;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
function collectDirectories(dir: string, result: string[], depth: number): void {
|
|
if (depth < 0 || !existsSync(dir)) return;
|
|
result.push(dir);
|
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
if (entry.isDirectory()) collectDirectories(join(dir, entry.name), result, depth - 1);
|
|
}
|
|
}
|
|
|
|
function assertNoForbiddenOutput(outputDir: string): void {
|
|
const forbiddenSegments = new Set(["v1", "web", ".env", ".claude"]);
|
|
for (const path of walk(outputDir)) {
|
|
const rel = relative(outputDir, path);
|
|
const segments = rel.split(/[\\/]/g);
|
|
assert.ok(!segments.includes("docs") || !segments.includes("phases"), `package output must not include phase docs: ${rel}`);
|
|
for (const segment of segments) {
|
|
assert.ok(!forbiddenSegments.has(segment) && !segment.startsWith(".env"), `package output contains forbidden path segment: ${rel}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
function assertNoEscapingSymlinks(outputDir: string): void {
|
|
const outputReal = realpathSync(outputDir);
|
|
for (const path of walk(outputDir)) {
|
|
const stat = lstatSync(path);
|
|
if (!stat.isSymbolicLink()) continue;
|
|
const target = realpathSync(path);
|
|
assert.ok(isInside(outputReal, target), `package output symlink escapes package directory: ${relative(outputDir, path)} -> ${target}`);
|
|
}
|
|
}
|
|
|
|
function walk(dir: string): string[] {
|
|
const result: string[] = [];
|
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
const path = join(dir, entry.name);
|
|
result.push(path);
|
|
if (entry.isDirectory()) {
|
|
result.push(...walk(path));
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function isInside(parent: string, child: string): boolean {
|
|
const rel = relative(parent, child);
|
|
return rel === "" || (!rel.startsWith("..") && !isAbsolute(rel));
|
|
}
|
|
|
|
function checkCleanupHelper(): void {
|
|
const sentinel = join(appDir, "dist-electron", ".familiaros-clean-sentinel");
|
|
mkdirSync(dirname(sentinel), { recursive: true });
|
|
writeFileSync(sentinel, "stale", "utf8");
|
|
const result = spawnSync(process.execPath, [join(appDir, "scripts", "clean-package-output.cjs")], { cwd: appDir, encoding: "utf8" });
|
|
assert.equal(result.status, 0, `cleanup helper failed: ${result.stderr || result.stdout}`);
|
|
assert.ok(!existsSync(sentinel), "cleanup helper did not remove stale package output sentinel.");
|
|
}
|
|
|
|
function assertRegularNonSymlink(path: string): void {
|
|
assert.ok(!lstatSync(path).isSymbolicLink(), `packaged command file must not be a symlink: ${path}`);
|
|
assert.ok(lstatSync(path).isFile(), `packaged command file must be regular: ${path}`);
|
|
}
|
|
|
|
function assertNonEmptyFile(path: string, message: string): void {
|
|
assert.ok(existsSync(path), message);
|
|
const stat = lstatSync(path);
|
|
assert.ok(stat.isFile(), message);
|
|
assert.ok(stat.size > 0, message);
|
|
}
|
|
|
|
function assertBundledOfficialPlugins(resourceDir: string): void {
|
|
for (const id of ["familiaros.reminders"]) {
|
|
const dir = join(resourceDir, "plugins", "official", id);
|
|
const manifestPath = join(dir, "familiaros.plugin.json");
|
|
assertNonEmptyFile(manifestPath, `packaged bundled plugin manifest is missing: ${id}`);
|
|
const manifest = JSON.parse(readFileSync(manifestPath, "utf8")) as { entry?: string };
|
|
if (manifest.entry) assertNonEmptyFile(join(dir, manifest.entry), `packaged bundled plugin entry is missing: ${id}`);
|
|
}
|
|
}
|
|
|
|
function assertSafeBundledSvg(path: string, message: string): void {
|
|
assertNonEmptyFile(path, message);
|
|
const source = readFileSync(path, "utf8");
|
|
assert.doesNotMatch(source, /<script\b/i, `${message}: script tags are not allowed.`);
|
|
assert.doesNotMatch(source, /\son[a-z]+\s*=/i, `${message}: event attributes are not allowed.`);
|
|
assert.doesNotMatch(source, /(?:href|xlink:href)\s*=\s*["'](?:https?:|file:|javascript:)/i, `${message}: external or script hrefs are not allowed.`);
|
|
assert.doesNotMatch(source.replace(/xmlns="http:\/\/www\.w3\.org\/2000\/svg"/gi, ""), /https?:\/\//i, `${message}: remote references are not allowed.`);
|
|
}
|
|
|
|
function assertCommandSmoke(appContents: string): void {
|
|
const mcpEntry = join(appContents, "node_modules", "@familiaros", "mcp", "dist", "index.js");
|
|
const mcp = spawnSync(process.execPath, [mcpEntry, "--version"], { encoding: "utf8" });
|
|
assert.equal(mcp.status, 0, `packaged MCP command smoke failed: ${mcp.stderr || mcp.stdout}`);
|
|
|
|
const hookEntry = join(appContents, "node_modules", "@familiaros", "claude", "dist", "cli.js");
|
|
const hook = spawnSync(process.execPath, [hookEntry, "hook", "--familiaros-managed"], {
|
|
input: JSON.stringify({ hook_event_name: "Notification", message: "safe" }),
|
|
encoding: "utf8",
|
|
env: { ...process.env, FAMILIAROS_DISCOVERY_FILE: join(appContents, "missing-ipc.json") },
|
|
});
|
|
assert.equal(hook.status, 0, `packaged Claude hook command smoke failed: ${hook.stderr || hook.stdout}`);
|
|
assert.equal(hook.stdout, "");
|
|
|
|
const opencodePlugin = join(appContents, "node_modules", "@familiaros", "opencode", "dist", "plugin.js");
|
|
const plugin = spawnSync(process.execPath, ["--input-type=module", "--eval", `const mod = await import(${JSON.stringify(`file://${opencodePlugin}`)}); if (!mod.default?.server || !mod.default?.id) process.exit(2);`], { encoding: "utf8" });
|
|
assert.equal(plugin.status, 0, `packaged OpenCode plugin smoke failed: ${plugin.stderr || plugin.stdout}`);
|
|
}
|