@fluid-app/fluid-cli-portal 0.1.51 → 0.1.52
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.mjs +30 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["_currentDir","PORTAL_DIR","readPortalFile","PORTAL_SYNC_DIR","createClient","fluidOs.fluid_os_v0_create_fluid_osscreen","fluidOs.fluid_os_v0_update_fluid_osscreen","fluidOs.fluid_os_v0_delete_fluid_osscreen","fluidOs.fluid_os_v0_create_fluid_ostheme","fluidOs.fluid_os_v0_update_fluid_ostheme","fluidOs.fluid_os_v0_delete_fluid_ostheme","fluidOs.fluid_os_v0_create_fluid_osnavigation","fluidOs.fluid_os_v0_create_fluid_osnavigation_item","fluidOs.fluid_os_v0_update_fluid_osnavigation","fluidOs.fluid_os_v0_list_fluid_osnavigation_items","fluidOs.fluid_os_v0_delete_fluid_osnavigation_item","fluidOs.fluid_os_v0_update_fluid_osnavigation_item","fluidOs.fluid_os_v0_delete_fluid_osnavigation","fluidOs.fluid_os_v0_create_fluid_osprofile","fluidOs.fluid_os_v0_update_fluid_osprofile","fluidOs.fluid_os_v0_delete_fluid_osprofile","createClient","fluidOs.fluid_os_v0_list_fluid_osdefinitions","WIDGET_CONFIG_PATH","isRecord","toPosixPath","isRecord","isRecord","isRecord","fluidOs.fluid_os_v0_create_widget_package_version","fluidOs.fluid_os_v0_complete_widget_package_version_upload","isRecord","DEFAULT_OUT_DIR","fluidOs.fluid_os_v0_create_fluid_osversion","fluidOs.fluid_os_v0_update_fluid_osversion","fluidOs.fluid_os_v0_list_fluid_osversions"],"sources":["../src/types.ts","../src/utils/prompts.ts","../src/utils/file-system.ts","../src/utils/package-manager.ts","../src/lib/git.ts","../src/utils/scaffold-github.ts","../src/commands/create.ts","../src/commands/dev.ts","../src/commands/build.ts","../src/utils/push-validation.ts","../src/api/git-credential.ts","../src/lib/git-sync.ts","../src/commands/push.ts","../src/commands/clone.ts","../src/utils/widget-helpers.ts","../src/commands/widget-create.ts","../src/utils/widget-package-config.ts","../src/utils/widget-package-artifacts.ts","../../../portal/core/src/remote-dom-widget-package.ts","../src/utils/widget-package-validation.ts","../src/utils/widget-package-builder.ts","../src/utils/widget-package-upload.ts","../src/commands/widget-package-publish.ts","../src/commands/deploy.ts","../src/commands/doctor.ts","../src/commands/version.ts","../src/commands/widget-publish.ts","../src/index.ts"],"sourcesContent":["// ─────────────────────────────────────────────────────────────────────────────\n// Template types - derived from const object\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Available project templates\n */\nexport const TEMPLATES = {\n starter: \"starter\",\n} as const;\n\n/**\n * Union type of valid template names\n */\nexport type TemplateName = (typeof TEMPLATES)[keyof typeof TEMPLATES];\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Project configuration types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Selected page template info\n */\nexport interface SelectedPageTemplate {\n readonly id: string;\n readonly slug: string;\n readonly name: string;\n}\n\n/**\n * Configuration options collected during project scaffolding\n */\nexport interface ProjectConfig {\n /** Project name (used for directory and package.json name) */\n readonly name: string;\n /** Whether to install dependencies after scaffolding */\n readonly installDeps: boolean;\n /** Selected optional page templates to include */\n readonly selectedPages: readonly SelectedPageTemplate[];\n /** CLI profile name for .fluidrc (empty string if none selected) */\n readonly profileName: string;\n}\n\n/**\n * Options for the create command (from CLI arguments)\n */\nexport interface CreateOptions {\n /** Create only the scaffold and skip dependency installation */\n readonly skipInstall?: boolean;\n /** Directory to create the project in (defaults to cwd) */\n readonly outputDir?: string;\n /** Use local monorepo packages via file: links instead of npm versions */\n readonly local?: boolean;\n /** Explicitly use the GitHub starter instead of the bundled template. */\n readonly remoteTemplate?: boolean;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Command option types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Options for the dev command\n */\nexport interface DevOptions {\n readonly port?: number;\n readonly host?: boolean;\n}\n\n/**\n * Options for the build command\n */\nexport interface BuildOptions {\n readonly outDir?: string;\n}\n\n/**\n * Options for the widget create command\n */\nexport interface WidgetCreateOptions {\n /** Category for palette grouping */\n readonly category?: string;\n}\n\n/**\n * Options for the portal deploy command\n */\nexport interface PortalDeployOptions {\n readonly environment?: string;\n readonly outDir?: string;\n readonly dryRun?: boolean;\n}\n\n/**\n * Options for the top-level widget publish command\n */\nexport interface WidgetPublishOptions {\n readonly droplet: string;\n readonly outDir?: string;\n readonly dryRun?: boolean;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Template processing types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Template variables for Handlebars processing\n */\nexport interface TemplateVariables {\n readonly projectName: string;\n readonly sdkVersion: string;\n /** portal-core link used only for --local scaffolds */\n readonly localCoreVersion?: string;\n /** CLI package version (versioned independently from the SDK) */\n readonly cliVersion: string;\n /** Portal CLI plugin version (versioned independently from CLI core) */\n readonly portalCliVersion: string;\n /** Selected page templates for the project */\n readonly selectedPages: readonly SelectedPageTemplate[];\n /** Whether any optional pages were selected */\n readonly hasSelectedPages: boolean;\n /** CLI profile name for .fluidrc */\n readonly profileName: string;\n}\n","import { getActiveProfile, listProfileNames } from \"@fluid-app/fluid-cli\";\nimport prompts from \"prompts\";\nimport {\n type ProjectConfig,\n type CreateOptions,\n type SelectedPageTemplate,\n} from \"../types.js\";\n\n/**\n * Optional page template shape\n */\ninterface OptionalPageTemplate {\n readonly id: string;\n readonly slug: string;\n readonly name: string;\n readonly description: string;\n}\n\n/**\n * Available optional page templates that can be selected during project creation.\n * Core pages (Messaging, Contacts, CRM) are always included automatically.\n */\nconst OPTIONAL_PAGE_TEMPLATES: readonly OptionalPageTemplate[] = [\n // Currently no optional pages - all pages are core\n // Future optional pages can be added here:\n // { id: 'orders', slug: 'orders', name: 'Orders', description: 'Order management page' },\n // { id: 'products', slug: 'products', name: 'Products', description: 'Product catalog page' },\n];\n\n/**\n * Prompts the user for project configuration\n * Pre-fills values from CLI options when provided\n */\nexport async function promptProjectConfig(\n projectName: string,\n options: CreateOptions,\n): Promise<ProjectConfig | null> {\n // Build questions based on what options are missing\n const questions: prompts.PromptObject[] = [];\n\n // Page template selection (only if there are optional templates)\n if (OPTIONAL_PAGE_TEMPLATES.length > 0) {\n questions.push({\n type: \"multiselect\",\n name: \"selectedPages\",\n message: \"Select additional page templates to include\",\n instructions:\n \"\\n Space to select, Enter to confirm. Core pages (Messaging, Contacts, CRM) are always included.\",\n choices: OPTIONAL_PAGE_TEMPLATES.map((page) => ({\n title: page.name,\n value: { id: page.id, slug: page.slug, name: page.name },\n description: page.description,\n })),\n });\n }\n\n // CLI profile for .fluidrc\n const existingProfiles = listProfileNames();\n if (existingProfiles.length > 0) {\n const active = getActiveProfile();\n questions.push({\n type: \"select\",\n name: \"profileName\",\n message: \"CLI profile for this project (.fluidrc)\",\n choices: existingProfiles.map((name) => ({\n title: name === active?.name ? `${name} (active)` : name,\n value: name,\n })),\n });\n }\n\n // Install dependencies\n if (!options.skipInstall) {\n questions.push({\n type: \"confirm\",\n name: \"installDeps\",\n message: \"Install dependencies?\",\n initial: true,\n });\n }\n\n // Non-interactive mode: if stdin is not a TTY and there are remaining\n // prompts, return safe defaults instead of hanging on interactive input.\n if (!process.stdin.isTTY && questions.length > 0) {\n return {\n name: projectName,\n installDeps: false,\n selectedPages: [],\n profileName: getActiveProfile()?.name ?? \"\",\n } satisfies ProjectConfig;\n }\n\n // Fast-path: all options provided via CLI flags, no prompts needed\n if (questions.length === 0) {\n return {\n name: projectName,\n installDeps: options.skipInstall ? false : true,\n selectedPages: [],\n profileName: getActiveProfile()?.name ?? \"\",\n } satisfies ProjectConfig;\n }\n\n // Handle Ctrl+C gracefully\n let cancelled = false;\n const response = await prompts(questions, {\n onCancel: () => {\n cancelled = true;\n return false;\n },\n });\n\n if (cancelled) {\n return null;\n }\n\n // Parse selected pages\n const selectedPages: readonly SelectedPageTemplate[] =\n response.selectedPages ?? [];\n\n return {\n name: projectName,\n installDeps: options.skipInstall ? false : (response.installDeps ?? true),\n selectedPages,\n profileName:\n (response.profileName as string | undefined) ??\n getActiveProfile()?.name ??\n \"\",\n } satisfies ProjectConfig;\n}\n","import type { CliError } from \"@fluid-app/fluid-cli\";\nimport { readdir, readFile, stat, mkdir, writeFile } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { join, dirname, relative } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport Handlebars from \"handlebars\";\nimport type { TemplateVariables } from \"../types.js\";\nimport { type Result, success, failure } from \"@fluid-app/fluid-cli\";\n\nconst _currentFile = fileURLToPath(import.meta.url);\nconst _currentDir = dirname(_currentFile);\n\ndeclare const __FLUID_PORTAL_SDK_VERSION__: string | undefined;\ndeclare const __FLUID_CLI_CORE_VERSION__: string | undefined;\ndeclare const __FLUID_CLI_PORTAL_VERSION__: string | undefined;\n\nconst EMBEDDED_PORTAL_SDK_VERSION =\n typeof __FLUID_PORTAL_SDK_VERSION__ === \"string\"\n ? __FLUID_PORTAL_SDK_VERSION__\n : undefined;\nconst EMBEDDED_CLI_CORE_VERSION =\n typeof __FLUID_CLI_CORE_VERSION__ === \"string\"\n ? __FLUID_CLI_CORE_VERSION__\n : undefined;\nconst EMBEDDED_CLI_PORTAL_VERSION =\n typeof __FLUID_CLI_PORTAL_VERSION__ === \"string\"\n ? __FLUID_CLI_PORTAL_VERSION__\n : undefined;\n\n/**\n * Find the package root by walking up from the current directory to the nearest package.json.\n * Works whether running from dist/ (bundled) or src/utils/ (tsx dev mode).\n */\nfunction findPackageRoot(): string {\n let dir = _currentDir;\n while (!existsSync(join(dir, \"package.json\"))) {\n const parent = dirname(dir);\n if (parent === dir) throw new Error(\"Could not find package root\");\n dir = parent;\n }\n return dir;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// File system operation error types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Error types for file system operations\n */\nexport const FILE_SYSTEM_ERRORS = {\n directoryNotFound: \"DIRECTORY_NOT_FOUND\",\n fileNotFound: \"FILE_NOT_FOUND\",\n readError: \"READ_ERROR\",\n writeError: \"WRITE_ERROR\",\n templateError: \"TEMPLATE_ERROR\",\n} as const;\n\n/**\n * Union type for file system error codes\n */\nexport type FileSystemErrorCode =\n (typeof FILE_SYSTEM_ERRORS)[keyof typeof FILE_SYSTEM_ERRORS];\n\n/**\n * Structured file system error with code for pattern matching\n */\nexport interface FileSystemError extends CliError {\n readonly code: FileSystemErrorCode;\n readonly message: string;\n readonly path?: string;\n readonly cause?: Error;\n}\n\n/**\n * Create a file system error\n */\nfunction createFsError(\n code: FileSystemErrorCode,\n message: string,\n path?: string,\n cause?: Error,\n): FileSystemError {\n return { code, message, path, cause };\n}\n\n/**\n * Paths for the base + overlay template system\n */\nexport interface TemplatePaths {\n /** Path to shared frontend files used by all templates */\n readonly base: string;\n /** Path to template-specific overlay files */\n readonly overlay: string;\n}\n\n/**\n * Gets paths for the base + overlay template system.\n *\n * The create command copies `base` first, then the `overlay` on top.\n * Any overlay file with the same relative path overwrites the base version.\n */\nexport function getTemplatePaths(templateName: string): TemplatePaths {\n const packageRoot = findPackageRoot();\n const templatesDir = join(packageRoot, \"templates\");\n return {\n base: join(templatesDir, \"base\"),\n overlay: join(templatesDir, templateName),\n };\n}\n\n/**\n * Gets all files in a directory recursively\n */\nasync function getFiles(dir: string, baseDir: string = dir): Promise<string[]> {\n const entries = await readdir(dir, { withFileTypes: true });\n const files: string[] = [];\n\n for (const entry of entries) {\n const fullPath = join(dir, entry.name);\n if (entry.isDirectory()) {\n files.push(...(await getFiles(fullPath, baseDir)));\n } else {\n // Return relative path from baseDir\n files.push(fullPath.slice(baseDir.length + 1));\n }\n }\n\n return files;\n}\n\n/**\n * Processes a template file with Handlebars\n * Files ending in .template have the extension removed and content processed\n * Other files are copied as-is\n */\nfunction processTemplate(\n content: string,\n variables: TemplateVariables,\n isTemplate: boolean,\n filePath?: string,\n): string {\n if (!isTemplate) {\n return content;\n }\n\n try {\n const template = Handlebars.compile(content);\n return template(variables);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n throw new Error(\n `Template processing failed${filePath ? ` for ${filePath}` : \"\"}: ${message}`,\n );\n }\n}\n\n/**\n * Gets the output filename for a template file\n * Removes .template extension if present\n */\ninterface TemplateSkillFile {\n readonly relativePath: string;\n readonly content: string;\n}\n\nasync function getSharedTemplateSkillFiles(): Promise<TemplateSkillFile[]> {\n const packageRoot = findFluidCliPackageRoot();\n const skillsRoot = join(packageRoot, \"template-skills\");\n const files = await getFiles(skillsRoot);\n\n return Promise.all(\n files.map(async (file) => ({\n relativePath: join(\"skills\", file),\n content: await readFile(join(skillsRoot, file), \"utf-8\"),\n })),\n );\n}\n\nfunction findFluidCliPackageRoot(): string {\n const workspacePackageRoot = join(findPackageRoot(), \"..\", \"core\");\n if (existsSync(join(workspacePackageRoot, \"template-skills\"))) {\n return workspacePackageRoot;\n }\n\n let dir = dirname(fileURLToPath(import.meta.resolve(\"@fluid-app/fluid-cli\")));\n while (!existsSync(join(dir, \"package.json\"))) {\n const parent = dirname(dir);\n if (parent === dir)\n throw new Error(\"Could not find Fluid CLI package root\");\n dir = parent;\n }\n return dir;\n}\n\nasync function writeOutputFiles(\n targetPath: string,\n outputFiles: readonly string[],\n content: string,\n): Promise<void> {\n for (const outputFile of outputFiles) {\n const destPath = join(targetPath, outputFile);\n const destDir = dirname(destPath);\n await mkdir(destDir, { recursive: true });\n await writeFile(destPath, content, \"utf-8\");\n }\n}\n\nfunction getOutputFilenames(filename: string): string[] {\n const outputFilename = getOutputFilename(filename);\n const normalized = outputFilename.replace(/\\\\/g, \"/\");\n\n if (normalized === \"AGENTS.md\") return [\"AGENTS.md\", \"CLAUDE.md\"];\n\n if (normalized.startsWith(\"skills/\")) {\n return [join(\".agents\", outputFilename), join(\".claude\", outputFilename)];\n }\n\n return [outputFilename];\n}\n\nfunction getOutputFilename(filename: string): string {\n if (filename.endsWith(\".template\")) {\n return filename.slice(0, -\".template\".length);\n }\n return filename;\n}\n\n/**\n * Copies a template directory to the target directory\n * Processes .template files with Handlebars\n */\nexport async function copyTemplate(\n templatePath: string,\n targetPath: string,\n variables: TemplateVariables,\n): Promise<void> {\n const files = await getFiles(templatePath);\n\n for (const file of files) {\n const sourcePath = join(templatePath, file);\n const isTemplate = file.endsWith(\".template\");\n const outputFiles = getOutputFilenames(file);\n\n // Read source file\n const content = await readFile(sourcePath, \"utf-8\");\n\n // Process and write\n const processed = processTemplate(\n content,\n variables,\n isTemplate,\n sourcePath,\n );\n\n for (const outputFile of outputFiles) {\n const destPath = join(targetPath, outputFile);\n const destDir = dirname(destPath);\n await mkdir(destDir, { recursive: true });\n await writeFile(destPath, processed, \"utf-8\");\n }\n }\n\n for (const skillFile of await getSharedTemplateSkillFiles()) {\n await writeOutputFiles(\n targetPath,\n getOutputFilenames(skillFile.relativePath),\n skillFile.content,\n );\n }\n}\n\n/**\n * Checks if a directory exists\n */\nexport async function directoryExists(path: string): Promise<boolean> {\n try {\n const stats = await stat(path);\n return stats.isDirectory();\n } catch {\n return false;\n }\n}\n\n/**\n * Checks if a file exists\n */\nexport async function fileExists(path: string): Promise<boolean> {\n try {\n const stats = await stat(path);\n return stats.isFile();\n } catch {\n return false;\n }\n}\n\n/**\n * Checks if a path exists (file or directory)\n */\nexport async function pathExists(path: string): Promise<boolean> {\n try {\n await stat(path);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Creates a directory\n */\nexport async function createDirectory(path: string): Promise<void> {\n await mkdir(path, { recursive: true });\n}\n\n/**\n * Reads the exact SDK version embedded by the build, or from the workspace\n * package while running source directly.\n */\nexport async function getSdkVersion(): Promise<string> {\n return getTemplatePackageVersion(\n [\"portal\", \"sdk\"],\n EMBEDDED_PORTAL_SDK_VERSION,\n );\n}\n\n/**\n * Builds a pnpm link: specifier for local scaffolds.\n *\n * link: keeps the dependency pointed at the workspace package source instead of\n * packing only package.json \"files\" entries like file: does. That lets Vite's\n * development export resolve the SDK source and its workspace dependencies.\n */\nexport function getLocalPackageLinkVersion(\n targetPath: string,\n packagePath: string,\n): string {\n const linkPath = (relative(targetPath, packagePath) || \".\").replace(\n /\\\\/g,\n \"/\",\n );\n return `link:${linkPath}`;\n}\n\n/**\n * Reads the exact CLI core version embedded by the build, or from the\n * workspace package while running source directly.\n *\n * This is separate from getSdkVersion because the CLI and portal SDK\n * are versioned independently.\n */\nexport async function getCliVersion(): Promise<string> {\n return getTemplatePackageVersion([\"cli\", \"core\"], EMBEDDED_CLI_CORE_VERSION);\n}\n\n/** Reads the independently versioned portal CLI plugin version. */\nexport async function getPortalCliVersion(): Promise<string> {\n return getTemplatePackageVersion(\n [\"cli\", \"portal\"],\n EMBEDDED_CLI_PORTAL_VERSION,\n );\n}\n\nasync function getTemplatePackageVersion(\n packagePathSegments: readonly string[],\n embeddedVersion: string | undefined,\n): Promise<string> {\n if (embeddedVersion) return embeddedVersion;\n\n try {\n const packageRoot = findPackageRoot();\n const packagesRoot = join(packageRoot, \"..\", \"..\");\n const packageJsonPath = join(\n packagesRoot,\n ...packagePathSegments,\n \"package.json\",\n );\n\n const content = await readFile(packageJsonPath, \"utf-8\");\n const pkg = JSON.parse(content) as { version?: unknown };\n if (typeof pkg.version !== \"string\") {\n throw new Error(`Missing version in ${packageJsonPath}`);\n }\n return pkg.version;\n } catch (error) {\n throw new Error(\n `Could not resolve the exact template package version for ${packagePathSegments.join(\"/\")}`,\n { cause: error },\n );\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Result-based variants for type-safe error handling\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Read a file's content with Result-based error handling\n */\nexport async function readFileSafe(\n path: string,\n): Promise<Result<string, FileSystemError>> {\n try {\n const content = await readFile(path, \"utf-8\");\n return success(content);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n return failure(\n createFsError(\n FILE_SYSTEM_ERRORS.readError,\n `Failed to read file: ${path}`,\n path,\n error,\n ),\n );\n }\n}\n\n/**\n * Write content to a file with Result-based error handling\n */\nexport async function writeFileSafe(\n path: string,\n content: string,\n): Promise<Result<void, FileSystemError>> {\n try {\n await writeFile(path, content, \"utf-8\");\n return success(undefined);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n return failure(\n createFsError(\n FILE_SYSTEM_ERRORS.writeError,\n `Failed to write file: ${path}`,\n path,\n error,\n ),\n );\n }\n}\n\n/**\n * Create a directory with Result-based error handling\n */\nexport async function createDirectorySafe(\n path: string,\n): Promise<Result<void, FileSystemError>> {\n try {\n await mkdir(path, { recursive: true });\n return success(undefined);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n return failure(\n createFsError(\n FILE_SYSTEM_ERRORS.writeError,\n `Failed to create directory: ${path}`,\n path,\n error,\n ),\n );\n }\n}\n\n/**\n * Copy a template directory with Result-based error handling\n */\nexport async function copyTemplateSafe(\n templatePath: string,\n targetPath: string,\n variables: Readonly<TemplateVariables>,\n): Promise<Result<void, FileSystemError>> {\n try {\n const files = await getFiles(templatePath);\n\n for (const file of files) {\n const sourcePath = join(templatePath, file);\n const isTemplateFile = file.endsWith(\".template\");\n const outputFiles = getOutputFilenames(file);\n\n // Read source file\n const content = await readFile(sourcePath, \"utf-8\");\n\n // Process and write\n const processed = processTemplate(\n content,\n variables,\n isTemplateFile,\n sourcePath,\n );\n\n for (const outputFile of outputFiles) {\n const destPath = join(targetPath, outputFile);\n const destDir = dirname(destPath);\n await mkdir(destDir, { recursive: true });\n await writeFile(destPath, processed, \"utf-8\");\n }\n }\n\n for (const skillFile of await getSharedTemplateSkillFiles()) {\n await writeOutputFiles(\n targetPath,\n getOutputFilenames(skillFile.relativePath),\n skillFile.content,\n );\n }\n\n return success(undefined);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n return failure(\n createFsError(\n FILE_SYSTEM_ERRORS.templateError,\n `Failed to copy template from ${templatePath} to ${targetPath}`,\n templatePath,\n error,\n ),\n );\n }\n}\n\n/**\n * Get SDK version with Result-based error handling\n * Unlike getSdkVersion, this returns an error instead of a fallback\n */\nexport async function getSdkVersionSafe(): Promise<\n Result<string, FileSystemError>\n> {\n try {\n // CLI lives at packages/cli/portal/, SDK at packages/portal/sdk/\n const packageRoot = findPackageRoot();\n const packagesRoot = join(packageRoot, \"..\", \"..\");\n const sdkPackagePath = join(packagesRoot, \"portal\", \"sdk\", \"package.json\");\n\n const content = await readFile(sdkPackagePath, \"utf-8\");\n const pkg = JSON.parse(content) as { version?: string };\n const version = pkg.version;\n\n if (version === undefined) {\n return failure(\n createFsError(\n FILE_SYSTEM_ERRORS.readError,\n \"SDK package.json does not contain a version field\",\n sdkPackagePath,\n ),\n );\n }\n\n return success(`^${version}`);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n return failure(\n createFsError(\n FILE_SYSTEM_ERRORS.fileNotFound,\n \"Could not find SDK package.json\",\n undefined,\n error,\n ),\n );\n }\n}\n","import { execa } from \"execa\";\n\n/**\n * Returns the install command for pnpm\n */\nexport function getInstallCommand(): string {\n return \"pnpm install\";\n}\n\n/**\n * Returns the run command for pnpm\n */\nexport function getRunCommand(script: string): string {\n return `pnpm run ${script}`;\n}\n\n/**\n * Runs a pnpm command in the specified directory\n */\nexport async function runPackageManager(\n args: string[],\n cwd: string,\n): Promise<void> {\n await execa(\"pnpm\", args, {\n cwd,\n stdio: \"inherit\",\n });\n}\n\n/**\n * Installs dependencies using pnpm\n */\nexport async function installDependencies(cwd: string): Promise<void> {\n await runPackageManager([\"install\"], cwd);\n}\n","/**\n * Git wrapper for the portal CLI — shells out to the system `git` via\n * execa. We deliberately do NOT bundle git or use a pure-JS impl; users\n * are expected to have it installed (the same stance as `fluid mist`).\n *\n * Security: a token-bearing `remote_url` (minted by the portal\n * `git_credential` endpoint) must never be logged, persisted, *or*\n * exposed in the process table. The token is stripped from the URL and\n * handed to git through a GIT_ASKPASS script via env vars\n * (FLUID_PORTAL_GIT_USER / FLUID_PORTAL_GIT_PASS) — so it never appears\n * in argv (visible to `ps aux` / `/proc/<pid>/cmdline`) and is never\n * written to `.git/config`.\n *\n * This mirrors `packages/cli/mist/src/lib/git.ts`. Kept portal-local for\n * now; if the widget/mist tracks extract a shared git lib we should\n * consume it instead (see overnight/track2-portal.md).\n */\n\nimport { chmodSync, existsSync, mkdirSync, writeFileSync } from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { execa, type ExecaError } from \"execa\";\n\nexport class GitError extends Error {\n readonly exitCode: number;\n\n constructor(message: string, exitCode = 1) {\n super(message);\n this.name = \"GitError\";\n this.exitCode = exitCode;\n }\n}\n\n/**\n * execa throws ENOENT when the git binary itself is missing — the single\n * most common failure on non-developer machines. Translate it into an\n * actionable message. Returns null for every other failure so call sites\n * keep their specific messages.\n */\nexport function gitMissingError(err: unknown): GitError | null {\n const code = (err as NodeJS.ErrnoException | undefined)?.code;\n if (code !== \"ENOENT\") return null;\n return new GitError(\n \"Git isn't installed (or isn't on your PATH), so this command can't run.\\n\" +\n \" Install it, then try again:\\n\" +\n \" macOS: xcode-select --install (or `brew install git`)\\n\" +\n \" Windows: https://git-scm.com/download/win\\n\" +\n \" Linux: use your package manager, e.g. `apt install git`\",\n 127,\n );\n}\n\n/**\n * Is `git` available on this machine? Cheap, never throws — used to\n * decide whether to take the GitHub-template path or fall back to the\n * bundled scaffold.\n */\nexport async function isGitAvailable(): Promise<boolean> {\n try {\n await execa(\"git\", [\"--version\"]);\n return true;\n } catch {\n return false;\n }\n}\n\n/** Confirm git is installed, throwing the friendly message otherwise. */\nexport async function ensureGitAvailable(): Promise<void> {\n if (!(await isGitAvailable())) {\n throw new GitError(\n \"Git isn't installed (or isn't on your PATH), so this command can't run.\\n\" +\n \" Install it from https://git-scm.com/downloads (or `brew install git`), then try again.\",\n );\n }\n}\n\n/**\n * Split a token-bearing remote URL into the userless URL + creds. Keeps\n * the token off git's argv: we pass `cleanUrl` to git and feed\n * `username` / `password` back through GIT_ASKPASS env vars.\n *\n * If the URL has no embedded credentials, returns empty strings — fine\n * for public repos where git won't invoke askpass at all.\n */\nfunction splitRemoteCredentials(remoteUrl: string): {\n cleanUrl: string;\n username: string;\n password: string;\n} {\n const u = new URL(remoteUrl);\n const username = decodeURIComponent(u.username);\n const password = decodeURIComponent(u.password);\n u.username = \"\";\n u.password = \"\";\n return { cleanUrl: u.toString(), username, password };\n}\n\n/**\n * Write a stub GIT_ASKPASS shell script to a per-process tmpdir and\n * return its path. The script contains no secret — it echoes back\n * whichever env var (FLUID_PORTAL_GIT_USER / FLUID_PORTAL_GIT_PASS)\n * matches git's prompt. The credentials only live in the spawned git\n * process's env for the duration of the call.\n *\n * One POSIX script for all platforms: Git for Windows executes\n * GIT_ASKPASS through its bundled sh, so a plain sh script works\n * everywhere. Idempotent: subsequent calls reuse the same path.\n */\nlet askpassPath: string | null = null;\nfunction ensureAskpassScript(): string {\n if (askpassPath && existsSync(askpassPath)) return askpassPath;\n const dir = path.join(os.tmpdir(), `fluid-portal-askpass-${process.pid}`);\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true, mode: 0o700 });\n const p = path.join(dir, \"askpass.sh\");\n writeFileSync(\n p,\n `#!/bin/sh\\ncase \"$1\" in\\n Username*) printf '%s' \"$FLUID_PORTAL_GIT_USER\" ;;\\n *) printf '%s' \"$FLUID_PORTAL_GIT_PASS\" ;;\\nesac\\n`,\n { mode: 0o700 },\n );\n chmodSync(p, 0o700);\n askpassPath = p;\n return askpassPath;\n}\n\n/**\n * Build the spawn env + cleaned URL for a credentialed git invocation.\n * - `cleanUrl`: URL with userinfo stripped — safe to pass as argv.\n * - `env`: parent env plus GIT_ASKPASS + FLUID_PORTAL_GIT_USER/PASS.\n * GIT_TERMINAL_PROMPT=0 prevents the TTY-prompt fallback if the\n * credentials are empty (auth should fail fast, not hang).\n */\nfunction gitAuthSpawn(remoteUrl: string): {\n cleanUrl: string;\n env: NodeJS.ProcessEnv;\n} {\n const { cleanUrl, username, password } = splitRemoteCredentials(remoteUrl);\n return {\n cleanUrl,\n env: {\n ...process.env,\n GIT_ASKPASS: ensureAskpassScript(),\n GIT_TERMINAL_PROMPT: \"0\",\n FLUID_PORTAL_GIT_USER: username,\n FLUID_PORTAL_GIT_PASS: password,\n },\n };\n}\n\n/** Map an execa failure to a GitError, preferring the friendly\n * git-missing message when the binary itself is absent. */\nfunction toGitError(err: unknown, verb: string): GitError {\n const missing = gitMissingError(err);\n if (missing) return missing;\n const e = err as ExecaError;\n return new GitError(\n `git ${verb} failed${e.exitCode != null ? ` (exit ${e.exitCode})` : \"\"}`,\n e.exitCode ?? 1,\n );\n}\n\n/**\n * `git clone <cleanUrl> <targetDir>`. When `remoteUrl` carries a token,\n * gitAuthSpawn strips it from the URL and re-injects it via GIT_ASKPASS\n * so it never appears in argv. `credential.helper=` stops git from\n * caching what askpass returned to any system credential store.\n *\n * `depth` shallow-clones (used by the create flow, which discards `.git`\n * afterwards anyway).\n */\nexport async function gitClone(\n remoteUrl: string,\n targetDir: string,\n opts: { depth?: number } = {},\n): Promise<void> {\n const { cleanUrl, env } = gitAuthSpawn(remoteUrl);\n const depthArgs = opts.depth ? [\"--depth\", String(opts.depth)] : [];\n try {\n await execa(\n \"git\",\n [\n \"-c\",\n \"credential.helper=\",\n \"clone\",\n \"--quiet\",\n ...depthArgs,\n cleanUrl,\n targetDir,\n ],\n { env, stdio: [\"ignore\", \"inherit\", \"inherit\"] },\n );\n } catch (err) {\n throw toGitError(err, \"clone\");\n }\n}\n\n/** Initialize a fresh repo (used after scaffolding so the project is\n * git-ready). Best-effort: never throws — a scaffold without a repo is\n * still usable, and a missing git binary is handled by the caller. */\nexport async function gitInit(cwd: string): Promise<boolean> {\n try {\n await execa(\"git\", [\"init\", \"--quiet\"], { cwd });\n return true;\n } catch {\n return false;\n }\n}\n\n/** Is `cwd` inside a git work tree? Cheap, never throws. */\nexport async function isGitRepo(cwd: string): Promise<boolean> {\n try {\n const { stdout } = await execa(\n \"git\",\n [\"rev-parse\", \"--is-inside-work-tree\"],\n { cwd },\n );\n return stdout.trim() === \"true\";\n } catch {\n return false;\n }\n}\n\n/** Read `git remote get-url origin` (or null if no origin / not a repo). */\nexport async function getOriginUrl(cwd: string): Promise<string | null> {\n try {\n const { stdout } = await execa(\"git\", [\"remote\", \"get-url\", \"origin\"], {\n cwd,\n });\n return stdout.trim();\n } catch {\n return null;\n }\n}\n\n/**\n * Are there uncommitted changes (working tree or index)? `git status\n * --porcelain` is empty when clean. Returns false (never throws) when\n * `cwd` isn't a repo.\n */\nexport async function hasUncommittedChanges(cwd: string): Promise<boolean> {\n try {\n const { stdout } = await execa(\"git\", [\"status\", \"--porcelain\"], { cwd });\n return stdout.trim().length > 0;\n } catch {\n return false;\n }\n}\n\n/**\n * Fetch `branch` from a token-bearing remote URL, writing FETCH_HEAD so\n * `gitAheadBehindVsFetchHead` is meaningful. Token comes through\n * GIT_ASKPASS, never argv or `.git/config`.\n */\nexport async function gitFetch(\n cwd: string,\n remoteUrl: string,\n branch = \"main\",\n): Promise<void> {\n const { cleanUrl, env } = gitAuthSpawn(remoteUrl);\n try {\n await execa(\n \"git\",\n [\"-c\", \"credential.helper=\", \"fetch\", \"--quiet\", cleanUrl, branch],\n { cwd, env },\n );\n } catch (err) {\n throw toGitError(err, \"fetch\");\n }\n}\n\n/**\n * Count commits in local HEAD vs FETCH_HEAD (after `gitFetch`). Returns\n * `{ahead, behind}`. `0/0` means \"in sync\" (or no FETCH_HEAD yet).\n */\nexport async function gitAheadBehindVsFetchHead(\n cwd: string,\n): Promise<{ ahead: number; behind: number }> {\n try {\n const { stdout } = await execa(\n \"git\",\n [\"rev-list\", \"--left-right\", \"--count\", \"HEAD...FETCH_HEAD\"],\n { cwd },\n );\n const [a, b] = stdout.trim().split(/\\s+/);\n const ahead = Number.parseInt(a ?? \"0\", 10);\n const behind = Number.parseInt(b ?? \"0\", 10);\n return {\n ahead: Number.isFinite(ahead) ? ahead : 0,\n behind: Number.isFinite(behind) ? behind : 0,\n };\n } catch {\n return { ahead: 0, behind: 0 };\n }\n}\n\n/** Does `cwd` have any commits yet? (false right after `git init`.) */\nexport async function hasCommits(cwd: string): Promise<boolean> {\n try {\n await execa(\"git\", [\"rev-parse\", \"--verify\", \"HEAD\"], { cwd });\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Does `branch` exist on the remote? Throws on transport/auth failure so\n * a network blip is never mistaken for an empty remote (which would skip\n * drift detection). Token via GIT_ASKPASS, never argv or .git/config.\n */\nexport async function gitRemoteBranchExists(\n cwd: string,\n remoteUrl: string,\n branch = \"main\",\n): Promise<boolean> {\n const { cleanUrl, env } = gitAuthSpawn(remoteUrl);\n try {\n const { stdout } = await execa(\n \"git\",\n [\n \"-c\",\n \"credential.helper=\",\n \"ls-remote\",\n \"--heads\",\n cleanUrl,\n `refs/heads/${branch}`,\n ],\n { cwd, env },\n );\n return stdout.trim().length > 0;\n } catch (err) {\n throw toGitError(err, \"ls-remote\");\n }\n}\n\n/**\n * Adopt `FETCH_HEAD` as the current (unborn) branch's base: point the\n * branch ref at it and load its tree into the index, leaving the working\n * tree untouched. The next auto-commit (run with `ignoreRemovals`) then\n * records the local project tree on top of the remote history — local\n * additions/edits win, and remote-only files survive because a\n * content-only local dir (e.g. populated by `fluid portal pull`) is no\n * statement about what the full repo tracks — so the push fast-forwards\n * instead of being rejected for unrelated history. Only call when\n * `hasCommits` is false; with local commits it would silently rewrite\n * the branch ref.\n * Mirrors the widget CLI's first-push adoption (the backend provisions\n * repos with an auto-init/template commit, so a fresh root commit is\n * always unrelated history).\n *\n * Returns the number of remote commits adopted.\n */\nexport async function gitAdoptFetchHead(cwd: string): Promise<number> {\n try {\n await execa(\"git\", [\"reset\", \"--mixed\", \"FETCH_HEAD\"], { cwd });\n const { stdout } = await execa(\n \"git\",\n [\"rev-list\", \"--count\", \"FETCH_HEAD\"],\n { cwd },\n );\n const count = Number.parseInt(stdout.trim(), 10);\n return Number.isFinite(count) ? count : 0;\n } catch (err) {\n throw toGitError(err, \"adopt remote history\");\n }\n}\n\n/**\n * Normalize a git remote URL to `host/owner/repo` for identity\n * comparison — strips credentials, protocol, `.git`, and trailing\n * slashes so a token-bearing minted URL compares equal to the stored\n * origin. Returns null when the URL doesn't parse.\n */\nexport function normalizedRepoIdentity(remoteUrl: string): string | null {\n // scp-style SSH remotes (`git@github.com:owner/repo.git`) aren't URLs —\n // normalize them by hand so an SSH origin still verifies against the\n // https minted URL.\n const scp = remoteUrl.match(/^(?:[^@/\\s]+@)?([^:/\\s]+):([^/].*)$/);\n if (scp?.[1] && scp[2] && !remoteUrl.includes(\"://\")) {\n const path = scp[2].replace(/\\.git$/, \"\").replace(/\\/+$/, \"\");\n return `${scp[1]}/${path}`.toLowerCase();\n }\n // Plain filesystem remotes (local bare repos — tests, offline mirrors):\n // identity is the normalized path itself.\n if (remoteUrl.startsWith(\"/\") || remoteUrl.startsWith(\".\")) {\n return remoteUrl\n .replace(/\\.git$/, \"\")\n .replace(/\\/+$/, \"\")\n .toLowerCase();\n }\n try {\n const u = new URL(remoteUrl);\n const path = u.pathname\n .replace(/\\.git$/, \"\")\n .replace(/\\/+$/, \"\")\n .replace(/^\\/+/, \"\");\n return `${u.host}/${path}`.toLowerCase();\n } catch {\n return null;\n }\n}\n\n/**\n * Do HEAD and FETCH_HEAD share any common ancestor? False for unrelated\n * histories — the guard that keeps a whole-tree auto-commit of the WRONG\n * repository from being pushed to a portal's remote. Never throws.\n */\nexport async function hasCommonAncestorWithFetchHead(\n cwd: string,\n): Promise<boolean> {\n try {\n await execa(\"git\", [\"merge-base\", \"HEAD\", \"FETCH_HEAD\"], { cwd });\n return true;\n } catch {\n return false;\n }\n}\n\n/** Fast-forward HEAD to FETCH_HEAD. Fails (non-zero exit) if not\n * fast-forwardable — callers check the ahead-count first. */\nexport async function gitMergeFastForward(cwd: string): Promise<void> {\n try {\n await execa(\"git\", [\"merge\", \"--ff-only\", \"FETCH_HEAD\"], { cwd });\n } catch (err) {\n throw toGitError(err, \"fast-forward merge\");\n }\n}\n\nexport interface GitAutoCommitOptions {\n /**\n * Stage with `git add --ignore-removal .` (new + modified files only)\n * instead of `git add -A`, so files present in the index but absent\n * from the working tree are NOT recorded as deletions. Used by the\n * first-push adoption path in git-sync: a directory populated by\n * `fluid portal pull` (content-only sync) has no idea what the full\n * repo tracks, so local additions/edits win but remote-only files are\n * preserved — deletions only ever ship from full checkouts.\n */\n readonly ignoreRemovals?: boolean;\n}\n\n/**\n * `git add -A && git commit -m <message>` (or, with\n * `{ ignoreRemovals: true }`, `git add --ignore-removal .` — see\n * GitAutoCommitOptions).\n *\n * `git add -A` intentionally stages the whole work tree: a portal's\n * provisioned repo tracks the entire project (it's seeded from the\n * starter template), and the \"publish my changes\" UX expects every edit\n * to ship — the same stance as `fluid mist push`. The exclusion boundary\n * is the project's `.gitignore` (the starter ignores `.env`,\n * `node_modules/`, `dist/`, `.portal-sync/`, logs, `*.tsbuildinfo`), so\n * generated output and local secrets stay out of the commit.\n *\n * Uses the repo's configured identity, else a synthetic Fluid bot\n * identity so the commit isn't rejected. \"nothing to commit\" is treated\n * as a no-op, not an error.\n */\nexport async function gitAutoCommit(\n cwd: string,\n message: string,\n options: GitAutoCommitOptions = {},\n): Promise<void> {\n const addArgs = options.ignoreRemovals\n ? [\"add\", \"--ignore-removal\", \".\"]\n : [\"add\", \"-A\"];\n try {\n await execa(\"git\", addArgs, { cwd });\n } catch (err) {\n throw toGitError(err, \"add\");\n }\n\n let hasName = false;\n let hasEmail = false;\n try {\n const r = await execa(\"git\", [\"config\", \"user.name\"], { cwd });\n hasName = r.stdout.trim().length > 0;\n } catch {\n // exit 1 from `git config <key>` just means the key is unset.\n }\n try {\n const r = await execa(\"git\", [\"config\", \"user.email\"], { cwd });\n hasEmail = r.stdout.trim().length > 0;\n } catch {\n // ditto.\n }\n const identityArgs = [\n ...(hasName ? [] : [\"-c\", \"user.name=Fluid Portal CLI\"]),\n ...(hasEmail ? [] : [\"-c\", \"user.email=portal@fluid.app\"]),\n ];\n try {\n // BOTH output streams are piped (not inherited) so the catch below\n // can actually inspect them — with \"inherit\", execa's e.stdout /\n // e.stderr are undefined and the documented \"nothing to commit is a\n // no-op\" contract can never match. Notably git prints \"no changes\n // added to commit\" to STDOUT, so piping stderr alone isn't enough.\n // The sync is meant to be invisible, so swallowing the commit\n // summary on success is fine.\n await execa(\"git\", [...identityArgs, \"commit\", \"-m\", message], {\n cwd,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n } catch (err) {\n const missing = gitMissingError(err);\n if (missing) throw missing;\n const e = err as ExecaError;\n // git prints \"nothing to commit\" on stdout or stderr depending on\n // version/porcelain state — check both captured streams. With\n // ignoreRemovals, a tree whose only pending changes are deletions\n // stages nothing, and git phrases that as \"no changes added to\n // commit\" instead — equally a no-op here.\n const streams = `${typeof e.stdout === \"string\" ? e.stdout : \"\"}\\n${typeof e.stderr === \"string\" ? e.stderr : \"\"}`;\n if (/nothing to commit|no changes added to commit/i.test(streams)) {\n return;\n }\n throw toGitError(err, \"commit\");\n }\n}\n\n/**\n * `git push <cleanUrl> HEAD:<branch>` — credentials supplied via\n * GIT_ASKPASS, never argv. `http.…/.extraheader=` clears any cached\n * Authorization header a CI-style config might inject.\n */\nexport async function gitPush(\n cwd: string,\n remoteUrl: string,\n branch = \"main\",\n): Promise<void> {\n const { cleanUrl, env } = gitAuthSpawn(remoteUrl);\n try {\n await execa(\n \"git\",\n [\n \"-c\",\n \"credential.helper=\",\n \"-c\",\n \"http.https://github.com/.extraheader=\",\n \"push\",\n cleanUrl,\n `HEAD:${branch}`,\n ],\n { cwd, env, stdio: [\"ignore\", \"inherit\", \"inherit\"] },\n );\n } catch (err) {\n throw toGitError(err, \"push\");\n }\n}\n","/**\n * GitHub-template scaffolding for `fluid portal create`.\n *\n * The canonical starter lives at github.com/Fluid-WeCommerce/portal-template\n * as a **fully rendered, runnable** project (GitHub's generate-from-template\n * copies files verbatim, so it can't contain `{{handlebars}}` placeholders).\n * The project name is baked in as a distinctive token (CANONICAL_PROJECT_NAME);\n * after cloning we re-substitute it with the user's chosen app name.\n *\n * This is an explicit opt-in create path. The bundled template is the\n * deterministic default, so git or network availability cannot change the\n * normal scaffold output.\n */\n\nimport {\n readdir,\n readFile,\n writeFile,\n rm,\n copyFile,\n stat,\n} from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { gitClone } from \"../lib/git.js\";\n\n/** Public HTTPS URL of the portal starter template repo. */\nexport const TEMPLATE_REPO_URL =\n \"https://github.com/Fluid-WeCommerce/portal-template.git\";\n\n/**\n * The project name baked into the rendered template repo. `fluid portal\n * create` replaces every occurrence with the user's app name after\n * cloning. Must match the name the template was rendered with — keep in\n * sync with the render used to publish the repo.\n */\nexport const CANONICAL_PROJECT_NAME = \"fluid-portal-starter\";\n\n/**\n * Files/dirs we never rewrite when re-substituting the project name.\n * The lockfile keys packages by import path (`.`), not the root name, so\n * touching it would only risk corruption for zero benefit.\n */\nconst SUBSTITUTION_SKIP = new Set([\".git\", \"node_modules\", \"pnpm-lock.yaml\"]);\n\n/** Extensions we treat as text and safe to rewrite. Everything else is\n * left byte-for-byte (avoids mangling any binary asset a future template\n * might carry). */\nconst TEXT_EXTENSIONS = new Set([\n \".ts\",\n \".tsx\",\n \".js\",\n \".jsx\",\n \".mjs\",\n \".cjs\",\n \".json\",\n \".md\",\n \".yml\",\n \".yaml\",\n \".css\",\n \".html\",\n \".txt\",\n]);\n\nfunction hasTextExtension(file: string): boolean {\n const dot = file.lastIndexOf(\".\");\n if (dot < 0) return false;\n return TEXT_EXTENSIONS.has(file.slice(dot));\n}\n\n/** Recursively list absolute file paths, skipping SUBSTITUTION_SKIP dirs. */\nasync function listFiles(dir: string): Promise<string[]> {\n const entries = await readdir(dir, { withFileTypes: true });\n const files: string[] = [];\n for (const entry of entries) {\n if (SUBSTITUTION_SKIP.has(entry.name)) continue;\n const full = join(dir, entry.name);\n if (entry.isDirectory()) {\n files.push(...(await listFiles(full)));\n } else {\n files.push(full);\n }\n }\n return files;\n}\n\n/** Escape a string for use in a RegExp. */\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\n/**\n * Replace every occurrence of the canonical project name with `appName`\n * across the cloned tree's text files. Only files that actually contain\n * the token are rewritten.\n */\nexport async function rewriteProjectName(\n targetPath: string,\n appName: string,\n): Promise<void> {\n if (appName === CANONICAL_PROJECT_NAME) return;\n const token = new RegExp(escapeRegExp(CANONICAL_PROJECT_NAME), \"g\");\n // listFiles already prunes the SUBSTITUTION_SKIP dirs/files.\n const files = await listFiles(targetPath);\n for (const file of files) {\n if (!hasTextExtension(file)) continue;\n const content = await readFile(file, \"utf-8\");\n if (!content.includes(CANONICAL_PROJECT_NAME)) continue;\n // Function replacement so `$` sequences in the app name are taken\n // literally instead of as replacement patterns ($&, $$, …).\n await writeFile(\n file,\n content.replace(token, () => appName),\n \"utf-8\",\n );\n }\n}\n\n/** Rewrite `.fluidrc` with the chosen CLI profile (the template ships an\n * empty profile). No-op when the file is absent. */\nexport async function writeFluidrcProfile(\n targetPath: string,\n profileName: string,\n): Promise<void> {\n const fluidrcPath = join(targetPath, \".fluidrc\");\n if (!existsSync(fluidrcPath)) return;\n await writeFile(\n fluidrcPath,\n JSON.stringify({ profile: profileName }, null, 2) + \"\\n\",\n \"utf-8\",\n );\n}\n\nexport interface ScaffoldFromGitHubOptions {\n readonly targetPath: string;\n readonly appName: string;\n readonly profileName: string;\n /** Override the template repo URL (tests / private mirrors). */\n readonly repoUrl?: string;\n}\n\n/**\n * Clone the GitHub starter template into `targetPath`, discard its git\n * history, re-substitute the project name, wire `.fluidrc`, and copy\n * `.env.example` → `.env`.\n *\n * Throws (GitError or otherwise) on any failure so the explicit remote\n * template request is reported as a failure.\n */\nexport async function scaffoldFromGitHub(\n options: ScaffoldFromGitHubOptions,\n): Promise<void> {\n const { targetPath, appName, profileName } = options;\n const repoUrl = options.repoUrl ?? TEMPLATE_REPO_URL;\n\n // Shallow clone — we throw the history away immediately.\n await gitClone(repoUrl, targetPath, { depth: 1 });\n\n // Drop the template's git history so the new project starts clean.\n await rm(join(targetPath, \".git\"), { recursive: true, force: true });\n\n await rewriteProjectName(targetPath, appName);\n await writeFluidrcProfile(targetPath, profileName);\n\n // Copy .env.example → .env so dotenv works out of the box (matches the\n // bundled scaffold behaviour).\n const envExample = join(targetPath, \".env.example\");\n try {\n if ((await stat(envExample)).isFile()) {\n await copyFile(envExample, join(targetPath, \".env\"));\n }\n } catch {\n // No .env.example — nothing to seed.\n }\n}\n","import { Command } from \"commander\";\nimport type { PluginContext } from \"@fluid-app/fluid-cli\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport { join, dirname, relative, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { copyFile, rm } from \"node:fs/promises\";\nimport type { CreateOptions, ProjectConfig } from \"../types.js\";\nimport { promptProjectConfig } from \"../utils/prompts.js\";\nimport {\n getTemplatePaths,\n copyTemplate,\n directoryExists,\n createDirectory,\n getSdkVersion,\n getCliVersion,\n getPortalCliVersion,\n fileExists,\n getLocalPackageLinkVersion,\n} from \"../utils/file-system.js\";\nimport {\n installDependencies,\n getRunCommand,\n} from \"../utils/package-manager.js\";\nimport { isGitAvailable } from \"../lib/git.js\";\nimport { scaffoldFromGitHub } from \"../utils/scaffold-github.js\";\n\nexport interface CreatePortalProjectDependencies {\n readonly installDependencies: typeof installDependencies;\n readonly isGitAvailable: typeof isGitAvailable;\n readonly scaffoldFromGitHub: typeof scaffoldFromGitHub;\n}\n\nconst defaultCreatePortalProjectDependencies = {\n installDependencies,\n isGitAvailable,\n scaffoldFromGitHub,\n} satisfies CreatePortalProjectDependencies;\n\nexport const createCommand: Command = new Command(\"create\")\n .description(\"Create a new Fluid portal application\")\n .argument(\"<app-name>\", \"Name of the application to create\")\n .option(\n \"--skip-install\",\n \"Create the scaffold only; skip dependency installation\",\n )\n .option(\n \"-o, --output-dir <dir>\",\n \"Directory to create the project in (defaults to cwd)\",\n )\n .option(\n \"--local\",\n \"Use local monorepo packages via file: links (for development testing)\",\n )\n .option(\n \"--remote-template\",\n \"Use the GitHub starter template instead of the bundled template\",\n )\n .action(async (appName: string, options: CreateOptions) => {\n try {\n console.log();\n console.log(chalk.bold(\"Creating a new Fluid portal application\"));\n console.log();\n\n // Validate app name\n if (!/^[a-z0-9-]+$/.test(appName)) {\n console.error(\n chalk.red(\n \"Error: App name must contain only lowercase letters, numbers, and hyphens\",\n ),\n );\n process.exit(1);\n }\n\n // Check if directory already exists\n const targetPath = join(\n resolve(options.outputDir ?? process.cwd()),\n appName,\n );\n if (await directoryExists(targetPath)) {\n console.error(\n chalk.red(`Error: Directory \"${appName}\" already exists`),\n );\n process.exit(1);\n }\n\n // Prompt for configuration\n const config = await promptProjectConfig(appName, options);\n if (!config) {\n console.log();\n console.log(chalk.yellow(\"Cancelled\"));\n process.exit(0);\n }\n\n console.log();\n await createPortalProject(appName, options, config);\n } catch (error) {\n console.log();\n console.log(\n chalk.red(\"Error:\") +\n \" \" +\n (error instanceof Error ? error.message : String(error)),\n );\n console.log();\n process.exit(1);\n }\n });\n\nexport async function createPortalProject(\n appName: string,\n options: CreateOptions,\n config: ProjectConfig,\n dependencies: CreatePortalProjectDependencies = defaultCreatePortalProjectDependencies,\n): Promise<void> {\n const targetPath = join(resolve(options.outputDir ?? process.cwd()), appName);\n const isLocal = options.local === true;\n\n if (options.remoteTemplate === true) {\n if (isLocal) {\n throw new Error(\"--remote-template cannot be combined with --local\");\n }\n if (config.selectedPages.length > 0) {\n throw new Error(\n \"--remote-template cannot be used with optional page selections\",\n );\n }\n if (!(await dependencies.isGitAvailable())) {\n throw new Error(\n \"The remote template requires Git. Remove --remote-template to use the bundled template.\",\n );\n }\n\n const spinner = ora(\"Fetching starter template from GitHub...\").start();\n try {\n await dependencies.scaffoldFromGitHub({\n targetPath,\n appName: config.name,\n profileName: config.profileName,\n });\n spinner.succeed(\"Fetched starter template\");\n } catch (error) {\n spinner.fail(\"Failed to fetch the GitHub starter template\");\n await rm(targetPath, { recursive: true, force: true }).catch(() => {});\n throw error;\n }\n } else {\n await scaffoldFromBundled(targetPath, config, isLocal);\n }\n\n if (config.installDeps) {\n const installSpinner = ora(\"Installing dependencies with pnpm...\").start();\n try {\n await dependencies.installDependencies(targetPath);\n installSpinner.succeed(\"Installed dependencies\");\n } catch (error) {\n installSpinner.fail(\"Failed to install dependencies\");\n console.log();\n console.log(chalk.yellow(`The scaffold was retained at ${targetPath}.`));\n console.log(chalk.yellow(\"Install dependencies manually to continue:\"));\n console.log(chalk.cyan(` cd ${formatCdPath(targetPath)}`));\n console.log(chalk.cyan(\" pnpm install\"));\n throw error;\n }\n }\n\n console.log();\n if (!config.installDeps) {\n console.log(\n chalk.yellow(\n \"Scaffold only: dependencies were not installed and must be installed before development.\",\n ),\n );\n console.log();\n }\n console.log(chalk.green.bold(\"Success!\") + ` Created ${chalk.cyan(appName)}`);\n console.log();\n console.log(\"Next steps:\");\n console.log();\n console.log(chalk.cyan(` cd ${formatCdPath(targetPath)}`));\n if (!config.installDeps) {\n console.log(chalk.cyan(\" pnpm install\"));\n }\n console.log(chalk.cyan(` ${getRunCommand(\"dev\")}`));\n console.log();\n console.log(\n \"Then open \" + chalk.cyan(\"http://localhost:5173\") + \" in your browser.\",\n );\n console.log(\n chalk.dim(\n \" (port may differ if 5173 is in use — check the dev server output)\",\n ),\n );\n console.log();\n if (!config.profileName) {\n console.log(\n chalk.yellow(\n \" Run \" +\n chalk.cyan(\"fluid login\") +\n \" and update \" +\n chalk.cyan(\".fluidrc\") +\n \" with your profile name.\",\n ),\n );\n console.log();\n }\n console.log(\n \"Run \" +\n chalk.cyan(\"pnpm pull\") +\n \" and edit \" +\n chalk.cyan(\"portal/\") +\n \" JSON to customize your portal definition.\",\n );\n console.log();\n}\n\nfunction formatCdPath(targetPath: string): string {\n return relative(process.cwd(), targetPath) || \".\";\n}\n\n/**\n * Scaffold from the bundled base + overlay templates (Handlebars). Used\n * by default, or with `--local`\n * (which rewrites SDK/core deps to workspace `file:`/`link:` paths so the\n * generated project resolves unpublished monorepo source).\n *\n * Throws on failure; the caller reports and exits.\n */\nasync function scaffoldFromBundled(\n targetPath: string,\n config: ProjectConfig,\n isLocal: boolean,\n): Promise<void> {\n // Get template paths (base + overlay)\n const templatePaths = getTemplatePaths(\"starter\");\n if (!(await directoryExists(templatePaths.base))) {\n throw new Error(\"Base template not found\");\n }\n if (!(await directoryExists(templatePaths.overlay))) {\n throw new Error(\"Starter template not found\");\n }\n\n // Get package versions (SDK and CLI are versioned independently)\n let sdkVersion: string;\n let localCoreVersion: string | undefined;\n const [cliVersion, portalCliVersion] = await Promise.all([\n getCliVersion(),\n getPortalCliVersion(),\n ]);\n\n if (isLocal) {\n // Resolve relative file: paths so the generated project is portable\n // within the same monorepo clone (works regardless of absolute\n // location). The portal CLI lives at\n // packages/cli/portal/dist/index.mjs\n // so going up four levels lands us at the monorepo root, then\n // `packages/portal/{sdk,core}` is the canonical location for the\n // SDK + core packages.\n const currentDir = dirname(fileURLToPath(import.meta.url));\n const monorepoRoot = join(currentDir, \"..\", \"..\", \"..\", \"..\");\n const sdkPath = join(monorepoRoot, \"packages\", \"portal\", \"sdk\");\n const corePath = join(monorepoRoot, \"packages\", \"portal\", \"core\");\n\n if (\n !(await directoryExists(sdkPath)) ||\n !(await directoryExists(corePath))\n ) {\n throw new Error(\n \"--local requires running from within the fluid-mono monorepo\\n\" +\n \" Could not find packages/portal/sdk or packages/portal/core\",\n );\n }\n\n sdkVersion = getLocalPackageLinkVersion(targetPath, sdkPath);\n localCoreVersion = getLocalPackageLinkVersion(targetPath, corePath);\n console.log(chalk.cyan(\" Using local packages (--local mode)\"));\n } else {\n sdkVersion = await getSdkVersion();\n }\n\n // Create project directory\n const spinner = ora(\"Creating project directory...\").start();\n try {\n await createDirectory(targetPath);\n spinner.succeed(\"Created project directory\");\n } catch (error) {\n spinner.fail(\"Failed to create project directory\");\n throw error;\n }\n\n // Copy base template first, then overlay template-specific files on top\n const templateVariables = {\n projectName: config.name,\n sdkVersion,\n localCoreVersion,\n cliVersion,\n portalCliVersion,\n selectedPages: config.selectedPages,\n hasSelectedPages: config.selectedPages.length > 0,\n profileName: config.profileName,\n };\n\n spinner.start(\"Copying template files...\");\n try {\n await copyTemplate(templatePaths.base, targetPath, templateVariables);\n await copyTemplate(templatePaths.overlay, targetPath, templateVariables);\n\n // Copy .env.example → .env so dotenv works out of the box\n const envExamplePath = join(targetPath, \".env.example\");\n if (await fileExists(envExamplePath)) {\n await copyFile(envExamplePath, join(targetPath, \".env\"));\n }\n\n spinner.succeed(\"Copied template files\");\n } catch (error) {\n spinner.fail(\"Failed to copy template files\");\n throw error;\n }\n}\n\nexport function registerCreateCommand(ctx: PluginContext): void {\n ctx.program.addCommand(createCommand);\n}\n","/**\n * `fluid portal dev` command\n *\n * Starts the Vite development server with the portal dev plugin,\n * which intercepts manifest API requests and serves content from\n * the local `portal/` directory.\n *\n * If no `portal/` directory exists, prompts the user to run `fluid portal pull`\n * or auto-pulls if they are logged in.\n */\n\nimport { Command } from \"commander\";\nimport type { PluginContext } from \"@fluid-app/fluid-cli\";\nimport chalk from \"chalk\";\nimport { execa } from \"execa\";\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { DevOptions } from \"../types.js\";\n\nconst PORTAL_DIR = \"portal\";\n\n/**\n * Check if the portal directory exists and has content files.\n * Returns true if at minimum `portal/definition.json` exists.\n */\nfunction hasPortalContent(cwd: string): boolean {\n return existsSync(join(cwd, PORTAL_DIR, \"definition.json\"));\n}\n\n/**\n * Attempt to auto-pull portal content by invoking the pull command's action.\n * Falls back to a helpful error message if pull is not possible.\n */\nasync function autoPull(cwd: string): Promise<boolean> {\n console.log();\n console.log(\n chalk.yellow(\"No portal/ directory found.\") +\n \" Attempting to pull content...\",\n );\n console.log();\n\n try {\n // Dynamically import the pull command to avoid circular deps at module level.\n // Auth is handled internally by the pull command via stored CLI credentials\n // (getAuthToken / getActiveProfile), so no explicit token args are needed.\n // Note: the pull command may call process.exit(1) on failure (e.g. auth\n // errors), which will terminate the process rather than throwing. Use\n // --skip-pull to bypass this if auto-pull causes issues.\n const { pullCommand } = await import(\"./pull.js\");\n\n await pullCommand.parseAsync([], { from: \"user\" });\n\n // Verify content was pulled\n return hasPortalContent(cwd);\n } catch (err) {\n console.log();\n console.log(\n chalk.red(\"Auto-pull failed: \") +\n (err instanceof Error ? err.message : String(err)),\n );\n console.log();\n console.log(\n \"Run \" +\n chalk.cyan(\"fluid portal pull\") +\n \" manually to set up local content.\",\n );\n console.log();\n return false;\n }\n}\n\nexport const devCommand: Command = new Command(\"dev\")\n .description(\"Start the development server with local portal content serving\")\n .option(\"-p, --port <port>\", \"Port to run the dev server on\", \"5173\")\n .option(\"--host\", \"Expose the dev server to the network\")\n .option(\"--skip-pull\", \"Skip auto-pull if portal/ directory is missing\")\n .action(async (options: DevOptions & { skipPull?: boolean }) => {\n const cwd = process.cwd();\n\n // Check if we're in a Fluid project\n const packageJsonPath = join(cwd, \"package.json\");\n if (!existsSync(packageJsonPath)) {\n console.error(\n chalk.red(\"Error: No package.json found in current directory\"),\n );\n console.error(\n chalk.yellow(\"Make sure you're in a Fluid project directory\"),\n );\n process.exit(1);\n }\n\n // Check for vite.config\n const viteConfigPath = join(cwd, \"vite.config.ts\");\n if (!existsSync(viteConfigPath)) {\n console.error(chalk.red(\"Error: No vite.config.ts found\"));\n console.error(\n chalk.yellow(\"This command must be run from a Fluid project directory\"),\n );\n process.exit(1);\n }\n\n // ── Auto-pull check ────────────────────────────────────────────────\n if (!hasPortalContent(cwd) && !options.skipPull) {\n const pulled = await autoPull(cwd);\n if (!pulled) {\n console.error(\n chalk.red(\"Cannot start dev server without portal content.\"),\n );\n console.error(\n chalk.yellow(\n \"Run \" +\n chalk.cyan(\"fluid portal pull\") +\n \" to download content first.\",\n ),\n );\n process.exit(1);\n }\n }\n\n if (hasPortalContent(cwd)) {\n console.log();\n console.log(\n chalk.green(\"Portal dev mode: \") +\n \"local content from \" +\n chalk.cyan(\"portal/\") +\n \" will be served\",\n );\n console.log(\n chalk.gray(\n \" Manifest requests intercepted at /api/app/manifest (+ /api/fluid_os/definitions/active)\",\n ),\n );\n console.log(\n chalk.gray(\" File changes in portal/ will trigger a page reload\"),\n );\n }\n\n console.log();\n console.log(chalk.bold(\"Starting development server...\"));\n console.log();\n\n // Fallback args for the degraded path (project's own `vite`).\n const viteArgs = [\"vite\"];\n if (options.port) viteArgs.push(\"--port\", String(options.port));\n if (options.host) viteArgs.push(\"--host\");\n\n // Start Vite programmatically and INJECT the portal dev plugins here, so\n // a portal never has to wire portalDevPlugin/backendDevPlugin into its own\n // vite.config — this command \"just serves the local portal/\". The user's\n // vite.config (react, tailwind, build output) is still loaded and merged\n // underneath. Falls back to spawning the project's `vite` if the\n // programmatic path fails, so behavior degrades rather than breaks.\n // Number.isFinite (not `|| 5173`) so an explicit `--port 0` (\"pick a free\n // port\") is honored rather than coerced back to the default.\n const parsedPort = Number(options.port);\n const port = Number.isFinite(parsedPort) ? parsedPort : 5173;\n\n try {\n const { createServer } = await import(\"vite\");\n const { portalDevPlugin } =\n await import(\"../vite-plugin/portal-dev-plugin.js\");\n const { backendDevPlugin } =\n await import(\"../vite-plugin/backend-dev-plugin.js\");\n\n // No inline `root` or `server.proxy` here: inline config takes\n // precedence over the project's vite.config, so setting them would\n // silently override a project's own root (e.g. root: \"web\") or a\n // local/staging /api proxy. portalDevPlugin's config() hook provides\n // the /api proxy fallback — resolved from the project's env, yielding\n // to any proxy the project already defines.\n const server = await createServer({\n configFile: viteConfigPath,\n envPrefix: [\"VITE_\", \"NEXT_PUBLIC_\"],\n plugins: [portalDevPlugin(), backendDevPlugin()],\n server: {\n port,\n host: options.host ? true : undefined,\n },\n });\n\n await server.listen();\n server.printUrls();\n\n const shutdown = () => {\n void server.close().finally(() => process.exit(0));\n };\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n\n // Keep the process alive until a signal tears the server down.\n await new Promise<never>(() => {});\n } catch (error) {\n console.warn(\n chalk.yellow(\n \"Could not start the enriched dev server; falling back to `vite`. \" +\n \"Local portal content requires portalDevPlugin in vite.config for the fallback path.\",\n ),\n error instanceof Error ? error.message : String(error),\n );\n try {\n await execa(\"pnpm\", viteArgs, { cwd, stdio: \"inherit\" });\n } catch (fallbackErr) {\n // execa v8 sets `signal` (not `code`) when killed by a signal. Treat\n // both SIGINT and SIGTERM (container/Mist orchestration) as clean\n // shutdowns, matching the programmatic path above.\n const execaError = fallbackErr as { signal?: string };\n if (execaError.signal === \"SIGINT\" || execaError.signal === \"SIGTERM\") {\n return;\n }\n console.error(chalk.red(\"Development server exited with an error\"));\n process.exit(1);\n }\n }\n });\n\nexport function registerDevCommand(ctx: PluginContext): void {\n ctx.program.addCommand(devCommand);\n}\n","import { Command } from \"commander\";\nimport type { PluginContext } from \"@fluid-app/fluid-cli\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport { execa } from \"execa\";\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { BuildOptions } from \"../types.js\";\n\nexport const buildCommand: Command = new Command(\"build\")\n .description(\"Build the application for production\")\n .option(\"-o, --out-dir <dir>\", \"Output directory\", \"dist\")\n .action(async (options: BuildOptions) => {\n const cwd = process.cwd();\n\n // Check if we're in a Fluid project\n const packageJsonPath = join(cwd, \"package.json\");\n if (!existsSync(packageJsonPath)) {\n console.error(\n chalk.red(\"Error: No package.json found in current directory\"),\n );\n console.error(\n chalk.yellow(\"Make sure you're in a Fluid project directory\"),\n );\n process.exit(1);\n }\n\n // Check for vite.config\n const viteConfigPath = join(cwd, \"vite.config.ts\");\n if (!existsSync(viteConfigPath)) {\n console.error(chalk.red(\"Error: No vite.config.ts found\"));\n console.error(\n chalk.yellow(\"This command must be run from a Fluid project directory\"),\n );\n process.exit(1);\n }\n\n console.log();\n console.log(chalk.bold(\"Building for production...\"));\n console.log();\n\n const spinner = ora(\"Building...\").start();\n\n try {\n // Run the project's build script\n await execa(\"pnpm\", [\"run\", \"build\"], {\n cwd,\n stdio: \"pipe\",\n });\n\n spinner.succeed(\"Build completed\");\n\n const outDir = options.outDir ?? \"dist\";\n console.log();\n console.log(`Output written to ${chalk.cyan(outDir)}/`);\n console.log();\n console.log(\"To preview the build locally:\");\n console.log(chalk.cyan(\" pnpm vite preview\"));\n console.log();\n } catch (error) {\n spinner.fail(\"Build failed\");\n const execaError = error as { stderr?: string };\n if (execaError.stderr) {\n console.error(execaError.stderr);\n }\n process.exit(1);\n }\n });\n\nexport function registerBuildCommand(ctx: PluginContext): void {\n ctx.program.addCommand(buildCommand);\n}\n","/**\n * Cross-reference validation and change categorization utilities for the push command.\n *\n * Extracted into a standalone utility so that pure logic can be tested\n * without pulling in CLI dependencies (ora, chalk, prompts, etc.).\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport { join, basename } from \"node:path\";\nimport { existsSync, readdirSync } from \"node:fs\";\n\nimport type { PortalMappings } from \"./mappings.js\";\nimport type { SnapshotDiff } from \"./snapshot.js\";\nimport type {\n LocalNavigation,\n LocalNavigationItem,\n LocalProfile,\n} from \"./transform.js\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Categorize changed files by resource type. */\nexport interface CategorizedChanges {\n readonly screens: { new: string[]; changed: string[]; deleted: string[] };\n readonly themes: { new: string[]; changed: string[]; deleted: string[] };\n readonly navigations: { new: string[]; changed: string[]; deleted: string[] };\n readonly profiles: { new: string[]; changed: string[]; deleted: string[] };\n}\n\n/** A validation error found during cross-reference checking. */\nexport interface ValidationError {\n readonly file: string;\n readonly message: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Extract the slug from a file path (e.g., \"screens/home.json\" -> \"home\").\n */\nexport function slugFromPath(filePath: string): string {\n return basename(filePath, \".json\");\n}\n\n/**\n * Extract the resource type directory from a file path (e.g., \"screens/home.json\" -> \"screens\").\n */\nfunction resourceTypeFromPath(\n filePath: string,\n): \"screens\" | \"themes\" | \"navigations\" | \"profiles\" | null {\n const dir = filePath.split(\"/\")[0];\n if (\n dir === \"screens\" ||\n dir === \"themes\" ||\n dir === \"navigations\" ||\n dir === \"profiles\"\n ) {\n return dir;\n }\n return null;\n}\n\n/**\n * Read and parse a JSON file from the portal directory.\n */\nasync function readPortalFile<T>(\n portalDir: string,\n relativePath: string,\n): Promise<T> {\n const filePath = join(portalDir, relativePath);\n const content = await readFile(filePath, \"utf-8\");\n return JSON.parse(content) as T;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Change categorization\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Categorize a snapshot diff into resource-type-specific change lists.\n */\nexport function categorizeChanges(diff: SnapshotDiff): CategorizedChanges {\n const result: CategorizedChanges = {\n screens: { new: [], changed: [], deleted: [] },\n themes: { new: [], changed: [], deleted: [] },\n navigations: { new: [], changed: [], deleted: [] },\n profiles: { new: [], changed: [], deleted: [] },\n };\n\n for (const file of diff.new) {\n const type = resourceTypeFromPath(file);\n if (type) result[type].new.push(file);\n }\n for (const file of diff.changed) {\n const type = resourceTypeFromPath(file);\n if (type) result[type].changed.push(file);\n }\n for (const file of diff.deleted) {\n const type = resourceTypeFromPath(file);\n if (type) result[type].deleted.push(file);\n }\n\n return result;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Cross-reference validation\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Validate that all cross-references between local portal files are valid.\n *\n * Checks:\n * - Navigation items' \"screen\" slugs reference existing screen files or mappings\n * - Profile \"navigation\" and \"mobile_navigation\" slugs reference existing nav files or mappings\n * - Profile \"themes\" slugs reference existing theme files or mappings\n */\nexport async function validateCrossReferences(\n portalDir: string,\n mappings: PortalMappings,\n changes: CategorizedChanges,\n): Promise<ValidationError[]> {\n const errors: ValidationError[] = [];\n\n // Build sets of valid slugs (existing mappings + local files on disk)\n const validScreenSlugs = buildValidSlugsSet(portalDir, \"screens\", mappings);\n const validNavSlugs = buildValidSlugsSet(portalDir, \"navigations\", mappings);\n const validThemeSlugs = buildValidSlugsSet(portalDir, \"themes\", mappings);\n\n // Remove deleted resource slugs from valid sets\n for (const file of changes.screens.deleted) {\n validScreenSlugs.delete(slugFromPath(file));\n }\n for (const file of changes.navigations.deleted) {\n validNavSlugs.delete(slugFromPath(file));\n }\n for (const file of changes.themes.deleted) {\n validThemeSlugs.delete(slugFromPath(file));\n }\n\n // Validate navigation files (new + changed)\n const navFilesToCheck = [\n ...changes.navigations.new,\n ...changes.navigations.changed,\n ];\n for (const file of navFilesToCheck) {\n try {\n const nav = await readPortalFile<LocalNavigation>(portalDir, file);\n validateNavigationItems(\n nav.navigation_items,\n file,\n validScreenSlugs,\n errors,\n );\n } catch {\n errors.push({ file, message: \"Failed to read navigation file\" });\n }\n }\n\n // Validate profile files (new + changed)\n const profileFilesToCheck = [\n ...changes.profiles.new,\n ...changes.profiles.changed,\n ];\n for (const file of profileFilesToCheck) {\n try {\n const profile = await readPortalFile<LocalProfile>(portalDir, file);\n\n if (profile.navigation && !validNavSlugs.has(profile.navigation)) {\n errors.push({\n file,\n message: `References navigation \"${profile.navigation}\" which does not exist`,\n });\n }\n\n if (\n profile.mobile_navigation &&\n !validNavSlugs.has(profile.mobile_navigation)\n ) {\n errors.push({\n file,\n message: `References mobile_navigation \"${profile.mobile_navigation}\" which does not exist`,\n });\n }\n\n for (const themeSlug of profile.themes) {\n if (!validThemeSlugs.has(themeSlug)) {\n errors.push({\n file,\n message: `References theme \"${themeSlug}\" which does not exist`,\n });\n }\n }\n } catch {\n errors.push({ file, message: \"Failed to read profile file\" });\n }\n }\n\n return errors;\n}\n\n/**\n * Build a set of valid slugs for a resource type by combining\n * existing mapping slugs with local file slugs on disk.\n */\nfunction buildValidSlugsSet(\n portalDir: string,\n resourceType: \"screens\" | \"themes\" | \"navigations\" | \"profiles\",\n mappings: PortalMappings,\n): Set<string> {\n const slugs = new Set<string>();\n\n // Add all slugs from mappings\n for (const slug of Object.keys(mappings[resourceType])) {\n slugs.add(slug);\n }\n\n // Add slugs from local files on disk\n const dir = join(portalDir, resourceType);\n if (existsSync(dir)) {\n try {\n const entries = readdirSync(dir);\n for (const entry of entries) {\n if (entry.endsWith(\".json\")) {\n slugs.add(basename(entry, \".json\"));\n }\n }\n } catch {\n // Directory doesn't exist or can't be read — skip\n }\n }\n\n return slugs;\n}\n\n/**\n * Recursively validate navigation item screen references.\n */\nfunction validateNavigationItems(\n items: LocalNavigationItem[],\n file: string,\n validScreenSlugs: Set<string>,\n errors: ValidationError[],\n): void {\n for (const item of items) {\n if (item.screen && !validScreenSlugs.has(item.screen)) {\n errors.push({\n file,\n message: `Navigation item \"${item.label ?? \"(unlabeled)\"}\" references screen \"${item.screen}\" which does not exist`,\n });\n }\n if (item.children && item.children.length > 0) {\n validateNavigationItems(item.children, file, validScreenSlugs, errors);\n }\n }\n}\n","/**\n * Portal git-credential adapter (the \"invisible git\" seam).\n *\n * Mints a short-lived (~1h) remote URL with an embedded installation\n * token for the portal's provisioned GitHub repo. The URL is used\n * immediately for one git op and discarded — NEVER persisted or logged.\n *\n * The backend endpoint (GitSync phase 006, portal adapter) mirrors the\n * widget contract exactly:\n *\n * POST <endpoint> -> 201 { git_credential: { remote_url, expires_at }, status, meta }\n * errors: 409 not_provisioned/not_connected, 502 vendor_error, 422 else\n * { error: { message, details: { type } }, status, meta }\n *\n * The exact path is still being finalized as 006 lands, so it lives in\n * one constant here. Until the endpoint ships to prod a request 404s —\n * which we surface as `unavailable` so callers degrade gracefully\n * (warn + skip the git sync, proceed with the normal content push).\n */\n\n/** Minted credential — token embedded in `remote_url`. Treat as secret. */\nexport interface PortalGitCredential {\n readonly remote_url: string;\n readonly expires_at: string;\n}\n\n/**\n * Outcome of a credential request. `unavailable` means \"no repo to sync\"\n * (endpoint missing / not provisioned / not connected) — the caller\n * should warn and skip, never abort. `error` is an unexpected transport\n * failure the caller may log but still shouldn't hard-fail push on.\n */\nexport type GitCredentialOutcome =\n | { readonly status: \"ok\"; readonly credential: PortalGitCredential }\n | { readonly status: \"unavailable\"; readonly reason: string }\n | { readonly status: \"error\"; readonly reason: string };\n\n/**\n * Build the portal git-credential path for a definition id.\n *\n * Matches GitSync Phase 006 (fluid-commerce/fluid #19596): a member\n * route on the company-scoped definitions resource —\n * `POST /api/company/fluid_os/definitions/:id/git_credential`.\n */\nexport function portalGitCredentialPath(definitionId: number): string {\n return `/api/company/fluid_os/definitions/${definitionId}/git_credential`;\n}\n\nexport interface CreatePortalGitCredentialOptions {\n readonly baseUrl: string;\n readonly token: string;\n readonly definitionId: number;\n /** Injectable for tests; defaults to global fetch. */\n readonly fetchImpl?: typeof fetch;\n}\n\nfunction extractErrorReason(body: unknown, fallback: string): string {\n if (body && typeof body === \"object\" && \"error\" in body) {\n const error = (body as { error?: unknown }).error;\n if (error && typeof error === \"object\" && \"message\" in error) {\n const message = (error as { message?: unknown }).message;\n if (typeof message === \"string\" && message.length > 0) return message;\n }\n }\n return fallback;\n}\n\n/**\n * Request a git credential for the portal definition's provisioned repo.\n *\n * Never throws — every failure maps to an `unavailable`/`error` outcome\n * so the invisible-git sync is strictly best-effort.\n */\nexport async function createPortalGitCredential(\n options: CreatePortalGitCredentialOptions,\n): Promise<GitCredentialOutcome> {\n const { baseUrl, token, definitionId } = options;\n const fetchImpl = options.fetchImpl ?? fetch;\n const url = `${baseUrl.replace(/\\/$/, \"\")}${portalGitCredentialPath(definitionId)}`;\n\n let resp: Response;\n try {\n resp = await fetchImpl(url, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${token}`,\n Accept: \"application/json\",\n },\n });\n } catch (err) {\n return {\n status: \"error\",\n reason: err instanceof Error ? err.message : String(err),\n };\n }\n\n // 404 → endpoint not deployed yet (prod today). Graceful skip.\n if (resp.status === 404) {\n return {\n status: \"unavailable\",\n reason: \"git sync isn't available for portals on this Fluid API yet\",\n };\n }\n\n const body: unknown = await resp.json().catch(() => null);\n\n if (!resp.ok) {\n // 409 not_provisioned/not_connected also means \"nothing to sync yet\".\n const reason = extractErrorReason(\n body,\n `git credential request failed (HTTP ${resp.status})`,\n );\n if (resp.status === 409) return { status: \"unavailable\", reason };\n return { status: \"error\", reason };\n }\n\n const credential = (body as { git_credential?: PortalGitCredential } | null)\n ?.git_credential;\n if (!credential?.remote_url) {\n return {\n status: \"error\",\n reason: \"git credential response missing remote_url\",\n };\n }\n\n return { status: \"ok\", credential };\n}\n","/**\n * Invisible-git sync for `fluid portal push`.\n *\n * Before the normal content push, we best-effort mirror the local portal\n * project to its provisioned GitHub repo: fetch → fast-forward-pull if a\n * teammate pushed → auto-commit working-tree changes → push HEAD:main.\n * This mirrors `fluid mist push`, but is STRICTLY non-fatal: any problem\n * (no git, no repo, credential endpoint missing, diverged history,\n * network blip) degrades to a skip so the content push always proceeds.\n */\n\nimport {\n createPortalGitCredential,\n type CreatePortalGitCredentialOptions,\n} from \"../api/git-credential.js\";\nimport {\n getOriginUrl,\n gitAdoptFetchHead,\n gitAheadBehindVsFetchHead,\n gitAutoCommit,\n gitFetch,\n gitInit,\n gitMergeFastForward,\n gitPush,\n gitRemoteBranchExists,\n hasCommits,\n hasCommonAncestorWithFetchHead,\n hasUncommittedChanges,\n isGitRepo,\n normalizedRepoIdentity,\n} from \"./git.js\";\n\nexport type PortalGitSyncResult =\n | { readonly status: \"skipped\"; readonly reason: string }\n | { readonly status: \"synced\"; readonly pushed: boolean }\n /** Local git state was already mutated (commit / fast-forward) and the\n * push then failed — the caller must NOT proceed with the content\n * push, or published artifacts drift from unsynced source. */\n | { readonly status: \"failed\"; readonly reason: string };\n\nexport interface PortalGitSyncOptions {\n readonly cwd: string;\n readonly baseUrl: string;\n readonly token: string;\n readonly definitionId: number;\n readonly commitMessage: string;\n readonly branch?: string;\n /** Injectable for tests. */\n readonly fetchImpl?: CreatePortalGitCredentialOptions[\"fetchImpl\"];\n}\n\n/**\n * Run the best-effort git sync. Returns a result describing what\n * happened; never throws. Callers surface `skipped` reasons as a dim\n * note and continue.\n */\nexport async function runPortalGitSync(\n options: PortalGitSyncOptions,\n): Promise<PortalGitSyncResult> {\n const { cwd, baseUrl, token, definitionId, commitMessage } = options;\n const branch = options.branch ?? \"main\";\n\n // Ensure a local repo exists — `fluid portal create` strips the\n // template's git history, so the first push initializes one and adopts\n // the provisioned remote's history below (the widget CLI's proven\n // first-push flow). A failed init falls back to the old skip.\n if (!(await isGitRepo(cwd)) && !(await gitInit(cwd))) {\n return { status: \"skipped\", reason: \"not a git repository\" };\n }\n\n // One credential for the whole sync — tokens live ~1h, far beyond a\n // push. Minting again mid-flow would open a degrade window AFTER the\n // auto-commit: a failure there reads as \"sync unavailable\" while the\n // fresh commit stays local and the content push carries on with\n // unsynced source. With a single mint, unavailability can only surface\n // before any local or remote mutation.\n const outcome = await createPortalGitCredential({\n baseUrl,\n token,\n definitionId,\n fetchImpl: options.fetchImpl,\n });\n if (outcome.status !== \"ok\") {\n return { status: \"skipped\", reason: outcome.reason };\n }\n const remoteUrl = outcome.credential.remote_url;\n\n // Identity guard: if this checkout already has an origin, it must be\n // the repo the credential was minted for. Without this, running\n // `fluid portal push` from an UNRELATED git repo would whole-tree\n // auto-commit that repo and push it to the portal's remote.\n const origin = await getOriginUrl(cwd);\n if (origin) {\n const originId = normalizedRepoIdentity(origin);\n const mintedId = normalizedRepoIdentity(remoteUrl);\n // Fail closed: an origin we can't normalize can't be verified, and\n // the downstream merge-base guard doesn't help when repos share\n // starter-template ancestry — skip rather than risk pushing another\n // project to the portal's remote.\n if (mintedId && originId !== mintedId) {\n return {\n status: \"skipped\",\n reason:\n `this directory's git origin (${originId ?? origin}) is not the ` +\n `portal's provisioned repository (${mintedId}) — are you in the ` +\n \"right project directory?\",\n };\n }\n }\n\n try {\n // Detect drift. When strictly behind with a CLEAN tree we can\n // fast-forward a teammate's commits in safely. We deliberately do NOT\n // stash-then-merge a dirty tree: a stash-pop conflict would leave the\n // checkout advanced with conflict markers while the content push\n // carried on. Instead, behind + dirty (and diverged) simply skip and\n // ask the user to reconcile in a terminal — the working tree is left\n // exactly as it was.\n // A freshly-initialized local repo (unborn HEAD) can't reconcile via\n // ahead/behind — and the backend provisions repos WITH an auto-init\n // or starter-template commit, so a fresh root commit would be\n // rejected as unrelated history. Adopt the remote history as the\n // local base instead; the auto-commit below then records the local\n // tree on top of it and the push fast-forwards.\n if (!(await hasCommits(cwd))) {\n if (await gitRemoteBranchExists(cwd, remoteUrl, branch)) {\n await gitFetch(cwd, remoteUrl, branch);\n await gitAdoptFetchHead(cwd);\n }\n if (await hasUncommittedChanges(cwd)) {\n // ADOPTION-ONLY semantics: stage new + modified files, never\n // removals. This directory may hold only a content subset (e.g.\n // populated by `fluid portal pull`, which syncs content but not\n // the repo), so a file absent locally is not a deletion — with\n // plain `add -A`, the first push would delete every remote-only\n // file from the provisioned repo. Local additions/edits win;\n // remote-only files are preserved on adoption. Deletions only\n // ever ship from full checkouts (the `add -A` path below).\n await gitAutoCommit(cwd, commitMessage, { ignoreRemovals: true });\n }\n return await pushCommitted(cwd, remoteUrl, branch);\n }\n\n await gitFetch(cwd, remoteUrl, branch);\n // Second identity guard, for checkouts without an origin: a repo\n // whose history shares no ancestor with the provisioned repo is some\n // OTHER project — never auto-commit and push it wholesale.\n if (!(await hasCommonAncestorWithFetchHead(cwd))) {\n return {\n status: \"skipped\",\n reason:\n \"this repository's history is unrelated to the portal's \" +\n \"provisioned repository — are you in the right project directory?\",\n };\n }\n const { ahead, behind } = await gitAheadBehindVsFetchHead(cwd);\n if (behind > 0 && ahead > 0) {\n return {\n status: \"skipped\",\n reason:\n \"local branch and origin have diverged — run `git pull --rebase` and retry\",\n };\n }\n if (behind > 0) {\n if (await hasUncommittedChanges(cwd)) {\n return {\n status: \"skipped\",\n reason:\n \"origin has newer commits and you have uncommitted changes — \" +\n \"run `git pull` and retry\",\n };\n }\n await gitMergeFastForward(cwd);\n }\n\n // Bundle working-tree edits into one commit so push has something to\n // send. No-op when the tree is already clean.\n if (await hasUncommittedChanges(cwd)) {\n await gitAutoCommit(cwd, commitMessage);\n }\n\n return await pushCommitted(cwd, remoteUrl, branch);\n } catch (err) {\n // Reconcile-phase failure (fetch / ls-remote / adopt / ff-merge /\n // commit). An ff-merge may already have advanced the checkout to\n // include a teammate's commits — that state is valid and self-heals,\n // but say so instead of pretending nothing happened.\n return {\n status: \"skipped\",\n reason: err instanceof Error ? err.message : String(err),\n };\n }\n}\n\n/**\n * The push gets its own failure boundary: by this point the auto-commit\n * (and any fast-forward) has already mutated local git state. A push\n * failure here is FATAL to the content push (matching `fluid widget\n * push`): publishing artifacts whose source never reached the repo is\n * exactly the drift the invisible-git model exists to prevent. The\n * local commit is durable — rerunning push finds the branch ahead and\n * retries the sync.\n */\nasync function pushCommitted(\n cwd: string,\n remoteUrl: string,\n branch: string,\n): Promise<PortalGitSyncResult> {\n try {\n await gitPush(cwd, remoteUrl, branch);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return {\n status: \"failed\",\n reason:\n \"your changes are committed locally but couldn't be pushed \" +\n `(${message}) — nothing was published; rerun \\`fluid portal push\\` ` +\n \"to retry\",\n };\n }\n return { status: \"synced\", pushed: true };\n}\n","/**\n * `fluid portal push` command\n *\n * Pushes local portal content changes to the Fluid OS API.\n * Detects changes since the last pull/push via snapshot diffing,\n * validates cross-references, and pushes resources in dependency order.\n */\n\nimport { Command } from \"commander\";\nimport type { PluginContext } from \"@fluid-app/fluid-cli\";\nimport { getAuthToken, getActiveProfile } from \"@fluid-app/fluid-cli\";\nimport { createFetchClient } from \"@fluid-app/fluidos-api-client\";\nimport type { FetchClient, components } from \"@fluid-app/fluidos-api-client\";\nimport { fluidOs } from \"@fluid-app/fluidos-api-client\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport { join } from \"node:path\";\nimport { readFile } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport prompts from \"prompts\";\n\nimport {\n readMappings,\n writeMappings,\n updateMapping,\n removeMapping,\n resolveSlugToId,\n} from \"../utils/mappings.js\";\nimport type { PortalMappings } from \"../utils/mappings.js\";\nimport {\n readSnapshot,\n diffAgainstSnapshot,\n writeSnapshot,\n computeFileHash,\n} from \"../utils/snapshot.js\";\nimport type { SnapshotDiff } from \"../utils/snapshot.js\";\nimport type {\n LocalScreen,\n LocalTheme,\n LocalNavigation,\n LocalNavigationItem,\n LocalProfile,\n} from \"../utils/transform.js\";\nimport {\n categorizeChanges,\n validateCrossReferences,\n slugFromPath,\n} from \"../utils/push-validation.js\";\nimport type { CategorizedChanges } from \"../utils/push-validation.js\";\nimport { runPortalGitSync } from \"../lib/git-sync.js\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Derived from the generated FluidOSNavigationItem but with `id` optional\n * (new items don't have one yet) and `label`/`position` required (needed\n * for create/update payloads).\n */\ntype NavigationSyncItem = Omit<\n components[\"schemas\"][\"FluidOSNavigationItem\"],\n \"id\" | \"label\" | \"position\" | \"children\"\n> & {\n id?: number;\n label: string;\n position: number;\n children?: NavigationSyncItem[];\n parent_id?: number | null;\n};\n\ninterface PushOptions {\n yes?: boolean;\n}\n\n/** Result of a single push operation. */\ninterface PushResult {\n readonly file: string;\n readonly action: \"created\" | \"updated\" | \"deleted\";\n readonly success: boolean;\n readonly error?: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Constants\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst PORTAL_DIR = \"portal\";\nconst PORTAL_SYNC_DIR = \".portal-sync\";\n\n/**\n * Convert the local array-form component_tree back to the object\n * the API expects. The pull command normalizes the API object into\n * an array for local convenience; this reverses that transformation.\n */\nfunction toApiComponentTree(\n tree: Record<string, unknown>[],\n): Record<string, unknown> | null {\n if (tree.length === 0) return null;\n if (tree.length === 1) return tree[0]!;\n // Fallback: wrap multiple roots in a container (shouldn't happen in practice)\n return { children: tree };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Create an authenticated FetchClient using the stored CLI profile.\n */\nfunction createClient(): FetchClient {\n const token = getAuthToken();\n if (!token) {\n const profile = getActiveProfile();\n if (!profile) {\n throw new Error(\n \"Not logged in. Run \" + chalk.cyan(\"fluid login\") + \" first.\",\n );\n }\n throw new Error(\n \"No auth token found for profile \" +\n chalk.cyan(profile.name) +\n \". Run \" +\n chalk.cyan(\"fluid login\") +\n \" to re-authenticate.\",\n );\n }\n\n const baseUrl = process.env[\"FLUID_API_BASE\"] ?? \"https://api.fluid.app\";\n\n return createFetchClient({\n baseUrl,\n getAuthToken: () => token,\n });\n}\n\n/**\n * Extract an enriched error message from a caught value.\n * Includes structured API error data when available.\n */\nfunction enrichedErrorMessage(err: unknown): string {\n let msg = err instanceof Error ? err.message : String(err);\n if (err && typeof err === \"object\" && \"data\" in err) {\n msg += ` — ${JSON.stringify((err as { data: unknown }).data)}`;\n }\n return msg;\n}\n\n/**\n * Best-effort \"invisible git\" sync: commit the local project and push it\n * to the portal's provisioned GitHub repo before the content push.\n * Degradable failures (no git, endpoint missing, diverged) are reported\n * as a dim note and the content push proceeds. Returns false — with a\n * clean error, never a thrown stack — when local git state was mutated\n * but the push failed, so the caller aborts the content push.\n */\nasync function runInvisibleGitSync(\n cwd: string,\n definitionId: number,\n definitionName: string,\n): Promise<boolean> {\n const token = getAuthToken();\n if (!token) return true; // content push reports the auth problem clearly.\n const baseUrl = process.env[\"FLUID_API_BASE\"] ?? \"https://api.fluid.app\";\n const timestamp = new Date().toISOString().slice(0, 19).replace(\"T\", \" \");\n const commitMessage = `Portal push: ${definitionName} · ${timestamp}`;\n\n const spinner = ora(\"Syncing project to git…\").start();\n const result = await runPortalGitSync({\n cwd,\n baseUrl,\n token,\n definitionId,\n commitMessage,\n });\n if (result.status === \"synced\") {\n spinner.succeed(\"Synced project to git\");\n } else if (result.status === \"failed\") {\n // Local git state was mutated but the push didn't land — abort\n // before the content push so published artifacts can't drift from\n // unsynced source (same contract as `fluid widget push`).\n spinner.fail(\"Git sync failed\");\n // Clean message, no stack trace: this is a user-facing outcome, not\n // a crash.\n console.error(chalk.red(result.reason));\n console.log();\n return false;\n } else {\n spinner.info(chalk.gray(`Skipped git sync — ${result.reason}`));\n }\n console.log();\n return true;\n}\n\n/**\n * Read and parse a JSON file from the portal directory.\n */\nasync function readPortalFile<T>(\n portalDir: string,\n relativePath: string,\n): Promise<T> {\n const filePath = join(portalDir, relativePath);\n const content = await readFile(filePath, \"utf-8\");\n return JSON.parse(content) as T;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Resource push functions\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Push screen changes to the API.\n */\nasync function pushScreens(\n client: FetchClient,\n defId: number,\n portalDir: string,\n changes: CategorizedChanges[\"screens\"],\n mappings: PortalMappings,\n): Promise<{ results: PushResult[]; mappings: PortalMappings }> {\n const results: PushResult[] = [];\n let currentMappings = mappings;\n\n // Create new screens\n for (const file of changes.new) {\n const slug = slugFromPath(file);\n try {\n const local = await readPortalFile<LocalScreen>(portalDir, file);\n const response = await fluidOs.fluid_os_v0_create_fluid_osscreen(\n client,\n defId,\n {\n screen: {\n name: local.name,\n slug,\n component_tree: toApiComponentTree(\n local.component_tree,\n ) as unknown as Record<string, unknown>,\n },\n },\n );\n const newId = response.screen?.id;\n if (newId != null) {\n currentMappings = updateMapping(\n currentMappings,\n \"screens\",\n slug,\n newId,\n );\n }\n results.push({ file, action: \"created\", success: true });\n } catch (err) {\n results.push({\n file,\n action: \"created\",\n success: false,\n error: enrichedErrorMessage(err),\n });\n return { results, mappings: currentMappings };\n }\n }\n\n // Update changed screens\n for (const file of changes.changed) {\n const slug = slugFromPath(file);\n const screenId = resolveSlugToId(currentMappings, \"screens\", slug);\n if (screenId == null) {\n results.push({\n file,\n action: \"updated\",\n success: false,\n error: `No mapping found for screen slug \"${slug}\"`,\n });\n return { results, mappings: currentMappings };\n }\n try {\n const local = await readPortalFile<LocalScreen>(portalDir, file);\n await fluidOs.fluid_os_v0_update_fluid_osscreen(client, defId, screenId, {\n screen: {\n name: local.name,\n slug,\n component_tree: toApiComponentTree(\n local.component_tree,\n ) as unknown as Record<string, unknown>,\n },\n });\n results.push({ file, action: \"updated\", success: true });\n } catch (err) {\n results.push({\n file,\n action: \"updated\",\n success: false,\n error: enrichedErrorMessage(err),\n });\n return { results, mappings: currentMappings };\n }\n }\n\n // Delete screens\n for (const file of changes.deleted) {\n const slug = slugFromPath(file);\n const screenId = resolveSlugToId(currentMappings, \"screens\", slug);\n if (screenId == null) {\n results.push({\n file,\n action: \"deleted\",\n success: false,\n error: `No mapping found for screen slug \"${slug}\"`,\n });\n return { results, mappings: currentMappings };\n }\n try {\n await fluidOs.fluid_os_v0_delete_fluid_osscreen(client, defId, screenId);\n currentMappings = removeMapping(currentMappings, \"screens\", slug);\n results.push({ file, action: \"deleted\", success: true });\n } catch (err) {\n results.push({\n file,\n action: \"deleted\",\n success: false,\n error: enrichedErrorMessage(err),\n });\n return { results, mappings: currentMappings };\n }\n }\n\n return { results, mappings: currentMappings };\n}\n\n/**\n * Push theme changes to the API.\n */\nasync function pushThemes(\n client: FetchClient,\n defId: number,\n portalDir: string,\n changes: CategorizedChanges[\"themes\"],\n mappings: PortalMappings,\n): Promise<{ results: PushResult[]; mappings: PortalMappings }> {\n const results: PushResult[] = [];\n let currentMappings = mappings;\n\n // Create new themes\n for (const file of changes.new) {\n const slug = slugFromPath(file);\n try {\n const local = await readPortalFile<LocalTheme>(portalDir, file);\n const response = await fluidOs.fluid_os_v0_create_fluid_ostheme(\n client,\n defId,\n {\n theme: {\n name: local.name,\n active: local.active,\n config: local.config,\n },\n },\n );\n const newId = response.theme?.id;\n if (newId != null) {\n currentMappings = updateMapping(currentMappings, \"themes\", slug, newId);\n }\n results.push({ file, action: \"created\", success: true });\n } catch (err) {\n results.push({\n file,\n action: \"created\",\n success: false,\n error: enrichedErrorMessage(err),\n });\n return { results, mappings: currentMappings };\n }\n }\n\n // Update changed themes\n for (const file of changes.changed) {\n const slug = slugFromPath(file);\n const themeId = resolveSlugToId(currentMappings, \"themes\", slug);\n if (themeId == null) {\n results.push({\n file,\n action: \"updated\",\n success: false,\n error: `No mapping found for theme slug \"${slug}\"`,\n });\n return { results, mappings: currentMappings };\n }\n try {\n const local = await readPortalFile<LocalTheme>(portalDir, file);\n await fluidOs.fluid_os_v0_update_fluid_ostheme(client, defId, themeId, {\n theme: {\n name: local.name,\n active: local.active,\n config: local.config,\n },\n });\n results.push({ file, action: \"updated\", success: true });\n } catch (err) {\n results.push({\n file,\n action: \"updated\",\n success: false,\n error: enrichedErrorMessage(err),\n });\n return { results, mappings: currentMappings };\n }\n }\n\n // Delete themes\n for (const file of changes.deleted) {\n const slug = slugFromPath(file);\n const themeId = resolveSlugToId(currentMappings, \"themes\", slug);\n if (themeId == null) {\n results.push({\n file,\n action: \"deleted\",\n success: false,\n error: `No mapping found for theme slug \"${slug}\"`,\n });\n return { results, mappings: currentMappings };\n }\n try {\n await fluidOs.fluid_os_v0_delete_fluid_ostheme(client, defId, themeId);\n currentMappings = removeMapping(currentMappings, \"themes\", slug);\n results.push({ file, action: \"deleted\", success: true });\n } catch (err) {\n results.push({\n file,\n action: \"deleted\",\n success: false,\n error: enrichedErrorMessage(err),\n });\n return { results, mappings: currentMappings };\n }\n }\n\n return { results, mappings: currentMappings };\n}\n\n/**\n * Resolve screen slug references to screen IDs in navigation items.\n * Returns a new tree with `screen_id` instead of `screen` slug,\n * shaped to match the FluidOSNavigationItemSyncItem schema.\n */\nfunction resolveNavigationItemScreenIds(\n items: LocalNavigationItem[],\n mappings: PortalMappings,\n): NavigationSyncItem[] {\n return items.map((item) => {\n const screenId = item.screen\n ? (resolveSlugToId(mappings, \"screens\", item.screen) ?? undefined)\n : undefined;\n const result: NavigationSyncItem = {\n ...(item.id ? { id: item.id } : {}),\n label: item.label ?? \"\",\n position: item.position ?? 0,\n icon: item.icon,\n screen_id: screenId ?? null,\n slug: item.slug,\n source: (item.source as \"user\" | \"system\" | \"code\") ?? \"user\",\n parent_id: item.parent_id,\n children: resolveNavigationItemScreenIds(item.children ?? [], mappings),\n };\n return result;\n });\n}\n\n/**\n * Flatten a tree of navigation sync items into a flat list.\n * The API reconciliation logic requires a flat list to correctly\n * compare against the flat server response.\n */\nfunction flattenNavigationItems(\n items: NavigationSyncItem[],\n): NavigationSyncItem[] {\n const flat: NavigationSyncItem[] = [];\n for (const item of items) {\n const { children, ...rest } = item;\n flat.push(rest as NavigationSyncItem);\n if (children && children.length > 0) {\n flat.push(...flattenNavigationItems(children as NavigationSyncItem[]));\n }\n }\n return flat;\n}\n\n/**\n * Push navigation changes to the API.\n */\nasync function pushNavigations(\n client: FetchClient,\n defId: number,\n portalDir: string,\n changes: CategorizedChanges[\"navigations\"],\n mappings: PortalMappings,\n): Promise<{ results: PushResult[]; mappings: PortalMappings }> {\n const results: PushResult[] = [];\n let currentMappings = mappings;\n\n // Create new navigations\n for (const file of changes.new) {\n const slug = slugFromPath(file);\n try {\n const local = await readPortalFile<LocalNavigation>(portalDir, file);\n const response = await fluidOs.fluid_os_v0_create_fluid_osnavigation(\n client,\n defId,\n {\n navigation: {\n name: local.name,\n platform: local.platform as \"web\" | \"mobile\",\n },\n },\n );\n const newId = response.navigation?.id;\n if (newId != null) {\n currentMappings = updateMapping(\n currentMappings,\n \"navigations\",\n slug,\n newId,\n );\n // Create navigation items individually (flatten tree for API).\n // Track local→server ID mapping so child items reference the\n // correct server-assigned parent IDs.\n const resolvedItems = flattenNavigationItems(\n resolveNavigationItemScreenIds(\n local.navigation_items,\n currentMappings,\n ),\n );\n const localToServerId = new Map<number, number>();\n\n for (const item of resolvedItems) {\n const resolvedParentId =\n item.parent_id != null\n ? (localToServerId.get(item.parent_id) ?? item.parent_id)\n : undefined;\n\n const created =\n await fluidOs.fluid_os_v0_create_fluid_osnavigation_item(\n client,\n defId,\n newId,\n {\n navigation_item: {\n label: item.label ?? \"\",\n position: item.position ?? 0,\n icon: item.icon ?? undefined,\n screen_id: item.screen_id ?? undefined,\n slug: item.slug ?? undefined,\n source: item.source ?? undefined,\n parent_id: resolvedParentId,\n },\n },\n );\n\n if (item.id != null && created.navigation_item?.id != null) {\n localToServerId.set(item.id, created.navigation_item.id);\n }\n }\n }\n results.push({ file, action: \"created\", success: true });\n } catch (err) {\n results.push({\n file,\n action: \"created\",\n success: false,\n error: enrichedErrorMessage(err),\n });\n return { results, mappings: currentMappings };\n }\n }\n\n // Update changed navigations\n for (const file of changes.changed) {\n const slug = slugFromPath(file);\n const navId = resolveSlugToId(currentMappings, \"navigations\", slug);\n if (navId == null) {\n results.push({\n file,\n action: \"updated\",\n success: false,\n error: `No mapping found for navigation slug \"${slug}\"`,\n });\n return { results, mappings: currentMappings };\n }\n try {\n const local = await readPortalFile<LocalNavigation>(portalDir, file);\n\n // Update navigation metadata\n await fluidOs.fluid_os_v0_update_fluid_osnavigation(\n client,\n defId,\n navId,\n {\n navigation: {\n name: local.name,\n platform: local.platform as \"web\" | \"mobile\",\n },\n },\n );\n\n // Reconcile navigation items via individual CRUD operations.\n // The bulk sync endpoint is unreliable, so we diff local vs server\n // and issue create/update/delete calls like the admin builder does.\n // Flatten the tree since the API returns/expects a flat list.\n const resolvedItems = flattenNavigationItems(\n resolveNavigationItemScreenIds(local.navigation_items, currentMappings),\n );\n\n const serverResponse =\n await fluidOs.fluid_os_v0_list_fluid_osnavigation_items(\n client,\n defId,\n navId,\n );\n const serverItems = serverResponse.navigation_items ?? [];\n const serverById = new Map(serverItems.map((s) => [s.id, s]));\n const localIds = new Set(\n resolvedItems.filter((i) => i.id).map((i) => i.id),\n );\n\n // Delete server items not in local\n for (const serverItem of serverItems) {\n if (!localIds.has(serverItem.id)) {\n await fluidOs.fluid_os_v0_delete_fluid_osnavigation_item(\n client,\n defId,\n navId,\n serverItem.id,\n );\n }\n }\n\n // Create or update local items.\n // Track local→server ID mapping so newly created child items\n // reference the correct server-assigned parent IDs.\n const localToServerId = new Map<number, number>();\n\n for (const item of resolvedItems) {\n const resolvedParentId =\n item.parent_id != null\n ? (localToServerId.get(item.parent_id) ?? item.parent_id)\n : undefined;\n\n const body = {\n label: item.label,\n position: item.position,\n icon: item.icon ?? undefined,\n screen_id: item.screen_id ?? undefined,\n slug: item.slug ?? undefined,\n source: item.source ?? undefined,\n parent_id: resolvedParentId,\n };\n\n if (item.id && serverById.has(item.id)) {\n // Update existing\n await fluidOs.fluid_os_v0_update_fluid_osnavigation_item(\n client,\n defId,\n navId,\n item.id,\n { navigation_item: body },\n );\n } else {\n // Create new\n const created =\n await fluidOs.fluid_os_v0_create_fluid_osnavigation_item(\n client,\n defId,\n navId,\n {\n navigation_item: {\n ...body,\n label: body.label ?? \"\",\n position: body.position ?? 0,\n },\n },\n );\n\n if (item.id != null && created.navigation_item?.id != null) {\n localToServerId.set(item.id, created.navigation_item.id);\n }\n }\n }\n\n results.push({ file, action: \"updated\", success: true });\n } catch (err) {\n results.push({\n file,\n action: \"updated\",\n success: false,\n error: enrichedErrorMessage(err),\n });\n return { results, mappings: currentMappings };\n }\n }\n\n // Delete navigations\n for (const file of changes.deleted) {\n const slug = slugFromPath(file);\n const navId = resolveSlugToId(currentMappings, \"navigations\", slug);\n if (navId == null) {\n results.push({\n file,\n action: \"deleted\",\n success: false,\n error: `No mapping found for navigation slug \"${slug}\"`,\n });\n return { results, mappings: currentMappings };\n }\n try {\n await fluidOs.fluid_os_v0_delete_fluid_osnavigation(client, defId, navId);\n currentMappings = removeMapping(currentMappings, \"navigations\", slug);\n results.push({ file, action: \"deleted\", success: true });\n } catch (err) {\n results.push({\n file,\n action: \"deleted\",\n success: false,\n error: enrichedErrorMessage(err),\n });\n return { results, mappings: currentMappings };\n }\n }\n\n return { results, mappings: currentMappings };\n}\n\n/**\n * Push profile changes to the API.\n */\nasync function pushProfiles(\n client: FetchClient,\n defId: number,\n portalDir: string,\n changes: CategorizedChanges[\"profiles\"],\n mappings: PortalMappings,\n): Promise<{ results: PushResult[]; mappings: PortalMappings }> {\n const results: PushResult[] = [];\n let currentMappings = mappings;\n\n // Create new profiles\n for (const file of changes.new) {\n const slug = slugFromPath(file);\n try {\n const local = await readPortalFile<LocalProfile>(portalDir, file);\n const body = resolveProfileBody(local, currentMappings);\n const response = await fluidOs.fluid_os_v0_create_fluid_osprofile(\n client,\n defId,\n {\n profile: body,\n },\n );\n const newId = response.profile?.id;\n if (newId != null) {\n currentMappings = updateMapping(\n currentMappings,\n \"profiles\",\n slug,\n newId,\n );\n }\n results.push({ file, action: \"created\", success: true });\n } catch (err) {\n results.push({\n file,\n action: \"created\",\n success: false,\n error: enrichedErrorMessage(err),\n });\n return { results, mappings: currentMappings };\n }\n }\n\n // Update changed profiles\n for (const file of changes.changed) {\n const slug = slugFromPath(file);\n const profileId = resolveSlugToId(currentMappings, \"profiles\", slug);\n if (profileId == null) {\n results.push({\n file,\n action: \"updated\",\n success: false,\n error: `No mapping found for profile slug \"${slug}\"`,\n });\n return { results, mappings: currentMappings };\n }\n try {\n const local = await readPortalFile<LocalProfile>(portalDir, file);\n const body = resolveProfileBody(local, currentMappings);\n await fluidOs.fluid_os_v0_update_fluid_osprofile(\n client,\n defId,\n profileId,\n {\n profile: body,\n },\n );\n results.push({ file, action: \"updated\", success: true });\n } catch (err) {\n results.push({\n file,\n action: \"updated\",\n success: false,\n error: enrichedErrorMessage(err),\n });\n return { results, mappings: currentMappings };\n }\n }\n\n // Delete profiles\n for (const file of changes.deleted) {\n const slug = slugFromPath(file);\n const profileId = resolveSlugToId(currentMappings, \"profiles\", slug);\n if (profileId == null) {\n results.push({\n file,\n action: \"deleted\",\n success: false,\n error: `No mapping found for profile slug \"${slug}\"`,\n });\n return { results, mappings: currentMappings };\n }\n try {\n await fluidOs.fluid_os_v0_delete_fluid_osprofile(\n client,\n defId,\n profileId,\n );\n currentMappings = removeMapping(currentMappings, \"profiles\", slug);\n results.push({ file, action: \"deleted\", success: true });\n } catch (err) {\n results.push({\n file,\n action: \"deleted\",\n success: false,\n error: enrichedErrorMessage(err),\n });\n return { results, mappings: currentMappings };\n }\n }\n\n return { results, mappings: currentMappings };\n}\n\n/** Typed profile body for API requests (create and update share the same shape). */\ninterface ProfileBody {\n name: string;\n default?: boolean;\n navigation_id?: number;\n mobile_navigation_id?: number;\n theme_ids?: number[];\n permissions?: {\n countries?: number[];\n ranks?: number[];\n roles?: string[];\n platform?: string[];\n };\n}\n\n/**\n * Resolve profile slug references to API IDs for create/update request body.\n */\nfunction resolveProfileBody(\n local: LocalProfile,\n mappings: PortalMappings,\n): ProfileBody {\n const body: ProfileBody = {\n name: local.name,\n default: local.default,\n permissions: local.permissions,\n };\n\n if (local.navigation) {\n const navId = resolveSlugToId(mappings, \"navigations\", local.navigation);\n if (navId != null) {\n body.navigation_id = navId;\n }\n }\n\n if (local.mobile_navigation) {\n const mobileNavId = resolveSlugToId(\n mappings,\n \"navigations\",\n local.mobile_navigation,\n );\n if (mobileNavId != null) {\n body.mobile_navigation_id = mobileNavId;\n }\n }\n\n const themeIds = local.themes\n .map((slug) => resolveSlugToId(mappings, \"themes\", slug))\n .filter((id): id is number => id != null);\n body.theme_ids = themeIds;\n\n return body;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Print helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\nfunction printChangesSummary(diff: SnapshotDiff, definitionName: string): void {\n console.log(\n chalk.blue(\"Changes to push for \") +\n chalk.white.bold(`\"${definitionName}\"`) +\n chalk.blue(\":\"),\n );\n console.log();\n\n if (diff.new.length > 0) {\n console.log(chalk.green(\" New: \") + diff.new.join(\", \"));\n }\n if (diff.changed.length > 0) {\n console.log(chalk.yellow(\" Changed: \") + diff.changed.join(\", \"));\n }\n if (diff.deleted.length > 0) {\n console.log(chalk.red(\" Deleted: \") + diff.deleted.join(\", \"));\n }\n console.log();\n}\n\nfunction printPushReport(results: PushResult[], skippedPhases: string[]): void {\n const succeeded = results.filter((r) => r.success);\n const failed = results.filter((r) => !r.success);\n\n if (succeeded.length > 0) {\n console.log(chalk.green.bold(\"Succeeded:\"));\n for (const r of succeeded) {\n console.log(chalk.green(\" \" + r.action + \": \") + r.file);\n }\n }\n\n if (failed.length > 0) {\n console.log();\n console.log(chalk.red.bold(\"Failed:\"));\n for (const r of failed) {\n console.log(\n chalk.red(\" \" + r.action + \": \") +\n r.file +\n chalk.gray(\" — \" + (r.error ?? \"Unknown error\")),\n );\n }\n }\n\n if (skippedPhases.length > 0) {\n console.log();\n console.log(chalk.yellow.bold(\"Skipped phases:\"));\n for (const phase of skippedPhases) {\n console.log(chalk.yellow(\" \" + phase));\n }\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Command\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport const pushCommand: Command = new Command(\"push\")\n .description(\"Push local portal content changes to the Fluid OS API\")\n .option(\"--yes\", \"Skip confirmation prompt\")\n .action(async (options: PushOptions) => {\n const cwd = process.cwd();\n const portalDir = join(cwd, PORTAL_DIR);\n const portalSyncDir = join(cwd, PORTAL_SYNC_DIR);\n\n console.log();\n console.log(chalk.blue.bold(\"Fluid Portal Push\"));\n console.log();\n\n // ── Check for portal directory ─────────────────────────────────────\n if (!existsSync(portalDir)) {\n console.log(\n chalk.red(\"Error:\") +\n \" No portal/ directory found. Run \" +\n chalk.cyan(\"fluid portal pull\") +\n \" first.\",\n );\n console.log();\n process.exit(1);\n }\n\n // ── Read snapshot ──────────────────────────────────────────────────\n const snapshot = await readSnapshot(portalSyncDir);\n if (!snapshot) {\n console.log(\n chalk.red(\"Error:\") +\n \" No snapshot found in .portal-sync/. Run \" +\n chalk.cyan(\"fluid portal pull\") +\n \" first.\",\n );\n console.log();\n process.exit(1);\n }\n\n // ── Read mappings ──────────────────────────────────────────────────\n const mappings = await readMappings(portalSyncDir);\n if (!mappings) {\n console.log(\n chalk.red(\"Error:\") +\n \" No mappings found in .portal-sync/. Run \" +\n chalk.cyan(\"fluid portal pull\") +\n \" first.\",\n );\n console.log();\n process.exit(1);\n }\n\n const definitionId = snapshot.definition_id;\n const definitionName = snapshot.definition;\n\n console.log(\n chalk.gray(\"Definition: \") +\n chalk.white(definitionName) +\n chalk.gray(` (ID: ${definitionId})`),\n );\n console.log();\n\n // ── Invisible git sync (before the content push) ────────────────────\n // Mirror the local project to its provisioned GitHub repo. Degradable\n // problems — no git / no repo / endpoint missing (404) / diverged\n // history — degrade to a dim note and the content push proceeds. The\n // one fatal case is a rejected push AFTER local git state was mutated:\n // publishing then would ship content whose source never reached the\n // repo, so we stop here (clean error already printed).\n if (!(await runInvisibleGitSync(cwd, definitionId, definitionName))) {\n process.exitCode = 1;\n return;\n }\n\n // ── Detect changes ─────────────────────────────────────────────────\n const spinner = ora();\n spinner.start(\"Detecting changes...\");\n\n const diff = await diffAgainstSnapshot(portalDir, snapshot);\n const totalChanges =\n diff.new.length + diff.changed.length + diff.deleted.length;\n\n if (totalChanges === 0) {\n spinner.succeed(\"Nothing to push.\");\n console.log();\n return;\n }\n\n spinner.succeed(`Found ${totalChanges} change(s)`);\n console.log();\n\n // ── Show changes and confirm ───────────────────────────────────────\n printChangesSummary(diff, definitionName);\n\n if (!options.yes) {\n const { confirmed } = await prompts({\n type: \"confirm\",\n name: \"confirmed\",\n message: `Push ${totalChanges} change(s) to Fluid OS?`,\n initial: false,\n });\n\n if (!confirmed) {\n console.log();\n console.log(chalk.gray(\"Push cancelled.\"));\n console.log();\n return;\n }\n console.log();\n }\n\n // ── Categorize changes ─────────────────────────────────────────────\n const changes = categorizeChanges(diff);\n\n // ── Cross-reference validation ─────────────────────────────────────\n spinner.start(\"Validating cross-references...\");\n const validationErrors = await validateCrossReferences(\n portalDir,\n mappings,\n changes,\n );\n\n if (validationErrors.length > 0) {\n spinner.fail(\"Cross-reference validation failed\");\n console.log();\n for (const err of validationErrors) {\n console.log(chalk.red(\" \" + err.file + \": \") + err.message);\n }\n console.log();\n process.exit(1);\n }\n spinner.succeed(\"Cross-references valid\");\n\n // ── Authenticate ───────────────────────────────────────────────────\n spinner.start(\"Authenticating...\");\n\n let client: FetchClient;\n try {\n client = createClient();\n spinner.succeed(\"Authenticated\");\n } catch (err) {\n spinner.fail(\"Authentication failed\");\n console.log();\n console.log(\n chalk.red(\"Error:\") +\n \" \" +\n (err instanceof Error ? err.message : String(err)),\n );\n console.log();\n process.exit(1);\n }\n\n // ── Phase 1: Screens + Themes (parallel) ───────────────────────────\n const allResults: PushResult[] = [];\n const skippedPhases: string[] = [];\n let currentMappings = mappings;\n let aborted = false;\n\n const hasScreenChanges =\n changes.screens.new.length > 0 ||\n changes.screens.changed.length > 0 ||\n changes.screens.deleted.length > 0;\n const hasThemeChanges =\n changes.themes.new.length > 0 ||\n changes.themes.changed.length > 0 ||\n changes.themes.deleted.length > 0;\n\n if (hasScreenChanges || hasThemeChanges) {\n spinner.start(\"Phase 1: Pushing screens and themes...\");\n\n const phase1Tasks: Promise<{\n results: PushResult[];\n mappings: PortalMappings;\n }>[] = [];\n\n if (hasScreenChanges) {\n phase1Tasks.push(\n pushScreens(\n client,\n definitionId,\n portalDir,\n changes.screens,\n currentMappings,\n ),\n );\n }\n if (hasThemeChanges) {\n phase1Tasks.push(\n pushThemes(\n client,\n definitionId,\n portalDir,\n changes.themes,\n currentMappings,\n ),\n );\n }\n\n const phase1Results = await Promise.all(phase1Tasks);\n\n // Merge mappings from both parallel operations\n // Only apply the resource type each operation actually owns\n for (const result of phase1Results) {\n allResults.push(...result.results);\n }\n if (hasScreenChanges) {\n currentMappings = {\n ...currentMappings,\n screens: phase1Results[0]!.mappings.screens,\n };\n }\n if (hasThemeChanges) {\n const idx = hasScreenChanges ? 1 : 0;\n currentMappings = {\n ...currentMappings,\n themes: phase1Results[idx]!.mappings.themes,\n };\n }\n\n const phase1Failed = phase1Results.some((r) =>\n r.results.some((res) => !res.success),\n );\n\n if (phase1Failed) {\n spinner.fail(\"Phase 1 failed\");\n aborted = true;\n skippedPhases.push(\"Phase 2: Navigations\", \"Phase 3: Profiles\");\n } else {\n spinner.succeed(\"Phase 1 complete\");\n }\n }\n\n // ── Phase 2: Navigations ───────────────────────────────────────────\n const hasNavChanges =\n changes.navigations.new.length > 0 ||\n changes.navigations.changed.length > 0 ||\n changes.navigations.deleted.length > 0;\n\n if (!aborted && hasNavChanges) {\n spinner.start(\"Phase 2: Pushing navigations...\");\n\n const navResult = await pushNavigations(\n client,\n definitionId,\n portalDir,\n changes.navigations,\n currentMappings,\n );\n\n allResults.push(...navResult.results);\n currentMappings = {\n ...currentMappings,\n navigations: navResult.mappings.navigations,\n };\n\n const phase2Failed = navResult.results.some((r) => !r.success);\n if (phase2Failed) {\n spinner.fail(\"Phase 2 failed\");\n aborted = true;\n skippedPhases.push(\"Phase 3: Profiles\");\n } else {\n spinner.succeed(\"Phase 2 complete\");\n }\n } else if (aborted && hasNavChanges) {\n // Already marked as skipped above\n }\n\n // ── Phase 3: Profiles ──────────────────────────────────────────────\n const hasProfileChanges =\n changes.profiles.new.length > 0 ||\n changes.profiles.changed.length > 0 ||\n changes.profiles.deleted.length > 0;\n\n if (!aborted && hasProfileChanges) {\n spinner.start(\"Phase 3: Pushing profiles...\");\n\n const profileResult = await pushProfiles(\n client,\n definitionId,\n portalDir,\n changes.profiles,\n currentMappings,\n );\n\n allResults.push(...profileResult.results);\n currentMappings = {\n ...currentMappings,\n profiles: profileResult.mappings.profiles,\n };\n\n const phase3Failed = profileResult.results.some((r) => !r.success);\n if (phase3Failed) {\n spinner.fail(\"Phase 3 failed\");\n } else {\n spinner.succeed(\"Phase 3 complete\");\n }\n }\n\n // ── Update mappings ────────────────────────────────────────────────\n await writeMappings(portalSyncDir, currentMappings);\n\n // ── Update snapshot for successfully pushed files ───────────────────\n const successfulFiles = new Set(\n allResults.filter((r) => r.success).map((r) => r.file),\n );\n\n if (successfulFiles.size > 0) {\n // Only advance snapshot entries for successfully pushed files;\n // leave skipped/failed entries at their original hashes so they\n // show up as changed on the next run.\n const updatedHashes = { ...snapshot.files };\n for (const file of successfulFiles) {\n const fullPath = join(portalDir, file);\n if (existsSync(fullPath)) {\n updatedHashes[file] = await computeFileHash(fullPath);\n } else {\n // deleted file — remove from snapshot\n delete updatedHashes[file];\n }\n }\n await writeSnapshot(portalSyncDir, {\n ...snapshot,\n files: updatedHashes,\n });\n }\n\n // ── Report ─────────────────────────────────────────────────────────\n console.log();\n const allSucceeded = allResults.every((r) => r.success);\n\n if (allSucceeded && !aborted) {\n console.log(chalk.green.bold(\"Push complete!\"));\n } else {\n console.log(chalk.yellow.bold(\"Push completed with issues:\"));\n }\n console.log();\n printPushReport(allResults, skippedPhases);\n console.log();\n });\n\nexport function registerPushCommand(ctx: PluginContext): void {\n ctx.program.addCommand(pushCommand);\n}\n","/**\n * `fluid portal clone [app]` — mints a fresh git credential for a Fluid\n * OS portal definition and clones its provisioned GitHub repo into\n * `./<slug>/`. Mirrors `fluid mist clone` (credential → clone), but\n * against the portal git-credential endpoint.\n *\n * The credential endpoint is part of GitSync phase 006; until it ships\n * the request 404s and we print a friendly \"not available yet\" message\n * instead of a raw git error.\n */\n\nimport { Command } from \"commander\";\nimport type { PluginContext } from \"@fluid-app/fluid-cli\";\nimport { getAuthToken, getActiveProfile } from \"@fluid-app/fluid-cli\";\nimport { createFetchClient } from \"@fluid-app/fluidos-api-client\";\nimport type { FetchClient } from \"@fluid-app/fluidos-api-client\";\nimport { fluidOs } from \"@fluid-app/fluidos-api-client\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport prompts from \"prompts\";\nimport { existsSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { deriveSlug } from \"../utils/mappings.js\";\nimport { createPortalGitCredential } from \"../api/git-credential.js\";\nimport { ensureGitAvailable, gitClone } from \"../lib/git.js\";\n\ninterface CloneOptions {\n app?: string;\n dir?: string;\n}\n\nfunction getBaseUrl(): string {\n return process.env[\"FLUID_API_BASE\"] ?? \"https://api.fluid.app\";\n}\n\nfunction createClient(token: string): FetchClient {\n return createFetchClient({\n baseUrl: getBaseUrl(),\n getAuthToken: () => token,\n });\n}\n\ninterface ResolvedDefinition {\n id: number;\n name: string;\n}\n\n/** Select a definition by --app name, else interactively. */\nasync function selectDefinition(\n client: FetchClient,\n appName?: string,\n): Promise<ResolvedDefinition> {\n const definitions: Array<{ id?: number; name?: string | null }> = [];\n let page = 1;\n while (true) {\n const response = await fluidOs.fluid_os_v0_list_fluid_osdefinitions(\n client,\n { page, per_page: 500 },\n );\n const batch = response.definitions ?? [];\n definitions.push(...batch);\n const totalPages = response.meta?.total_pages ?? 1;\n if (page >= totalPages || batch.length === 0) break;\n page++;\n }\n\n if (definitions.length === 0) {\n throw new Error(\n \"No Fluid OS definitions found. Create one in the admin dashboard first.\",\n );\n }\n\n if (appName) {\n const match = definitions.find(\n (d) => d.name?.toLowerCase() === appName.toLowerCase(),\n );\n if (!match || match.id == null) {\n const available = definitions\n .map((d) => chalk.cyan(d.name ?? \"(unnamed)\"))\n .join(\", \");\n throw new Error(\n `No definition found matching \"${appName}\". Available: ${available}`,\n );\n }\n return { id: match.id, name: match.name ?? `portal-${match.id}` };\n }\n\n const { definitionId } = await prompts({\n type: \"select\",\n name: \"definitionId\",\n message: \"Select a Fluid OS definition to clone\",\n choices: definitions\n .filter((d) => d.id != null)\n .map((d) => ({ title: d.name ?? \"(unnamed)\", value: d.id })),\n });\n if (definitionId == null) throw new Error(\"No definition selected.\");\n const selected = definitions.find((d) => d.id === definitionId);\n if (!selected || selected.id == null) {\n throw new Error(\"Selected definition not found.\");\n }\n return { id: selected.id, name: selected.name ?? `portal-${selected.id}` };\n}\n\nexport const cloneCommand: Command = new Command(\"clone\")\n .description(\n \"Clone a portal definition's provisioned GitHub repo (mints a fresh token per call)\",\n )\n .argument(\"[app]\", \"Definition name (prompts to select when omitted)\")\n .option(\"--app <name>\", \"Definition name (same as the positional argument)\")\n .option(\"--dir <dir>\", \"Target directory (defaults to ./<slug>/)\")\n .action(async (appArg: string | undefined, options: CloneOptions) => {\n try {\n await ensureGitAvailable();\n\n const token = getAuthToken();\n if (!token) {\n const profile = getActiveProfile();\n throw new Error(\n profile\n ? `No auth token for profile ${chalk.cyan(profile.name)}. Run ${chalk.cyan(\"fluid login\")}.`\n : `Not logged in. Run ${chalk.cyan(\"fluid login\")} first.`,\n );\n }\n const client = createClient(token);\n\n const spinner = ora(\"Resolving definition…\").start();\n const definition = await selectDefinition(client, options.app ?? appArg);\n\n spinner.text = \"Minting git credential…\";\n const outcome = await createPortalGitCredential({\n baseUrl: getBaseUrl(),\n token,\n definitionId: definition.id,\n });\n if (outcome.status !== \"ok\") {\n spinner.warn(`Can't clone — ${outcome.reason}`);\n process.exit(1);\n }\n\n const slug = deriveSlug(definition.name);\n const targetDir = resolve(options.dir ?? slug);\n if (existsSync(targetDir)) {\n spinner.fail(\n `Directory ${targetDir} already exists. Pass --dir or remove it first.`,\n );\n process.exit(1);\n }\n\n spinner.text = `Cloning into ${slug}/…`;\n try {\n // Token-bearing URL is passed inline — never written to .git/config.\n await gitClone(outcome.credential.remote_url, targetDir);\n } catch (err) {\n spinner.fail(\"git clone failed\");\n throw err;\n }\n spinner.succeed(`Cloned into ${chalk.cyan(targetDir)}`);\n console.log(chalk.dim(` cd ${slug}`));\n console.log(\n chalk.dim(` ${chalk.cyan(\"fluid portal pull\")} to sync content`),\n );\n } catch (error) {\n console.log();\n console.log(\n chalk.red(\"Error:\") +\n \" \" +\n (error instanceof Error ? error.message : String(error)),\n );\n process.exit(1);\n }\n });\n\nexport function registerCloneCommand(ctx: PluginContext): void {\n ctx.program.addCommand(cloneCommand);\n}\n","/**\n * Pure helper functions for widget scaffolding.\n * Extracted from widget-create command for testability.\n */\n\n/**\n * Convert kebab-case name to PascalCase widget type.\n * e.g., \"stock-ticker\" → \"StockTickerWidget\"\n */\nexport function toWidgetType(name: string): string {\n const pascal = name\n .split(\"-\")\n .map((s) => s.charAt(0).toUpperCase() + s.slice(1))\n .join(\"\");\n return pascal.endsWith(\"Widget\") ? pascal : `${pascal}Widget`;\n}\n\n/**\n * Derive the component name from a widget type.\n * Strips the trailing \"Widget\" suffix, but only if the result is non-empty\n * and starts with a letter (not a digit).\n * e.g., \"StockTickerWidget\" → \"StockTicker\"\n */\nexport function toComponentName(widgetType: string): string {\n if (widgetType === \"Widget\") return \"Widget\";\n const stripped = widgetType.replace(/Widget$/, \"\");\n return stripped || widgetType;\n}\n\n/**\n * Convert kebab-case name to a display name.\n * e.g., \"stock-ticker\" → \"Stock Ticker\"\n */\nexport function toDisplayName(name: string): string {\n return name\n .split(\"-\")\n .map((s) => s.charAt(0).toUpperCase() + s.slice(1))\n .join(\" \");\n}\n\n/**\n * Convert kebab-case to camelCase.\n * e.g., \"stock-ticker\" → \"stockTicker\"\n */\nexport function toCamelCase(name: string): string {\n return name.replace(/-./g, (x) => x.charAt(1).toUpperCase());\n}\n\n/**\n * Validate a widget name for scaffold.\n * Must be kebab-case, no trailing/consecutive dashes, no bare \"widget\".\n */\nexport function validateWidgetName(name: string): string | null {\n if (!/^[a-z][a-z0-9]*(-[a-z0-9]+)*$/.test(name)) {\n return \"Widget name must be kebab-case with no trailing or consecutive dashes (e.g., stock-ticker).\";\n }\n if (name === \"widget\") {\n return 'Widget name \"widget\" is reserved. Use a more descriptive name (e.g., custom-widget).';\n }\n return null;\n}\n\n/**\n * Insert an import line after the last import in a source file.\n * Skips if the import already exists (prevents duplicates on re-scaffold).\n * Returns null if no imports exist in the source.\n */\nexport function insertImport(\n source: string,\n importLine: string,\n): string | null {\n // Skip if import already exists\n if (source.includes(importLine)) return source;\n\n // Find the last real import statement (starts at column 0, not inside comments)\n const importPattern = /^import\\s/gm;\n let lastImportIndex = -1;\n let match: RegExpExecArray | null;\n while ((match = importPattern.exec(source)) !== null) {\n lastImportIndex = match.index;\n }\n if (lastImportIndex === -1) return null;\n\n const lineEnd = source.indexOf(\"\\n\", lastImportIndex);\n if (lineEnd === -1) {\n // Last import is on the final line with no trailing newline\n return source + \"\\n\" + importLine + \"\\n\";\n }\n\n return (\n source.slice(0, lineEnd + 1) + importLine + \"\\n\" + source.slice(lineEnd + 1)\n );\n}\n\n/** Insert a widget definition into a defineWidgetPackage widgets array. */\nexport function insertIntoWidgetPackage(\n source: string,\n exportName: string,\n): string | null {\n const packageStart = source.lastIndexOf(\"defineWidgetPackage(\");\n if (packageStart === -1) return null;\n\n const packageSource = source.slice(packageStart);\n const pattern = /(widgets\\s*:\\s*)\\[([^\\]]*)\\]/g;\n let lastMatch: RegExpExecArray | null = null;\n let m: RegExpExecArray | null;\n while ((m = pattern.exec(packageSource)) !== null) {\n lastMatch = m;\n }\n\n if (!lastMatch) return null;\n\n const fullMatch = lastMatch[0];\n const declaration = lastMatch[1]!;\n const inner = lastMatch[2]!;\n const matchStart = packageStart + lastMatch.index;\n\n const lines = inner.split(\"\\n\");\n\n // Collect existing real entries (stripped of commas for comparison)\n const existingEntries: string[] = [];\n for (const line of lines) {\n const trimmed = line.trim();\n if (trimmed && !trimmed.startsWith(\"//\")) {\n existingEntries.push(trimmed.replace(/,$/, \"\"));\n }\n }\n\n // Skip if already registered\n if (existingEntries.includes(exportName)) {\n return source;\n }\n\n // Preserve comment lines\n const commentLines = lines\n .filter((line) => line.trim().startsWith(\"//\"))\n .map((line) => line.trimEnd());\n\n const commentBlock =\n commentLines.length > 0 ? \"\\n\" + commentLines.join(\"\\n\") : \"\";\n\n // Build entry lines with consistent 2-space indentation\n const allEntries = [...existingEntries, exportName];\n const entryBlock = allEntries.map((e) => ` ${e},`).join(\"\\n\");\n\n const replacement = `${declaration}[${commentBlock}\\n${entryBlock}\\n]`;\n\n return (\n source.slice(0, matchStart) +\n replacement +\n source.slice(matchStart + fullMatch.length)\n );\n}\n","import { Command } from \"commander\";\nimport type { PluginContext } from \"@fluid-app/fluid-cli\";\nimport chalk from \"chalk\";\nimport fs from \"fs-extra\";\nimport path from \"node:path\";\nimport type { WidgetCreateOptions } from \"../types.js\";\nimport {\n insertImport,\n insertIntoWidgetPackage,\n toCamelCase,\n toComponentName,\n toDisplayName,\n toWidgetType,\n validateWidgetName,\n} from \"../utils/widget-helpers.js\";\n\nconst WIDGET_CONFIG_PATH = path.join(\"src\", \"widgets.config.ts\");\n\nconst createSubcommand = new Command(\"create\")\n .description(\"Scaffold a new Remote DOM widget\")\n .argument(\"<name>\", \"Widget name in kebab-case (e.g., stock-ticker)\")\n .option(\n \"-c, --category <category>\",\n \"Widget category for palette grouping\",\n \"components\",\n )\n .action(async (name: string, options: WidgetCreateOptions) => {\n const cwd = process.cwd();\n const packageJsonPath = path.join(cwd, \"package.json\");\n const configPath = path.join(cwd, WIDGET_CONFIG_PATH);\n const widgetDir = path.join(cwd, \"src\", \"widgets\", name);\n\n const validationError = validateWidgetName(name);\n if (validationError) {\n console.error(chalk.red(`Error: ${validationError}`));\n process.exit(1);\n }\n\n if (!(await fs.pathExists(packageJsonPath))) {\n console.error(chalk.red(\"Error: No package.json found.\"));\n console.error(\n chalk.yellow(\"Run this command from a Fluid portal project root.\"),\n );\n process.exit(1);\n }\n\n if (await fs.pathExists(widgetDir)) {\n console.error(\n chalk.red(\n `Error: Widget directory already exists: src/widgets/${name}`,\n ),\n );\n process.exit(1);\n }\n\n const widgetType = toWidgetType(name);\n const componentName = toComponentName(widgetType);\n const camelName = toCamelCase(name);\n const exportName = camelName.endsWith(\"Widget\")\n ? camelName\n : `${camelName}Widget`;\n const displayName = toDisplayName(name);\n const category = options.category ?? \"components\";\n const importLine = `import { ${exportName} } from \"./widgets/${name}\";`;\n\n try {\n await fs.ensureDir(widgetDir);\n await fs.writeFile(\n path.join(widgetDir, \"index.tsx\"),\n createWidgetSource({\n name,\n widgetType,\n componentName,\n exportName,\n displayName,\n category,\n }),\n \"utf-8\",\n );\n\n if (await fs.pathExists(configPath)) {\n const configSource = await fs.readFile(configPath, \"utf-8\");\n if (isEmptyWidgetConfig(configSource)) {\n await fs.writeFile(\n configPath,\n createWidgetConfigSource(importLine, exportName),\n \"utf-8\",\n );\n } else {\n const withImport = insertImport(configSource, importLine);\n const updated = withImport\n ? insertIntoWidgetPackage(withImport, exportName)\n : null;\n if (!updated) {\n throw new Error(\n `${WIDGET_CONFIG_PATH} must export the starter widgetPackages placeholder or contain a defineWidgetPackage({ widgets: [...] }) declaration.`,\n );\n }\n await fs.writeFile(configPath, updated, \"utf-8\");\n }\n } else {\n await fs.writeFile(\n configPath,\n createWidgetConfigSource(importLine, exportName),\n \"utf-8\",\n );\n }\n } catch (error) {\n await fs.remove(widgetDir).catch(() => {});\n console.error(chalk.red(\"Error: Failed to scaffold Remote DOM widget.\"));\n console.error(error);\n process.exit(1);\n }\n\n console.log();\n console.log(\n chalk.green(\"Created widget:\") + ` src/widgets/${name}/index.tsx`,\n );\n console.log(\n chalk.green(\"Registered\") + ` in ${chalk.cyan(WIDGET_CONFIG_PATH)}`,\n );\n console.log();\n console.log(chalk.yellow(\"Next steps:\"));\n console.log(` 1. Edit ${chalk.cyan(`src/widgets/${name}/index.tsx`)}`);\n console.log(\n ` 2. Run ${chalk.cyan(\"fluid portal deploy --dry-run\")} to verify the package`,\n );\n });\n\nfunction createWidgetSource(options: {\n readonly name: string;\n readonly widgetType: string;\n readonly componentName: string;\n readonly exportName: string;\n readonly displayName: string;\n readonly category: string;\n}): string {\n return `import { defineWidget } from \"@fluid-app/portal-sdk/widgets/worker\";\n\ninterface ${options.widgetType}Props {\n readonly title?: string;\n}\n\nfunction ${options.componentName}({ title = ${JSON.stringify(options.displayName)} }: ${options.widgetType}Props) {\n return (\n <section>\n <h3>{title}</h3>\n <p>Edit this widget in src/widgets/${options.name}/index.tsx</p>\n </section>\n );\n}\n\nexport const ${options.exportName} = defineWidget({\n name: ${JSON.stringify(options.componentName)},\n component: ${options.componentName},\n displayName: ${JSON.stringify(options.displayName)},\n description: ${JSON.stringify(`A custom ${options.displayName.toLowerCase()} widget`)},\n icon: \"puzzle-piece\",\n category: ${JSON.stringify(options.category)},\n propertySchema: {\n fields: [{ key: \"title\", label: \"Title\", type: \"text\" }],\n },\n defaultProps: {\n title: ${JSON.stringify(options.displayName)},\n },\n});\n`;\n}\n\nfunction isEmptyWidgetConfig(source: string): boolean {\n return /export\\s+const\\s+widgetPackages\\s*=\\s*\\[\\]\\s+as\\s+const\\s*;/u.test(\n source,\n );\n}\n\nfunction createWidgetConfigSource(\n importLine: string,\n exportName: string,\n): string {\n return `import { defineWidgetPackage } from \"@fluid-app/portal-sdk/widgets/worker\";\n${importLine}\n\nexport const widgetPackage = defineWidgetPackage({\n scope: \"company\",\n packageStableId: \"portal-widgets\",\n version: \"0.0.0-dev\",\n widgets: [${exportName}],\n});\n`;\n}\n\nexport const widgetCommand: Command = new Command(\"widget\")\n .description(\"Manage Remote DOM portal widgets\")\n .addCommand(createSubcommand);\n\nexport function registerWidgetCommand(ctx: PluginContext): void {\n ctx.program.addCommand(widgetCommand);\n}\n","import { execa } from \"execa\";\nimport fs from \"fs-extra\";\nimport { createRequire } from \"node:module\";\nimport path from \"node:path\";\nimport { type Result, success, failure } from \"@fluid-app/fluid-cli\";\n\nconst WIDGET_CONFIG_PATH = \"src/widgets.config.ts\";\nconst SOURCE_PACKAGE_EXTRACT_FILENAME = \"extract-widget-packages.ts\";\nconst SOURCE_PACKAGE_OUTPUT_FILENAME = \"source-widget-packages.json\";\nconst SOURCE_PACKAGE_OUTPUT_SENTINEL = \"fluid-widget-source-packages:v1\";\nconst require = createRequire(import.meta.url);\nconst TSX_CLI_PATH = require.resolve(\"tsx/cli\");\n\nexport interface WidgetSourceConfig {\n readonly path: string;\n readonly relativePath: string;\n}\n\nexport interface WidgetSourceConfigLoadError {\n readonly code: \"CONFIG_LOAD_FAILED\" | \"INVALID_FORMAT\";\n readonly message: string;\n readonly details?: string;\n}\n\nexport async function resolvePortalWidgetSourceConfig(\n projectDir: string,\n): Promise<WidgetSourceConfig | undefined> {\n const candidate = path.join(projectDir, WIDGET_CONFIG_PATH);\n return (await fs.pathExists(candidate))\n ? { path: candidate, relativePath: WIDGET_CONFIG_PATH }\n : undefined;\n}\n\nexport async function loadSourceWidgetPackages(\n projectDir: string,\n): Promise<Result<unknown[], WidgetSourceConfigLoadError>> {\n const config = await resolvePortalWidgetSourceConfig(projectDir);\n if (!config) return success([]);\n\n let tempDir: string | undefined;\n\n try {\n tempDir = await createTempDirectory(projectDir);\n const extractFile = path.join(tempDir, SOURCE_PACKAGE_EXTRACT_FILENAME);\n const outputFile = path.join(tempDir, SOURCE_PACKAGE_OUTPUT_FILENAME);\n\n await fs.writeFile(\n extractFile,\n createSourcePackageExtractorScript({\n projectDir,\n configPath: config.path,\n }),\n { encoding: \"utf-8\", flag: \"wx\" },\n );\n\n await execa(process.execPath, [TSX_CLI_PATH, extractFile, outputFile], {\n cwd: projectDir,\n stdio: \"pipe\",\n env: {\n ...process.env,\n NODE_ENV: \"production\",\n },\n });\n\n const outputResult = await readSourcePackageExtractorOutput(outputFile);\n if (!outputResult.success) return outputResult;\n\n const parsed = outputResult.value;\n if (parsed.length === 0) return success([]);\n\n return success(parsed);\n } catch (err) {\n const error = err as { stderr?: string; message?: string };\n return failure({\n code: \"CONFIG_LOAD_FAILED\",\n message: `Failed to load widget packages from ${config.relativePath}`,\n details: error.stderr ?? error.message ?? String(err),\n });\n } finally {\n if (tempDir) await fs.remove(tempDir).catch(() => {});\n }\n}\n\nasync function createTempDirectory(projectDir: string): Promise<string> {\n const projectTmpDir = path.join(projectDir, \".fluid\", \"tmp\");\n try {\n await fs.ensureDir(projectTmpDir);\n return await fs.mkdtemp(path.join(projectTmpDir, \"widget-packages-\"));\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n throw new Error(\n `Unable to create project-local temporary directory at ${projectTmpDir}: ${message}`,\n );\n }\n}\n\nasync function readSourcePackageExtractorOutput(\n outputFile: string,\n): Promise<Result<unknown[], WidgetSourceConfigLoadError>> {\n let output: string;\n try {\n output = await fs.readFile(outputFile, \"utf-8\");\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return failure({\n code: \"CONFIG_LOAD_FAILED\",\n message: \"Widget package extractor did not write an output file\",\n details: message,\n });\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(output);\n } catch {\n return failure({\n code: \"INVALID_FORMAT\",\n message: \"Failed to parse widget package output file as JSON\",\n details: `Output was: ${output.slice(0, 200)}`,\n });\n }\n\n if (!isRecord(parsed) || parsed.sentinel !== SOURCE_PACKAGE_OUTPUT_SENTINEL) {\n return failure({\n code: \"INVALID_FORMAT\",\n message: \"Widget package extractor output file had an invalid sentinel\",\n });\n }\n\n if (!Array.isArray(parsed.data)) {\n return failure({\n code: \"INVALID_FORMAT\",\n message: \"Extracted widget package output is not an array\",\n details: `Expected an array, got: ${typeof parsed.data}`,\n });\n }\n\n return success(parsed.data);\n}\n\nfunction createSourcePackageExtractorScript(options: {\n readonly projectDir: string;\n readonly configPath: string;\n}): string {\n return `\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { createServer, normalizePath } from \"vite\";\n\nconst SOURCE_PACKAGE_MARKER = \"__fluidSourceWidgetPackage\";\nconst OUTPUT_SENTINEL = ${JSON.stringify(SOURCE_PACKAGE_OUTPUT_SENTINEL)};\nconst projectRoot = ${JSON.stringify(options.projectDir)};\nconst widgetConfigPath = ${JSON.stringify(options.configPath)};\nconst outputPath = process.argv[2];\nif (!outputPath) throw new Error(\"Missing widget package extractor output path.\");\n\nfunction toViteModuleId(modulePath) {\n const relativePath = path.relative(projectRoot, modulePath);\n if (!relativePath.startsWith(\"..\") && !path.isAbsolute(relativePath)) {\n return \"/\" + normalizePath(relativePath);\n }\n return normalizePath(modulePath);\n}\n\nconst server = await createServer({\n root: projectRoot,\n mode: \"production\",\n server: { middlewareMode: true },\n appType: \"custom\",\n logLevel: \"error\",\n clearScreen: false,\n resolve: {\n conditions: [\"module\", \"browser\", \"development|production\"],\n },\n ssr: {\n noExternal: [\"@fluid-app/portal-sdk\"],\n resolve: {\n conditions: [\"module\", \"node\", \"development|production\"],\n },\n },\n});\n\ntry {\n const widgetConfig = await server.ssrLoadModule(toViteModuleId(widgetConfigPath));\n\n function isSourceWidgetPackage(value) {\n return Boolean(value && typeof value === \"object\" && value[SOURCE_PACKAGE_MARKER] === true);\n }\n\n function collectSourceWidgetPackages(mod) {\n const candidates = [\n mod.widgetPackage,\n ...(Array.isArray(mod.widgetPackages) ? mod.widgetPackages : []),\n mod.default,\n ];\n const byPackageId = new Map();\n for (const candidate of candidates) {\n if (!isSourceWidgetPackage(candidate)) continue;\n if (!byPackageId.has(candidate.packageId)) byPackageId.set(candidate.packageId, candidate);\n }\n return Array.from(byPackageId.values());\n }\n\n function serializeWidget(widget) {\n if (!widget || typeof widget !== \"object\" || Array.isArray(widget)) {\n return widget;\n }\n const { component, ...metadata } = widget;\n return metadata;\n }\n\n function serializePackage(sourcePackage) {\n return {\n manifestVersion: sourcePackage.manifestVersion,\n scope: sourcePackage.scope,\n packageStableId: sourcePackage.packageStableId,\n packageId: sourcePackage.packageId,\n packageType: sourcePackage.packageType,\n version: sourcePackage.version,\n cssUrls: sourcePackage.cssUrls,\n widgets: Array.isArray(sourcePackage.widgets)\n ? sourcePackage.widgets.map(serializeWidget)\n : sourcePackage.widgets,\n };\n }\n\n await fs.writeFile(\n outputPath,\n JSON.stringify({\n sentinel: OUTPUT_SENTINEL,\n data: collectSourceWidgetPackages(widgetConfig).map(serializePackage),\n }),\n \"utf-8\",\n );\n} finally {\n await server.close();\n}\n`;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n","import fs from \"fs-extra\";\nimport path from \"node:path\";\nimport { createHash } from \"node:crypto\";\n\nexport const WIDGET_PACKAGE_BUILDER_VERSION = \"1\";\n\nexport interface ArtifactMetadata {\n readonly path: string;\n readonly sha256: string;\n readonly bytes: number;\n readonly contentType: string;\n}\n\nexport interface PublishManifestMetadata {\n readonly packageId: string;\n readonly version: string;\n readonly builderVersion: string;\n readonly cliVersion: string;\n readonly runtimeVersion: string;\n readonly manifestHash: string;\n readonly artifacts: readonly ArtifactMetadata[];\n}\n\nconst CONTENT_TYPES: Readonly<Record<string, string>> = {\n \".css\": \"text/css\",\n \".js\": \"application/javascript\",\n \".json\": \"application/json\",\n \".map\": \"application/json\",\n};\nconst CSS_ARTIFACT_PATH_PATTERN = /^[A-Za-z0-9._~-]+\\.css$/;\nconst JS_MAP_ARTIFACT_PATH_PATTERN = /^[A-Za-z0-9._~-]+\\.js\\.map$/;\n\nexport async function computeArtifactMetadata(\n filePath: string,\n baseDir: string,\n): Promise<ArtifactMetadata> {\n const relativePath = toPosixPath(path.relative(baseDir, filePath));\n assertAllowedArtifactPath(relativePath);\n await assertRegularArtifactFile(filePath, relativePath);\n const buffer = await fs.readFile(filePath);\n\n return {\n path: relativePath,\n sha256: sha256(buffer),\n bytes: buffer.byteLength,\n contentType: getArtifactContentType(filePath),\n };\n}\n\nexport async function collectWidgetPackageArtifacts(\n publishDir: string,\n): Promise<ArtifactMetadata[]> {\n const artifactPaths = await findArtifactPaths(publishDir);\n const artifacts = await Promise.all(\n artifactPaths.map((artifactPath) =>\n computeArtifactMetadata(artifactPath, publishDir),\n ),\n );\n\n assertRequiredUploadArtifacts(artifacts);\n\n return artifacts.sort((a, b) => a.path.localeCompare(b.path));\n}\n\nexport function createPublishManifestMetadata(options: {\n readonly packageId: string;\n readonly version: string;\n readonly cliVersion: string;\n readonly runtimeVersion: string;\n readonly manifestContent: string | Buffer;\n readonly artifacts: readonly ArtifactMetadata[];\n readonly builderVersion?: string;\n}): PublishManifestMetadata {\n return {\n packageId: options.packageId,\n version: options.version,\n builderVersion: options.builderVersion ?? WIDGET_PACKAGE_BUILDER_VERSION,\n cliVersion: options.cliVersion,\n runtimeVersion: options.runtimeVersion,\n manifestHash: sha256(options.manifestContent),\n artifacts: options.artifacts,\n };\n}\n\nexport function getArtifactContentType(filePath: string): string {\n const basename = path.basename(filePath);\n if (basename.endsWith(\".js.map\")) {\n return \"application/json\";\n }\n return CONTENT_TYPES[path.extname(filePath)] ?? \"application/octet-stream\";\n}\n\nexport function sha256(content: string | Buffer): string {\n return createHash(\"sha256\").update(content).digest(\"hex\");\n}\n\nexport function isWidgetPackageCssArtifactPath(relativePath: string): boolean {\n return CSS_ARTIFACT_PATH_PATTERN.test(relativePath);\n}\n\nexport function isWidgetPackageJsMapArtifactPath(\n relativePath: string,\n): boolean {\n return JS_MAP_ARTIFACT_PATH_PATTERN.test(relativePath);\n}\n\nasync function assertRegularArtifactFile(\n filePath: string,\n relativePath: string,\n): Promise<void> {\n const stat = await fs.lstat(filePath);\n if (stat.isSymbolicLink()) {\n throw new Error(\n `Widget publish artifact ${JSON.stringify(relativePath)} must be a regular file and must not be a symbolic link.`,\n );\n }\n if (!stat.isFile()) {\n throw new Error(\n `Widget publish artifact ${JSON.stringify(relativePath)} must be a regular file.`,\n );\n }\n}\n\nasync function findArtifactPaths(\n dir: string,\n baseDir: string = dir,\n): Promise<string[]> {\n if (!(await fs.pathExists(dir))) return [];\n\n const entries = await fs.readdir(dir, { withFileTypes: true });\n const paths = await Promise.all(\n entries.map(async (entry) => {\n const entryPath = path.join(dir, entry.name);\n const relativePath = toPosixPath(path.relative(baseDir, entryPath));\n\n if (entry.isSymbolicLink()) {\n throw new Error(\n `Widget publish artifact ${JSON.stringify(relativePath)} must be a regular file and must not be a symbolic link.`,\n );\n }\n\n if (isUploadArtifact(relativePath)) {\n if (!entry.isFile()) {\n throw new Error(\n `Widget publish artifact ${JSON.stringify(relativePath)} must be a regular file.`,\n );\n }\n return [entryPath];\n }\n\n if (entry.isDirectory()) {\n const nestedPaths = await findArtifactPaths(entryPath, baseDir);\n if (nestedPaths.length === 0) {\n throwUnsupportedArtifactPath(`${relativePath}/`);\n }\n return nestedPaths;\n }\n if (isAllowedGeneratedFile(relativePath)) return [];\n throwUnsupportedArtifactPath(relativePath);\n }),\n );\n\n return paths.flat();\n}\n\nfunction assertRequiredUploadArtifacts(\n artifacts: readonly ArtifactMetadata[],\n): void {\n const artifactPaths = new Set(artifacts.map((artifact) => artifact.path));\n for (const requiredPath of [\"widget.js\", \"manifest.json\"] as const) {\n if (artifactPaths.has(requiredPath)) continue;\n throw new Error(\n `Widget package publish output must include regular file ${JSON.stringify(requiredPath)}.`,\n );\n }\n}\n\nfunction assertAllowedArtifactPath(relativePath: string): void {\n if (isUploadArtifact(relativePath) || isAllowedGeneratedFile(relativePath))\n return;\n throwUnsupportedArtifactPath(relativePath);\n}\n\nfunction isUploadArtifact(relativePath: string): boolean {\n return (\n relativePath === \"widget.js\" ||\n relativePath === \"manifest.json\" ||\n isWidgetPackageCssArtifactPath(relativePath) ||\n isWidgetPackageJsMapArtifactPath(relativePath)\n );\n}\n\nfunction isAllowedGeneratedFile(relativePath: string): boolean {\n return (\n isFlatArtifactPath(relativePath) && relativePath === \"publish-manifest.json\"\n );\n}\n\nfunction isFlatArtifactPath(relativePath: string): boolean {\n return (\n relativePath.length > 0 &&\n !relativePath.includes(\"/\") &&\n !relativePath.includes(\"\\\\\")\n );\n}\n\nfunction throwUnsupportedArtifactPath(relativePath: string): never {\n throw new Error(\n `Unsupported widget publish artifact ${JSON.stringify(relativePath)}. ` +\n \"Allowed files are widget.js, manifest.json, top-level [A-Za-z0-9._~-]+.css, top-level [A-Za-z0-9._~-]+.js.map, and publish-manifest.json.\",\n );\n}\n\nfunction toPosixPath(value: string): string {\n return value.split(path.sep).join(\"/\");\n}\n","export const REMOTE_DOM_WIDGET_MANIFEST_VERSION = 1 as const;\nexport const REMOTE_DOM_WIDGET_RUNTIME = \"remote-dom\" as const;\n\nexport type JsonPrimitive = string | number | boolean | null;\n\nexport type JsonValue = JsonPrimitive | readonly JsonValue[] | JsonObject;\n\nexport interface JsonObject {\n readonly [key: string]: JsonValue;\n}\n\nexport interface RemoteDomWidgetCapabilityDeclaration {\n readonly name: string;\n readonly version: string;\n}\n\nexport interface RemoteDomWidgetDefinition {\n readonly type: string;\n readonly displayName: string;\n readonly description: string;\n readonly icon: string;\n readonly category: string;\n readonly propertySchema: JsonObject;\n readonly defaultProps: JsonObject;\n readonly container: \"inline\" | \"block\" | \"card\" | \"fullscreen\";\n readonly resizable: boolean | \"horizontal\" | \"vertical\" | \"both\";\n readonly minSdkVersion: string;\n readonly capabilities: readonly RemoteDomWidgetCapabilityDeclaration[];\n}\n\n/**\n * Canonical package contract shared by the CLI, catalog APIs, and portal hosts.\n * Catalog producers resolve every artifact to its public URL before returning\n * this descriptor; consumers do not translate legacy manifest shapes.\n */\nexport interface RemoteDomWidgetPackageDescriptor {\n readonly manifestVersion: typeof REMOTE_DOM_WIDGET_MANIFEST_VERSION;\n readonly runtime: typeof REMOTE_DOM_WIDGET_RUNTIME;\n readonly packageId: string;\n readonly source: \"company\" | \"droplet\";\n readonly version: string;\n readonly workerEntryUrl: string;\n readonly cssUrls: readonly string[];\n readonly assetUrls: readonly string[];\n readonly widgets: readonly RemoteDomWidgetDefinition[];\n}\n\nconst PACKAGE_FIELDS = new Set([\n \"manifestVersion\",\n \"runtime\",\n \"packageId\",\n \"source\",\n \"version\",\n \"workerEntryUrl\",\n \"cssUrls\",\n \"assetUrls\",\n \"widgets\",\n]);\n\nconst WIDGET_FIELDS = new Set([\n \"type\",\n \"displayName\",\n \"description\",\n \"icon\",\n \"category\",\n \"propertySchema\",\n \"defaultProps\",\n \"container\",\n \"resizable\",\n \"minSdkVersion\",\n \"capabilities\",\n]);\n\nconst CAPABILITY_FIELDS = new Set([\"name\", \"version\"]);\nconst UNSAFE_JSON_KEYS = new Set([\"__proto__\", \"constructor\", \"prototype\"]);\n\nexport function validateRemoteDomWidgetPackageDescriptor(\n value: unknown,\n): asserts value is RemoteDomWidgetPackageDescriptor {\n const invalidReason = getRemoteDomWidgetPackageInvalidReason(value);\n if (invalidReason) {\n throw new TypeError(`Invalid Remote DOM widget package: ${invalidReason}`);\n }\n}\n\nexport function isRemoteDomWidgetPackageDescriptor(\n value: unknown,\n): value is RemoteDomWidgetPackageDescriptor {\n return getRemoteDomWidgetPackageInvalidReason(value) === undefined;\n}\n\nfunction getRemoteDomWidgetPackageInvalidReason(\n value: unknown,\n): string | undefined {\n if (!isRecord(value)) return \"descriptor must be an object\";\n\n const unexpectedFieldReason = getUnexpectedFieldReason(\n value,\n PACKAGE_FIELDS,\n \"descriptor\",\n );\n if (unexpectedFieldReason) return unexpectedFieldReason;\n\n if (value.manifestVersion !== REMOTE_DOM_WIDGET_MANIFEST_VERSION) {\n return `manifestVersion must be ${REMOTE_DOM_WIDGET_MANIFEST_VERSION}`;\n }\n if (value.runtime !== REMOTE_DOM_WIDGET_RUNTIME) {\n return `runtime must be ${REMOTE_DOM_WIDGET_RUNTIME}`;\n }\n\n const packageIdReason = getNonEmptyStringReason(value.packageId, \"packageId\");\n if (packageIdReason) return packageIdReason;\n\n if (value.source !== \"company\" && value.source !== \"droplet\") {\n return \"source must be company or droplet\";\n }\n\n const versionReason = getNonEmptyStringReason(value.version, \"version\");\n if (versionReason) return versionReason;\n\n const workerEntryUrlReason = getHttpUrlReason(\n value.workerEntryUrl,\n \"workerEntryUrl\",\n );\n if (workerEntryUrlReason) return workerEntryUrlReason;\n\n const cssUrlsReason = getUrlArrayReason(value.cssUrls, \"cssUrls\");\n if (cssUrlsReason) return cssUrlsReason;\n\n const assetUrlsReason = getUrlArrayReason(value.assetUrls, \"assetUrls\");\n if (assetUrlsReason) return assetUrlsReason;\n\n if (!Array.isArray(value.widgets) || value.widgets.length === 0) {\n return \"widgets must be a non-empty array\";\n }\n\n const widgetTypes = new Set<string>();\n for (let index = 0; index < value.widgets.length; index += 1) {\n const widget = value.widgets[index];\n const widgetReason = getWidgetReason(widget, `widgets[${index}]`);\n if (widgetReason) return widgetReason;\n\n const widgetType = widget.type;\n if (widgetTypes.has(widgetType)) {\n return `widgets contains duplicate type ${widgetType}`;\n }\n widgetTypes.add(widgetType);\n }\n\n return undefined;\n}\n\nfunction getWidgetReason(value: unknown, path: string): string | undefined {\n if (!isRecord(value)) return `${path} must be an object`;\n\n const unexpectedFieldReason = getUnexpectedFieldReason(\n value,\n WIDGET_FIELDS,\n path,\n );\n if (unexpectedFieldReason) return unexpectedFieldReason;\n\n for (const field of [\n \"type\",\n \"displayName\",\n \"description\",\n \"icon\",\n \"category\",\n \"minSdkVersion\",\n ] as const) {\n const reason = getNonEmptyStringReason(value[field], `${path}.${field}`);\n if (reason) return reason;\n }\n\n if (!isRecord(value.propertySchema) || !isJsonValue(value.propertySchema)) {\n return `${path}.propertySchema must be a JSON object`;\n }\n if (!isRecord(value.defaultProps) || !isJsonValue(value.defaultProps)) {\n return `${path}.defaultProps must be a JSON object`;\n }\n\n if (\n value.container !== \"inline\" &&\n value.container !== \"block\" &&\n value.container !== \"card\" &&\n value.container !== \"fullscreen\"\n ) {\n return `${path}.container is unsupported`;\n }\n\n if (\n typeof value.resizable !== \"boolean\" &&\n value.resizable !== \"horizontal\" &&\n value.resizable !== \"vertical\" &&\n value.resizable !== \"both\"\n ) {\n return `${path}.resizable is unsupported`;\n }\n\n if (!Array.isArray(value.capabilities)) {\n return `${path}.capabilities must be an array`;\n }\n\n const capabilityNames = new Set<string>();\n for (let index = 0; index < value.capabilities.length; index += 1) {\n const capability = value.capabilities[index];\n const capabilityPath = `${path}.capabilities[${index}]`;\n if (!isRecord(capability)) return `${capabilityPath} must be an object`;\n\n const unexpectedFieldReason = getUnexpectedFieldReason(\n capability,\n CAPABILITY_FIELDS,\n capabilityPath,\n );\n if (unexpectedFieldReason) return unexpectedFieldReason;\n\n const capabilityName = capability.name;\n const nameReason = getNonEmptyStringReason(\n capabilityName,\n `${capabilityPath}.name`,\n );\n if (nameReason) return nameReason;\n if (typeof capabilityName !== \"string\") {\n return `${capabilityPath}.name must be a non-empty string`;\n }\n\n const versionReason = getNonEmptyStringReason(\n capability.version,\n `${capabilityPath}.version`,\n );\n if (versionReason) return versionReason;\n\n if (capabilityNames.has(capabilityName)) {\n return `${path}.capabilities contains duplicate name ${capabilityName}`;\n }\n capabilityNames.add(capabilityName);\n }\n\n return undefined;\n}\n\nfunction getUrlArrayReason(value: unknown, path: string): string | undefined {\n if (!Array.isArray(value)) return `${path} must be an array`;\n\n for (let index = 0; index < value.length; index += 1) {\n const reason = getHttpUrlReason(value[index], `${path}[${index}]`);\n if (reason) return reason;\n }\n return undefined;\n}\n\nfunction getHttpUrlReason(value: unknown, path: string): string | undefined {\n const stringReason = getNonEmptyStringReason(value, path);\n if (stringReason) return stringReason;\n if (typeof value !== \"string\") return `${path} must be a non-empty string`;\n\n try {\n const url = new URL(value);\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") {\n return `${path} must use http or https`;\n }\n if (url.username || url.password) {\n return `${path} must not contain credentials`;\n }\n } catch {\n return `${path} must be an absolute URL`;\n }\n\n return undefined;\n}\n\nfunction getNonEmptyStringReason(\n value: unknown,\n path: string,\n): string | undefined {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n return `${path} must be a non-empty string`;\n }\n if (value !== value.trim())\n return `${path} must not have surrounding whitespace`;\n if (/\\p{Cc}/u.test(value))\n return `${path} must not contain control characters`;\n return undefined;\n}\n\nexport function isJsonValue(value: unknown): value is JsonValue {\n return isJsonValueAtDepth(value, new WeakSet<object>(), 0);\n}\n\nfunction isJsonValueAtDepth(\n value: unknown,\n ancestors: WeakSet<object>,\n depth: number,\n): value is JsonValue {\n if (\n value === null ||\n typeof value === \"string\" ||\n typeof value === \"boolean\"\n ) {\n return true;\n }\n if (typeof value === \"number\") return Number.isFinite(value);\n if (typeof value !== \"object\" || depth > 50 || ancestors.has(value)) {\n return false;\n }\n\n ancestors.add(value);\n const prototype = Object.getPrototypeOf(value);\n const isJson = Array.isArray(value)\n ? value.every((item) => isJsonValueAtDepth(item, ancestors, depth + 1))\n : (prototype === Object.prototype || prototype === null) &&\n isRecord(value) &&\n Object.entries(value).every(\n ([key, item]) =>\n !UNSAFE_JSON_KEYS.has(key) &&\n isJsonValueAtDepth(item, ancestors, depth + 1),\n );\n ancestors.delete(value);\n return isJson;\n}\n\nfunction isRecord(value: unknown): value is Readonly<Record<string, unknown>> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction getUnexpectedFieldReason(\n value: Readonly<Record<string, unknown>>,\n allowedFields: ReadonlySet<string>,\n path: string,\n): string | undefined {\n const unexpectedField = Object.keys(value).find(\n (field) => !allowedFields.has(field),\n );\n return unexpectedField\n ? `${path} contains unsupported field ${unexpectedField}`\n : undefined;\n}\n","import { isWidgetPackageCssArtifactPath } from \"./widget-package-artifacts.js\";\nimport {\n isJsonValue,\n type JsonObject,\n type RemoteDomWidgetDefinition,\n} from \"@fluid-app/portal-core/remote-dom-widget-package\";\n\nconst URL_SAFE_SEGMENT_SOURCE = \"[A-Za-z0-9][A-Za-z0-9_~-]*\";\nconst URL_SAFE_WIDGET_NAME_PATTERN = new RegExp(`^${URL_SAFE_SEGMENT_SOURCE}$`);\nconst URL_SAFE_DOTTED_IDENTIFIER_PATTERN = new RegExp(\n `^${URL_SAFE_SEGMENT_SOURCE}(?:\\\\.${URL_SAFE_SEGMENT_SOURCE})*$`,\n);\nconst SEMVER_CORE_PATTERN = \"(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)\";\nconst SEMVER_PRERELEASE_IDENTIFIER_PATTERN =\n \"(?:0|[1-9]\\\\d*|[0-9A-Za-z-]*[A-Za-z-][0-9A-Za-z-]*)\";\nconst SEMVER_URL_SAFE_PATTERN = new RegExp(\n `^${SEMVER_CORE_PATTERN}(?:-${SEMVER_PRERELEASE_IDENTIFIER_PATTERN}(?:\\\\.${SEMVER_PRERELEASE_IDENTIFIER_PATTERN})*)?$`,\n);\nconst WIDGET_PACKAGE_MANIFEST_VERSION = 1;\nconst DEFAULT_WIDGET_ICON = \"box\";\nconst DEFAULT_WIDGET_CATEGORY = \"components\";\nconst DEFAULT_WIDGET_CONTAINER = \"block\";\nconst DEFAULT_MIN_SDK_VERSION = \"0.0.0\";\n\nexport type WidgetPackageOwnerKind = \"company\" | \"droplet\";\n\nexport type WidgetRuntimeContainer = RemoteDomWidgetDefinition[\"container\"];\nexport type WidgetRuntimeResizable = RemoteDomWidgetDefinition[\"resizable\"];\n\nexport interface WidgetCapabilityMetadata {\n readonly name: string;\n readonly version?: string;\n}\n\nexport interface SourceWidgetMetadata {\n readonly name: string;\n readonly displayName?: string;\n readonly description?: string;\n readonly icon?: string;\n readonly category?: string;\n readonly propertySchema?: JsonObject;\n readonly defaultProps?: JsonObject;\n readonly container?: unknown;\n readonly minSdkVersion?: string;\n readonly resizable?: unknown;\n readonly capabilities?: readonly WidgetCapabilityMetadata[];\n}\n\nexport interface SourceWidgetPackageMetadata {\n readonly manifestVersion?: number;\n readonly scope?: string;\n readonly packageStableId?: string;\n readonly packageId: string;\n readonly packageType?: string;\n readonly version: string;\n readonly widgets: readonly SourceWidgetMetadata[];\n readonly cssUrls?: readonly string[];\n}\n\nexport interface WidgetDescriptorMetadata extends RemoteDomWidgetDefinition {\n readonly name: string;\n}\n\ndeclare const WIDGET_PACKAGE_ARTIFACT_PATH: unique symbol;\n\n/** A package-local artifact path written into the uploaded manifest. */\nexport type WidgetPackageArtifactPath = string & {\n readonly [WIDGET_PACKAGE_ARTIFACT_PATH]: true;\n};\n\n/**\n * Upload-time manifest whose artifact references are package-local paths.\n *\n * This is deliberately distinct from RemoteDomWidgetPackageDescriptor, whose\n * catalog-facing artifact fields must already be resolved to absolute URLs.\n */\nexport interface WidgetPackagePublishManifest {\n readonly manifestVersion: 1;\n readonly runtime: \"remote-dom\";\n readonly packageId: string;\n readonly source: WidgetPackageOwnerKind;\n readonly version: string;\n readonly workerEntryUrl: WidgetPackageArtifactPath;\n readonly cssUrls: readonly WidgetPackageArtifactPath[];\n readonly assetUrls: readonly WidgetPackageArtifactPath[];\n readonly widgets: readonly RemoteDomWidgetDefinition[];\n}\n\nexport interface ValidatedWidgetPackageBuild {\n readonly sourcePackage: SourceWidgetPackageMetadata;\n readonly packageKey: string;\n readonly packageId: string;\n readonly owner: WidgetPackageOwnerKind;\n readonly version: string;\n readonly widgets: readonly WidgetDescriptorMetadata[];\n}\n\nexport type WidgetPackageValidationErrorCode =\n | \"NO_SOURCE_PACKAGE\"\n | \"MULTIPLE_SOURCE_PACKAGES\"\n | \"INVALID_SOURCE_PACKAGE\"\n | \"INVALID_WIDGET_METADATA\"\n | \"UNSUPPORTED_OWNER\"\n | \"INVALID_PACKAGE_KEY\"\n | \"INVALID_VERSION\"\n | \"NO_WIDGETS\"\n | \"INVALID_WIDGET_NAME\"\n | \"INVALID_WIDGET_TYPE\"\n | \"DUPLICATE_WIDGET\";\n\nexport interface WidgetPackageValidationError {\n readonly code: WidgetPackageValidationErrorCode;\n readonly message: string;\n readonly path?: string;\n}\n\nexport interface WidgetPackageValidationResult {\n readonly success: boolean;\n readonly value?: ValidatedWidgetPackageBuild;\n readonly errors: readonly WidgetPackageValidationError[];\n}\n\nexport interface ValidateSourceWidgetPackageOptions {\n readonly owner?: WidgetPackageOwnerKind;\n}\n\nexport function validateSingleSourceWidgetPackage(\n sourcePackages: readonly unknown[],\n options: ValidateSourceWidgetPackageOptions = {},\n): WidgetPackageValidationResult {\n if (sourcePackages.length === 0) {\n return validationFailure({\n code: \"NO_SOURCE_PACKAGE\",\n message:\n \"No defineWidgetPackage source package was found. Export widgetPackage, widgetPackages, or a default widget package from src/widgets.config.ts.\",\n });\n }\n\n if (sourcePackages.length > 1) {\n return validationFailure({\n code: \"MULTIPLE_SOURCE_PACKAGES\",\n message:\n \"Publish/deploy supports exactly one defineWidgetPackage source package. Export one package or split packages into separate builds.\",\n });\n }\n\n const sourcePackageValue = sourcePackages[0];\n if (!sourcePackageValue) {\n return validationFailure({\n code: \"NO_SOURCE_PACKAGE\",\n message: \"No defineWidgetPackage source package was found.\",\n });\n }\n\n const shapeErrors = validateSourcePackageShape(sourcePackageValue);\n if (shapeErrors.length > 0) {\n return { success: false, errors: shapeErrors };\n }\n const sourcePackage = sourcePackageValue as SourceWidgetPackageMetadata;\n\n const errors: WidgetPackageValidationError[] = [];\n const owner = options.owner ?? sourcePackage.packageType;\n if (owner !== \"company\" && owner !== \"droplet\") {\n errors.push({\n code: \"UNSUPPORTED_OWNER\",\n path: \"packageType\",\n message:\n 'Widget package publish/deploy currently supports company and droplet owners. Set packageType to \"company\" or \"droplet\".',\n });\n }\n\n const resolvedOwner = owner === \"droplet\" ? \"droplet\" : \"company\";\n const packageKey = resolvePackageKey(sourcePackage, resolvedOwner);\n const packageKeyPath = readNonEmptyTrimmedString(\n sourcePackage.packageStableId,\n )\n ? \"packageStableId\"\n : \"packageId\";\n if (!isUrlSafeDottedIdentifier(packageKey)) {\n errors.push({\n code: \"INVALID_PACKAGE_KEY\",\n path: packageKeyPath,\n message:\n \"Widget package key must be URL-safe dot-separated text (letters, numbers, '.', '_', '~', and '-' only).\",\n });\n }\n\n if (!isUrlSafeSemver(sourcePackage.version)) {\n errors.push({\n code: \"INVALID_VERSION\",\n path: \"version\",\n message:\n \"Widget package version must be a URL-safe SemVer version without build metadata (for example 1.2.3 or 1.2.3-beta.1).\",\n });\n }\n\n if (sourcePackage.widgets.length === 0) {\n errors.push({\n code: \"NO_WIDGETS\",\n path: \"widgets\",\n message: \"Widget package must include at least one widget.\",\n });\n }\n\n const packageId = `${resolvedOwner}.${packageKey}`;\n const seenNames = new Set<string>();\n const seenTypes = new Set<string>();\n const widgets: WidgetDescriptorMetadata[] = [];\n\n sourcePackage.widgets.forEach((widget, index) => {\n const path = `widgets[${index}]`;\n if (!isUrlSafeWidgetName(widget.name)) {\n errors.push({\n code: \"INVALID_WIDGET_NAME\",\n path: `${path}.name`,\n message:\n \"Widget name must be URL-safe text (letters, numbers, '_', '~', and '-' only) and cannot be empty.\",\n });\n return;\n }\n\n if (seenNames.has(widget.name)) {\n errors.push({\n code: \"DUPLICATE_WIDGET\",\n path: `${path}.name`,\n message: `Duplicate widget name \"${widget.name}\" found in package ${packageKey}.`,\n });\n return;\n }\n seenNames.add(widget.name);\n\n const type = `${packageId}.${widget.name}`;\n if (!isUrlSafeDottedIdentifier(type)) {\n errors.push({\n code: \"INVALID_WIDGET_TYPE\",\n path: `${path}.type`,\n message: `Generated widget type \"${type}\" is not URL-safe.`,\n });\n return;\n }\n\n if (seenTypes.has(type)) {\n errors.push({\n code: \"DUPLICATE_WIDGET\",\n path: `${path}.type`,\n message: `Duplicate generated widget type \"${type}\" found.`,\n });\n return;\n }\n seenTypes.add(type);\n\n widgets.push(sourceWidgetToDescriptor(widget, type));\n });\n\n if (errors.length > 0) return { success: false, errors };\n\n return {\n success: true,\n value: {\n sourcePackage,\n packageKey,\n packageId,\n owner: resolvedOwner,\n version: sourcePackage.version,\n widgets,\n },\n errors: [],\n };\n}\n\nexport function buildWidgetPackagePublishManifest(\n validated: ValidatedWidgetPackageBuild,\n options: {\n readonly cssUrls?: readonly string[];\n } = {},\n): WidgetPackagePublishManifest {\n return {\n manifestVersion: WIDGET_PACKAGE_MANIFEST_VERSION,\n runtime: \"remote-dom\",\n packageId: validated.packageId,\n source: validated.owner,\n version: validated.version,\n // Published manifests name package-local artifacts. Catalog producers\n // resolve these paths to absolute URLs before returning the descriptor to\n // a host, where portal-core performs its absolute-URL ingress validation.\n workerEntryUrl: toWidgetPackageArtifactPath(\"widget.js\"),\n cssUrls: mergeCssUrls(validated.sourcePackage.cssUrls, options.cssUrls),\n assetUrls: [],\n widgets: validated.widgets.map(({ name: _name, ...widget }) => widget),\n };\n}\n\nfunction sourceWidgetToDescriptor(\n widget: SourceWidgetMetadata,\n type: string,\n): WidgetDescriptorMetadata {\n const displayName = widget.displayName ?? widget.name;\n\n return {\n type,\n name: widget.name,\n displayName,\n description: widget.description ?? `Custom widget ${displayName}`,\n icon: widget.icon ?? DEFAULT_WIDGET_ICON,\n category: widget.category ?? DEFAULT_WIDGET_CATEGORY,\n propertySchema: normalizePropertySchema(widget.propertySchema, type),\n defaultProps: widget.defaultProps ?? {},\n container: normalizeContainer(widget.container),\n minSdkVersion: widget.minSdkVersion ?? DEFAULT_MIN_SDK_VERSION,\n resizable: normalizeResizable(widget.resizable),\n capabilities: normalizeCapabilities(widget.capabilities ?? []),\n };\n}\n\nfunction normalizeCapabilities(\n capabilities: readonly WidgetCapabilityMetadata[],\n): readonly Required<WidgetCapabilityMetadata>[] {\n return capabilities.map((capability) => ({\n name: capability.name,\n version: capability.version ?? \"1\",\n }));\n}\n\nfunction validationFailure(\n error: WidgetPackageValidationError,\n): WidgetPackageValidationResult {\n return { success: false, errors: [error] };\n}\n\nfunction validateSourcePackageShape(\n value: unknown,\n): WidgetPackageValidationError[] {\n if (!isRecord(value)) {\n return [\n {\n code: \"INVALID_SOURCE_PACKAGE\",\n message: \"Widget source package must be a JSON object.\",\n },\n ];\n }\n\n const errors: WidgetPackageValidationError[] = [];\n requireStringField(value, \"packageId\", \"packageId\", errors);\n requireStringField(value, \"version\", \"version\", errors);\n validateOptionalStringField(value, \"scope\", \"scope\", errors);\n validateOptionalStringField(\n value,\n \"packageStableId\",\n \"packageStableId\",\n errors,\n );\n validateOptionalStringField(value, \"packageType\", \"packageType\", errors);\n\n if (value.cssUrls !== undefined) {\n if (!Array.isArray(value.cssUrls)) {\n errors.push({\n code: \"INVALID_SOURCE_PACKAGE\",\n path: \"cssUrls\",\n message:\n \"Widget package cssUrls must be an array of strings when present.\",\n });\n } else {\n value.cssUrls.forEach((cssUrl, index) => {\n validateCssUrl(cssUrl, `cssUrls[${index}]`, errors);\n });\n }\n }\n\n if (!Array.isArray(value.widgets)) {\n errors.push({\n code: \"INVALID_SOURCE_PACKAGE\",\n path: \"widgets\",\n message: \"Widget package widgets must be an array.\",\n });\n return errors;\n }\n\n value.widgets.forEach((widget, index) => {\n const widgetPath = `widgets[${index}]`;\n if (!isRecord(widget)) {\n errors.push({\n code: \"INVALID_WIDGET_METADATA\",\n path: widgetPath,\n message: \"Widget metadata must be a JSON object.\",\n });\n return;\n }\n\n requireStringField(widget, \"name\", `${widgetPath}.name`, errors, {\n code: \"INVALID_WIDGET_NAME\",\n message: \"Widget name must be a string and cannot be empty.\",\n });\n validateOptionalStringField(\n widget,\n \"displayName\",\n `${widgetPath}.displayName`,\n errors,\n { requireNonEmpty: true },\n );\n validateOptionalStringField(\n widget,\n \"description\",\n `${widgetPath}.description`,\n errors,\n { requireNonEmpty: true },\n );\n validateOptionalStringField(widget, \"icon\", `${widgetPath}.icon`, errors, {\n requireNonEmpty: true,\n });\n validateOptionalStringField(\n widget,\n \"category\",\n `${widgetPath}.category`,\n errors,\n { requireNonEmpty: true },\n );\n validateOptionalStringField(\n widget,\n \"minSdkVersion\",\n `${widgetPath}.minSdkVersion`,\n errors,\n { requireNonEmpty: true },\n );\n validateOptionalJsonRecordField(\n widget,\n \"propertySchema\",\n `${widgetPath}.propertySchema`,\n errors,\n );\n validateOptionalJsonRecordField(\n widget,\n \"defaultProps\",\n `${widgetPath}.defaultProps`,\n errors,\n );\n validateOptionalCapabilities(\n widget,\n \"capabilities\",\n `${widgetPath}.capabilities`,\n errors,\n );\n });\n\n return errors;\n}\n\nfunction requireStringField(\n record: Record<string, unknown>,\n key: string,\n path: string,\n errors: WidgetPackageValidationError[],\n override?: Pick<WidgetPackageValidationError, \"code\" | \"message\">,\n): void {\n const value = record[key];\n if (typeof value === \"string\" && value.trim().length > 0) return;\n\n errors.push({\n code: override?.code ?? \"INVALID_SOURCE_PACKAGE\",\n path,\n message:\n override?.message ?? `${path} must be a string and cannot be empty.`,\n });\n}\n\nfunction validateOptionalStringField(\n record: Record<string, unknown>,\n key: string,\n path: string,\n errors: WidgetPackageValidationError[],\n options: { readonly requireNonEmpty?: boolean } = {},\n): void {\n const value = record[key];\n if (value === undefined) return;\n if (typeof value === \"string\") {\n if (!options.requireNonEmpty || value.trim().length > 0) return;\n errors.push({\n code: \"INVALID_SOURCE_PACKAGE\",\n path,\n message: `${path} must be a non-empty string when present.`,\n });\n return;\n }\n\n errors.push({\n code: \"INVALID_SOURCE_PACKAGE\",\n path,\n message: `${path} must be a string when present.`,\n });\n}\n\nfunction validateCssUrl(\n value: unknown,\n cssUrlPath: string,\n errors: WidgetPackageValidationError[],\n): void {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n errors.push({\n code: \"INVALID_SOURCE_PACKAGE\",\n path: cssUrlPath,\n message: \"Widget package cssUrls entries must be non-empty strings.\",\n });\n return;\n }\n\n if (!isWidgetPackageCssArtifactPath(value)) {\n errors.push({\n code: \"INVALID_SOURCE_PACKAGE\",\n path: cssUrlPath,\n message:\n \"Widget package cssUrls entries must be top-level generated CSS artifact filenames matching [A-Za-z0-9._~-]+.css. Self-host external CSS or fonts via @font-face in widget CSS so the build can isolate them.\",\n });\n }\n}\n\nfunction validateOptionalJsonRecordField(\n record: Record<string, unknown>,\n key: string,\n path: string,\n errors: WidgetPackageValidationError[],\n): void {\n const value = record[key];\n if (value === undefined) return;\n if (isRecord(value) && isJsonValue(value)) return;\n\n errors.push({\n code: \"INVALID_WIDGET_METADATA\",\n path,\n message: `${path} must be a JSON object when present.`,\n });\n}\n\nfunction validateOptionalCapabilities(\n record: Record<string, unknown>,\n key: string,\n path: string,\n errors: WidgetPackageValidationError[],\n): void {\n const value = record[key];\n if (value === undefined) return;\n if (!Array.isArray(value)) {\n errors.push({\n code: \"INVALID_WIDGET_METADATA\",\n path,\n message: `${path} must be an array when present.`,\n });\n return;\n }\n\n const names = new Set<string>();\n value.forEach((candidate, index) => {\n const capabilityPath = `${path}[${index}]`;\n if (!isRecord(candidate)) {\n errors.push({\n code: \"INVALID_WIDGET_METADATA\",\n path: capabilityPath,\n message: `${capabilityPath} must be a JSON object.`,\n });\n return;\n }\n\n const name = candidate.name;\n if (!isCanonicalMetadataString(name)) {\n errors.push({\n code: \"INVALID_WIDGET_METADATA\",\n path: `${capabilityPath}.name`,\n message: `${capabilityPath}.name must be a non-empty string without surrounding whitespace or control characters.`,\n });\n return;\n }\n\n const version = candidate.version;\n if (version !== undefined && !isCanonicalMetadataString(version)) {\n errors.push({\n code: \"INVALID_WIDGET_METADATA\",\n path: `${capabilityPath}.version`,\n message: `${capabilityPath}.version must be a non-empty string without surrounding whitespace or control characters when present.`,\n });\n }\n\n if (names.has(name)) {\n errors.push({\n code: \"INVALID_WIDGET_METADATA\",\n path: `${capabilityPath}.name`,\n message: `${path} must not contain duplicate capability name ${JSON.stringify(name)}.`,\n });\n return;\n }\n names.add(name);\n });\n}\n\nfunction isCanonicalMetadataString(value: unknown): value is string {\n return (\n typeof value === \"string\" &&\n value.length > 0 &&\n value === value.trim() &&\n !/\\p{Cc}/u.test(value)\n );\n}\n\nfunction mergeCssUrls(\n sourceCssUrls: readonly string[] | undefined,\n generatedCssUrls: readonly string[] | undefined,\n): WidgetPackageArtifactPath[] {\n const generatedCssUrlSet = new Set(generatedCssUrls ?? []);\n generatedCssUrls?.forEach((cssUrl, index) => {\n validateGeneratedCssArtifactUrl(cssUrl, `generated cssUrls[${index}]`);\n });\n sourceCssUrls?.forEach((cssUrl, index) => {\n validateGeneratedCssArtifactUrl(cssUrl, `cssUrls[${index}]`);\n if (generatedCssUrlSet.has(cssUrl)) return;\n\n throw new Error(\n `cssUrls[${index}] URL ${JSON.stringify(cssUrl)} must match a generated top-level CSS artifact. ` +\n \"Import the CSS from the widget package bundle or remove it from cssUrls.\",\n );\n });\n\n return Array.from(\n new Set([...(sourceCssUrls ?? []), ...(generatedCssUrls ?? [])]),\n ).map(toWidgetPackageArtifactPath);\n}\n\nfunction toWidgetPackageArtifactPath(value: string): WidgetPackageArtifactPath {\n return value as WidgetPackageArtifactPath;\n}\n\nfunction validateGeneratedCssArtifactUrl(\n value: string,\n cssUrlPath: string,\n): void {\n if (value.trim().length === 0) {\n throw new Error(`${cssUrlPath} must be a non-empty string.`);\n }\n\n if (!isWidgetPackageCssArtifactPath(value)) {\n throw new Error(\n `${cssUrlPath} must be a top-level generated CSS artifact filename matching [A-Za-z0-9._~-]+.css. Self-host external CSS or fonts via @font-face in widget CSS so the build can isolate them.`,\n );\n }\n}\n\nfunction normalizePropertySchema(\n value: JsonObject | undefined,\n widgetType: string,\n): JsonObject {\n return { ...(value ?? {}), widgetType };\n}\n\nfunction normalizeContainer(value: unknown): WidgetRuntimeContainer {\n if (\n value === \"inline\" ||\n value === \"block\" ||\n value === \"card\" ||\n value === \"fullscreen\"\n ) {\n return value;\n }\n\n return DEFAULT_WIDGET_CONTAINER;\n}\n\nfunction normalizeResizable(value: unknown): WidgetRuntimeResizable {\n if (typeof value === \"boolean\") return value;\n if (value === \"horizontal\" || value === \"vertical\" || value === \"both\") {\n return value;\n }\n if (!isRecord(value)) return false;\n\n const horizontal = value.horizontal === true;\n const vertical = value.vertical === true;\n\n if (horizontal && vertical) return \"both\";\n if (horizontal) return \"horizontal\";\n if (vertical) return \"vertical\";\n return false;\n}\n\nfunction resolvePackageKey(\n sourcePackage: SourceWidgetPackageMetadata,\n owner: WidgetPackageOwnerKind,\n): string {\n const packageStableId = readNonEmptyTrimmedString(\n sourcePackage.packageStableId,\n );\n if (packageStableId) {\n return prefixPackageStableIdWithNonOwnerScope(\n packageStableId,\n sourcePackage.scope,\n );\n }\n\n return stripOwnerScopeFromPackageId(\n sourcePackage.packageId,\n sourcePackage.scope,\n owner,\n );\n}\n\nfunction prefixPackageStableIdWithNonOwnerScope(\n packageStableId: string,\n sourceScope: string | undefined,\n): string {\n const sourceScopeText = readNonEmptyTrimmedString(sourceScope);\n if (!sourceScopeText || isWidgetPackageOwnerKind(sourceScopeText)) {\n return packageStableId;\n }\n\n const sourceScopePrefix = `${sourceScopeText}.`;\n if (packageStableId.startsWith(sourceScopePrefix)) return packageStableId;\n return `${sourceScopeText}.${packageStableId}`;\n}\n\nfunction stripOwnerScopeFromPackageId(\n packageId: string,\n sourceScope: string | undefined,\n owner: WidgetPackageOwnerKind,\n): string {\n const ownerPrefix = `${owner}.`;\n if (\n packageId.startsWith(ownerPrefix) &&\n packageId.length > ownerPrefix.length\n ) {\n return packageId.slice(ownerPrefix.length);\n }\n\n const sourceScopeText = readNonEmptyTrimmedString(sourceScope);\n if (\n isWidgetPackageOwnerKind(sourceScopeText) &&\n packageId.startsWith(`${sourceScopeText}.`) &&\n packageId.length > sourceScopeText.length + 1\n ) {\n return packageId.slice(sourceScopeText.length + 1);\n }\n\n return packageId;\n}\n\nfunction readNonEmptyTrimmedString(\n value: string | undefined,\n): string | undefined {\n const trimmed = value?.trim();\n return trimmed ? trimmed : undefined;\n}\n\nfunction isWidgetPackageOwnerKind(\n value: string | undefined,\n): value is WidgetPackageOwnerKind {\n return value === \"company\" || value === \"droplet\";\n}\n\nfunction isUrlSafeDottedIdentifier(value: string): boolean {\n return URL_SAFE_DOTTED_IDENTIFIER_PATTERN.test(value);\n}\n\nfunction isUrlSafeWidgetName(value: string): boolean {\n return URL_SAFE_WIDGET_NAME_PATTERN.test(value);\n}\n\nfunction isUrlSafeSemver(value: string): boolean {\n return SEMVER_URL_SAFE_PATTERN.test(value);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n","import { execa } from \"execa\";\nimport fs from \"fs-extra\";\nimport path from \"node:path\";\nimport { createRequire } from \"node:module\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\nimport { type Result, success, failure } from \"@fluid-app/fluid-cli\";\nimport {\n loadSourceWidgetPackages,\n resolvePortalWidgetSourceConfig,\n} from \"./widget-package-config.js\";\nimport {\n buildWidgetPackagePublishManifest,\n validateSingleSourceWidgetPackage,\n type ValidatedWidgetPackageBuild,\n type WidgetPackageOwnerKind,\n} from \"./widget-package-validation.js\";\nimport {\n collectWidgetPackageArtifacts,\n createPublishManifestMetadata,\n isWidgetPackageCssArtifactPath,\n} from \"./widget-package-artifacts.js\";\n\nconst DEFAULT_PUBLISH_DIR = \".fluid/widget-dist\";\nconst DEFAULT_RUNTIME_VERSION = \"1\";\nconst TEMP_BUILD_DIR = \".fluid/widget-build\";\nconst TEMP_CLI_DIR = \".fluid/tmp\";\nconst PROJECT_VITE_CONFIG_CANDIDATES = [\n \"vite.config.ts\",\n \"vite.config.mts\",\n \"vite.config.js\",\n \"vite.config.mjs\",\n] as const;\nconst BLOCKED_FLUID_PROJECT_PLUGIN_NAMES = [\n \"fluid-portal-dev\",\n \"fluid-backend-dev\",\n] as const;\nconst require = createRequire(import.meta.url);\n\nexport interface BuildSharedWidgetPackageOptions {\n readonly projectDir: string;\n readonly publishDir?: string;\n readonly owner?: WidgetPackageOwnerKind;\n}\n\nexport interface SharedWidgetPackageBuildResult {\n readonly publishDir: string;\n readonly packageId: string;\n readonly version: string;\n readonly manifestPath: string;\n readonly publishManifestPath: string;\n readonly widgetScriptPath: string;\n}\n\nexport interface SharedWidgetPackageBuildError {\n readonly code:\n | \"CONFIG_LOAD_FAILED\"\n | \"VALIDATION_FAILED\"\n | \"INVALID_PUBLISH_DIR\"\n | \"WRITE_FAILED\";\n readonly message: string;\n readonly details?: string;\n}\n\nexport interface ValidateSharedWidgetPackagePublishDirOptions {\n readonly optionName?: string;\n}\n\nexport function validateSharedWidgetPackagePublishDir(\n projectDir: string,\n publishDir: string = DEFAULT_PUBLISH_DIR,\n options: ValidateSharedWidgetPackagePublishDirOptions = {},\n): void {\n const label = options.optionName ?? \"publishDir\";\n\n if (path.isAbsolute(publishDir)) {\n throw new Error(`${label} must be relative to the project directory.`);\n }\n\n const normalizedPublishDir = normalizePublishDirForValidation(publishDir);\n if (\n normalizedPublishDir === \".fluid\" ||\n !normalizedPublishDir.startsWith(\".fluid/\")\n ) {\n throw new Error(`${label} must be a relative path under .fluid/.`);\n }\n\n if (isPathWithinReservedDir(normalizedPublishDir, TEMP_BUILD_DIR)) {\n throw new Error(\n `${label} must not be ${TEMP_BUILD_DIR} because it is reserved for temporary builder output.`,\n );\n }\n\n if (isPathWithinReservedDir(normalizedPublishDir, TEMP_CLI_DIR)) {\n throw new Error(\n `${label} must not be ${TEMP_CLI_DIR} because it is reserved for Fluid CLI temporary files.`,\n );\n }\n\n const resolvedProjectDir = path.resolve(projectDir);\n const resolvedPublishDir = path.resolve(resolvedProjectDir, publishDir);\n const relative = path.relative(resolvedProjectDir, resolvedPublishDir);\n\n if (\n relative === \"\" ||\n relative.startsWith(\"..\") ||\n path.isAbsolute(relative)\n ) {\n throw new Error(`${label} must stay inside the project directory.`);\n }\n}\n\nexport async function buildSharedWidgetPackage(\n options: BuildSharedWidgetPackageOptions,\n): Promise<\n Result<SharedWidgetPackageBuildResult, SharedWidgetPackageBuildError>\n> {\n const publishDirInput = options.publishDir ?? DEFAULT_PUBLISH_DIR;\n try {\n await validateWidgetPackageOutputPaths(options.projectDir, publishDirInput);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n return failure({\n code: \"INVALID_PUBLISH_DIR\",\n message: \"Unsafe widget package publish directory\",\n details: error.message,\n });\n }\n\n const sourcePackageResult = await loadSourceWidgetPackages(\n options.projectDir,\n );\n if (!sourcePackageResult.success) {\n return failure({\n code: \"CONFIG_LOAD_FAILED\",\n message: sourcePackageResult.error.message,\n details: sourcePackageResult.error.details,\n });\n }\n\n const validation = validateSingleSourceWidgetPackage(\n sourcePackageResult.value,\n { owner: options.owner },\n );\n if (!validation.success || !validation.value) {\n return failure({\n code: \"VALIDATION_FAILED\",\n message: validation.errors.map((error) => error.message).join(\"\\n\"),\n });\n }\n\n try {\n await validateWidgetPackageOutputPaths(options.projectDir, publishDirInput);\n\n const publishDir = path.resolve(options.projectDir, publishDirInput);\n const widgetScriptPath = path.join(publishDir, \"widget.js\");\n await fs.emptyDir(publishDir);\n\n const tempDir = path.resolve(options.projectDir, TEMP_BUILD_DIR);\n await fs.emptyDir(tempDir);\n let buildFailure: { readonly error: unknown } | undefined;\n try {\n await buildWidgetScriptBundle({\n projectDir: options.projectDir,\n tempDir,\n publishDir,\n validated: validation.value,\n });\n } catch (error) {\n buildFailure = { error };\n }\n\n try {\n await fs.remove(tempDir);\n } catch {\n const cleanupFailure = new Error(\n \"Failed to remove reserved temporary build directory.\",\n );\n if (buildFailure) {\n const primaryError =\n buildFailure.error instanceof Error\n ? buildFailure.error\n : new Error(String(buildFailure.error));\n throw new AggregateError(\n [primaryError, cleanupFailure],\n `${primaryError.message}\\n${cleanupFailure.message}`,\n );\n }\n throw cleanupFailure;\n }\n\n if (buildFailure) {\n throw buildFailure.error;\n }\n\n const cssUrls = await collectCssUrls(publishDir);\n const descriptor = buildWidgetPackagePublishManifest(validation.value, {\n cssUrls,\n });\n const manifestContent = stableStringify(descriptor);\n const publishPackageId = descriptor.packageId;\n\n const manifestPath = path.join(publishDir, \"manifest.json\");\n const publishManifestPath = path.join(publishDir, \"publish-manifest.json\");\n\n await fs.writeFile(manifestPath, manifestContent, \"utf-8\");\n\n const artifacts = await collectWidgetPackageArtifacts(publishDir);\n const publishManifest = createPublishManifestMetadata({\n packageId: publishPackageId,\n version: validation.value.version,\n cliVersion: readCliVersion(),\n runtimeVersion: DEFAULT_RUNTIME_VERSION,\n manifestContent,\n artifacts,\n });\n await fs.writeFile(\n publishManifestPath,\n stableStringify(publishManifest),\n \"utf-8\",\n );\n\n return success({\n publishDir,\n packageId: validation.value.packageId,\n version: validation.value.version,\n manifestPath,\n publishManifestPath,\n widgetScriptPath,\n });\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n const isPublishDirValidationError =\n /^(publishDir|reserved temporary build directory) /.test(error.message);\n return failure({\n code: isPublishDirValidationError\n ? \"INVALID_PUBLISH_DIR\"\n : \"WRITE_FAILED\",\n message: isPublishDirValidationError\n ? \"Unsafe widget package publish directory\"\n : \"Failed to build shared widget package artifacts\",\n details: error.message,\n });\n }\n}\n\nexport function createWidgetPackageEntrySource(options: {\n readonly configImportPath: string;\n readonly cssImportPath?: string;\n readonly validated: ValidatedWidgetPackageBuild;\n}): string {\n return `import { createElement } from \"react\";\nimport { startWidgetPackage } from \"@fluid-app/portal-sdk/widgets/worker\";\n${options.cssImportPath ? `import ${JSON.stringify(options.cssImportPath)};` : \"\"}\n\nconst widgetConfig = await import(${JSON.stringify(options.configImportPath)});\nconst SOURCE_PACKAGE_MARKER = \"__fluidSourceWidgetPackage\";\nconst descriptors = ${JSON.stringify(options.validated.widgets, null, 2)};\n\nfunction isSourceWidgetPackage(value) {\n return Boolean(value && typeof value === \"object\" && value[SOURCE_PACKAGE_MARKER] === true);\n}\n\nfunction collectSourceWidgetPackages(mod) {\n return [\n mod.widgetPackage,\n ...(Array.isArray(mod.widgetPackages) ? mod.widgetPackages : []),\n mod.default,\n ].filter(isSourceWidgetPackage);\n}\n\nconst sourcePackage = collectSourceWidgetPackages(widgetConfig)[0];\nif (!sourcePackage) {\n throw new Error(\"No Fluid source widget package found while loading widget package worker bundle.\");\n}\n\nconst componentsByName = new Map(\n sourcePackage.widgets.map((widget) => [widget.name, widget.component]),\n);\n\nconst widgets = descriptors.map((descriptor) => {\n const component = componentsByName.get(descriptor.name);\n if (typeof component !== \"function\") {\n throw new Error(\"Widget package is missing Remote DOM component for \" + descriptor.name + \".\");\n }\n return {\n ...descriptor,\n component: (props) => createElement(component, props),\n };\n});\n\nstartWidgetPackage({ widgets });\n`;\n}\n\nasync function buildWidgetScriptBundle(options: {\n readonly projectDir: string;\n readonly tempDir: string;\n readonly publishDir: string;\n readonly validated: ValidatedWidgetPackageBuild;\n}): Promise<void> {\n const sourceConfig = await resolvePortalWidgetSourceConfig(\n options.projectDir,\n );\n if (!sourceConfig) {\n throw new Error(\"No portal widget source config found.\");\n }\n\n const entryPath = path.join(options.tempDir, \"widget-entry.ts\");\n const viteConfigPath = path.join(options.tempDir, \"vite.config.mjs\");\n const rootStylesPath = path.join(options.projectDir, \"styles.css\");\n await fs.writeFile(\n entryPath,\n createWidgetPackageEntrySource({\n configImportPath: pathToFileURL(sourceConfig.path).href,\n cssImportPath: (await fs.pathExists(rootStylesPath))\n ? pathToFileURL(rootStylesPath).href\n : undefined,\n validated: options.validated,\n }),\n \"utf-8\",\n );\n await fs.writeFile(\n viteConfigPath,\n createViteConfigSource({\n entryPath,\n publishDir: options.publishDir,\n projectConfigPath: await resolveProjectViteConfigPath(options.projectDir),\n }),\n \"utf-8\",\n );\n\n const viteCliPath = resolveViteCliPath(options.projectDir);\n await execa(\n process.execPath,\n [viteCliPath, \"build\", \"--config\", viteConfigPath],\n {\n cwd: options.projectDir,\n stdio: \"pipe\",\n env: {\n ...process.env,\n NODE_ENV: \"production\",\n },\n },\n );\n}\n\nexport function resolveViteCliPath(projectDir: string): string {\n const projectRequire = createRequire(path.join(projectDir, \"package.json\"));\n const projectViteCliPath = resolveViteCliPathFromRequire(projectRequire);\n if (projectViteCliPath) return projectViteCliPath;\n\n const cliViteCliPath = resolveViteCliPathFromRequire(require);\n if (cliViteCliPath) return cliViteCliPath;\n\n throw new Error(\n \"Unable to resolve Vite for the portal project. Install the project dependencies and ensure vite is available before building widget packages.\",\n );\n}\n\nexport async function resolveProjectViteConfigPath(\n projectDir: string,\n): Promise<string | undefined> {\n for (const fileName of PROJECT_VITE_CONFIG_CANDIDATES) {\n const candidate = path.join(projectDir, fileName);\n if (await fs.pathExists(candidate)) return candidate;\n }\n\n return undefined;\n}\n\nexport function createViteConfigSource(options: {\n readonly entryPath: string;\n readonly publishDir: string;\n readonly projectConfigPath?: string;\n}): string {\n const widgetConfigSource = createWidgetViteConfigObjectSource({\n ...options,\n disableConfigFileLookup: !options.projectConfigPath,\n });\n\n if (!options.projectConfigPath) {\n return `import { defineConfig } from \"vite\";\n\nexport default defineConfig(${widgetConfigSource});\n`;\n }\n\n return `import { defineConfig, loadConfigFromFile, mergeConfig } from \"vite\";\n\nconst projectConfigPath = ${JSON.stringify(options.projectConfigPath)};\nconst widgetConfig = ${widgetConfigSource};\nconst fluidWidgetDefineInvariants = {\n \"process.env.NODE_ENV\": JSON.stringify(\"production\"),\n};\nconst blockedFluidPluginNames = new Set(${JSON.stringify(BLOCKED_FLUID_PROJECT_PLUGIN_NAMES)});\n\nfunction isAllowedProjectPlugin(plugin) {\n return !plugin || typeof plugin !== \"object\" || !blockedFluidPluginNames.has(plugin.name);\n}\n\nfunction sanitizeProjectPlugins(pluginOption) {\n if (!Array.isArray(pluginOption)) {\n return isAllowedProjectPlugin(pluginOption) ? pluginOption : [];\n }\n\n return pluginOption\n .map((plugin) => Array.isArray(plugin) ? sanitizeProjectPlugins(plugin) : plugin)\n .filter(isAllowedProjectPlugin);\n}\n\nfunction copyProjectConfigValue(source, target, key) {\n if (Object.prototype.hasOwnProperty.call(source, key)) {\n target[key] = source[key];\n }\n}\n\nfunction sanitizeProjectConfig(config) {\n if (!config || typeof config !== \"object\") return {};\n\n const sanitized = {};\n if (Object.prototype.hasOwnProperty.call(config, \"plugins\")) {\n sanitized.plugins = sanitizeProjectPlugins(config.plugins);\n }\n copyProjectConfigValue(config, sanitized, \"resolve\");\n copyProjectConfigValue(config, sanitized, \"define\");\n copyProjectConfigValue(config, sanitized, \"css\");\n copyProjectConfigValue(config, sanitized, \"esbuild\");\n copyProjectConfigValue(config, sanitized, \"json\");\n copyProjectConfigValue(config, sanitized, \"assetsInclude\");\n copyProjectConfigValue(config, sanitized, \"envDir\");\n copyProjectConfigValue(config, sanitized, \"envPrefix\");\n return sanitized;\n}\n\nfunction copyConfigDefineValue(value) {\n return value && typeof value === \"object\" && !Array.isArray(value)\n ? { ...value }\n : {};\n}\n\nfunction mergeFluidWidgetDefineInvariants(mergedConfig, safeProjectConfig, widgetConfig) {\n mergedConfig.define = {\n ...copyConfigDefineValue(safeProjectConfig.define),\n ...copyConfigDefineValue(mergedConfig.define),\n ...copyConfigDefineValue(widgetConfig.define),\n ...fluidWidgetDefineInvariants,\n };\n return mergedConfig;\n}\n\nexport default defineConfig(async (configEnv) => {\n const projectConfig = await loadConfigFromFile(configEnv, projectConfigPath);\n const safeProjectConfig = sanitizeProjectConfig(projectConfig?.config ?? {});\n return mergeFluidWidgetDefineInvariants(mergeConfig(safeProjectConfig, widgetConfig), safeProjectConfig, widgetConfig);\n});\n`;\n}\n\nfunction createWidgetViteConfigObjectSource(options: {\n readonly entryPath: string;\n readonly publishDir: string;\n readonly disableConfigFileLookup: boolean;\n}): string {\n const configFileSource = options.disableConfigFileLookup\n ? \" configFile: false,\\n\"\n : \"\";\n return `(() => {\n const fluidWidgetDefineInvariants = {\n \"process.env.NODE_ENV\": JSON.stringify(\"production\"),\n };\n\n function createFluidWidgetBuildInvariants() {\n return {\n copyPublicDir: false,\n emptyOutDir: false,\n outDir: ${JSON.stringify(options.publishDir)},\n sourcemap: false,\n target: \"esnext\",\n cssCodeSplit: false,\n lib: {\n entry: ${JSON.stringify(options.entryPath)},\n formats: [\"es\"],\n fileName: () => \"widget.js\",\n cssFileName: \"widget\",\n },\n rollupOptions: {\n output: {\n inlineDynamicImports: true,\n hoistTransitiveImports: false,\n },\n },\n };\n }\n\n function fluidWidgetBuildInvariantsPlugin() {\n return {\n name: \"fluid-widget-build-invariants\",\n enforce: \"post\",\n config() {\n return { build: createFluidWidgetBuildInvariants() };\n },\n configResolved(config) {\n config.define = {\n ...(config.define ?? {}),\n ...fluidWidgetDefineInvariants,\n };\n Object.assign(config.build, createFluidWidgetBuildInvariants());\n },\n };\n }\n\n return {\n${configFileSource} publicDir: false,\n define: fluidWidgetDefineInvariants,\n resolve: {\n conditions: [\n \"module\",\n \"browser\",\n \"development|production\",\n ],\n },\n plugins: [fluidWidgetBuildInvariantsPlugin()],\n build: createFluidWidgetBuildInvariants(),\n };\n})()`;\n}\n\nasync function validateWidgetPackageOutputPaths(\n projectDir: string,\n publishDir: string,\n): Promise<void> {\n validateSharedWidgetPackagePublishDir(projectDir, publishDir);\n await rejectSymlinkedPathSegments({\n projectDir,\n relativePath: publishDir,\n label: \"publishDir\",\n });\n await rejectSymlinkedPathSegments({\n projectDir,\n relativePath: TEMP_BUILD_DIR,\n label: \"reserved temporary build directory\",\n });\n}\n\nasync function collectCssUrls(publishDir: string): Promise<string[]> {\n const entries = await fs.readdir(publishDir, { withFileTypes: true });\n return entries\n .filter(\n (entry) => entry.isFile() && isWidgetPackageCssArtifactPath(entry.name),\n )\n .map((entry) => entry.name)\n .sort();\n}\n\nasync function rejectSymlinkedPathSegments(options: {\n readonly projectDir: string;\n readonly relativePath: string;\n readonly label: string;\n}): Promise<void> {\n const resolvedProjectDir = path.resolve(options.projectDir);\n const resolvedTargetPath = path.resolve(\n resolvedProjectDir,\n options.relativePath,\n );\n const relative = path.relative(resolvedProjectDir, resolvedTargetPath);\n const segments = relative.split(path.sep).filter(Boolean);\n let currentPath = resolvedProjectDir;\n\n for (const segment of segments) {\n currentPath = path.join(currentPath, segment);\n let stat: fs.Stats;\n try {\n stat = await fs.lstat(currentPath);\n } catch (err) {\n if (isNodeError(err) && err.code === \"ENOENT\") return;\n throw err;\n }\n\n if (stat.isSymbolicLink()) {\n throw new Error(`${options.label} must not include symbolic links.`);\n }\n }\n}\n\nfunction normalizePublishDirForValidation(value: string): string {\n return toPosixPath(path.normalize(value)).replace(/\\/+$/, \"\") || \".\";\n}\n\nfunction isPathWithinReservedDir(value: string, reservedDir: string): boolean {\n return value === reservedDir || value.startsWith(`${reservedDir}/`);\n}\n\nfunction toPosixPath(value: string): string {\n return value.split(path.sep).join(\"/\");\n}\n\nfunction stableStringify(value: unknown): string {\n return `${JSON.stringify(value, null, 2)}\\n`;\n}\n\nfunction readCliVersion(): string {\n return resolveCliVersionFromPackageMetadata(\n path.dirname(fileURLToPath(import.meta.url)),\n require,\n );\n}\n\nexport function resolveCliVersionFromPackageMetadata(\n startDir: string,\n loadPackageMetadata: (packageJsonPath: string) => unknown,\n): string {\n for (const packageJsonPath of candidatePackageJsonPaths(startDir)) {\n let packageJson: unknown;\n try {\n packageJson = loadPackageMetadata(packageJsonPath);\n } catch (error) {\n // Missing candidates are expected during the upward walk; every other read\n // failure must stop the build so its provenance cannot be fabricated.\n if (isModuleResolutionError(error)) continue;\n throw new Error(\"Failed to read Fluid CLI package metadata.\", {\n cause: error,\n });\n }\n\n if (\n !isRecord(packageJson) ||\n packageJson.name !== \"@fluid-app/fluid-cli-portal\"\n ) {\n continue;\n }\n if (\n typeof packageJson.version !== \"string\" ||\n packageJson.version.length === 0\n ) {\n throw new Error(\n \"Fluid CLI package metadata does not declare a valid version.\",\n );\n }\n return packageJson.version;\n }\n\n throw new Error(\"Unable to find Fluid CLI package metadata.\");\n}\n\nfunction resolveViteCliPathFromRequire(\n moduleRequire: NodeJS.Require,\n): string | undefined {\n try {\n const packageJsonPath = moduleRequire.resolve(\"vite/package.json\");\n const packageJson = moduleRequire(packageJsonPath) as { bin?: unknown };\n const binPath = getViteBinPath(packageJson.bin);\n if (!binPath) return undefined;\n\n return path.resolve(path.dirname(packageJsonPath), binPath);\n } catch (err) {\n if (isModuleResolutionError(err)) return undefined;\n throw err;\n }\n}\n\nfunction getViteBinPath(bin: unknown): string | undefined {\n if (typeof bin === \"string\") return bin;\n if (!isRecord(bin)) return undefined;\n\n const viteBin = bin.vite;\n return typeof viteBin === \"string\" ? viteBin : undefined;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isNodeError(value: unknown): value is NodeJS.ErrnoException {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"code\" in value &&\n typeof value.code === \"string\"\n );\n}\n\nfunction isModuleResolutionError(value: unknown): boolean {\n return (\n isNodeError(value) &&\n (value.code === \"MODULE_NOT_FOUND\" || value.code === \"ERR_MODULE_NOT_FOUND\")\n );\n}\n\nfunction candidatePackageJsonPaths(startDir: string): string[] {\n const paths: string[] = [];\n let current = startDir;\n\n while (true) {\n paths.push(path.join(current, \"package.json\"));\n const parent = path.dirname(current);\n if (parent === current) return paths;\n current = parent;\n }\n}\n","/**\n * Shared widget package publish/upload helpers.\n *\n * These utilities cover the two owner scopes currently supported by the\n * portal widget package upload API: marketplace droplets and company-owned\n * Fluid OS packages. They intentionally do not register CLI commands; command\n * implementations can compose the typed payload builders, session/complete\n * requests, signed URL uploads, and dry-run orchestration exposed here.\n */\n\nimport type { FetchClient } from \"@fluid-app/fluid-cli\";\nimport { fluidOs } from \"@fluid-app/fluidos-api-client\";\nimport type { ArtifactMetadata } from \"./widget-package-artifacts.js\";\n\nexport type WidgetPackageUploadOwner =\n | { readonly kind: \"droplet\"; readonly uuid: string }\n | { readonly kind: \"company\" };\n\nexport type WidgetPackageArtifactMetadata = ArtifactMetadata;\n\nexport interface WidgetPackageUploadArtifact extends WidgetPackageArtifactMetadata {\n readonly body: BodyInit;\n}\n\nexport interface WidgetPackageSessionRequest {\n readonly version: string;\n readonly packageKey: string;\n readonly builderVersion: string;\n readonly cliVersion: string;\n readonly runtimeVersion: string;\n readonly manifestHash: string;\n readonly manifest: Record<string, unknown>;\n readonly artifacts: readonly WidgetPackageArtifactMetadata[];\n}\n\nexport interface WidgetPackagePublishRequest extends Omit<\n WidgetPackageSessionRequest,\n \"artifacts\"\n> {\n readonly artifacts: readonly WidgetPackageUploadArtifact[];\n}\n\nexport type WidgetPackageArtifactPayload = ArtifactMetadata;\n\nexport interface WidgetPackageUploadSessionPayload {\n readonly version: string;\n readonly package_key: string;\n readonly builder_version: string;\n readonly cli_version: string;\n readonly runtime_version: string;\n readonly manifest_hash: string;\n readonly manifest: Record<string, unknown>;\n readonly artifacts: readonly WidgetPackageArtifactPayload[];\n}\n\nexport interface WidgetPackageCompleteUploadPayload {\n readonly package_key: string;\n readonly artifacts: readonly WidgetPackageArtifactPayload[];\n}\n\nexport interface WidgetPackageSignedUpload {\n readonly path: string;\n readonly url: string;\n readonly contentType: string;\n readonly headers: Readonly<Record<string, string>>;\n}\n\nexport interface WidgetPackageUploadSession {\n readonly version: string;\n readonly uploads: readonly WidgetPackageSignedUpload[];\n readonly raw: unknown;\n}\n\nexport interface WidgetPackageArtifactUploadResult {\n readonly path: string;\n readonly status: number;\n readonly contentType: string;\n}\n\nexport interface WidgetPackageCompleteResult {\n readonly version: string;\n readonly status?: string;\n readonly packageKey?: string;\n readonly raw: unknown;\n}\n\nexport interface WidgetPackagePublishResult {\n readonly dryRun: boolean;\n readonly sessionPayload: WidgetPackageUploadSessionPayload;\n readonly session?: WidgetPackageUploadSession;\n readonly uploadedArtifacts?: readonly WidgetPackageArtifactUploadResult[];\n readonly complete?: WidgetPackageCompleteResult;\n}\n\ninterface BasePublishWidgetPackageVersionOptions {\n readonly owner: WidgetPackageUploadOwner;\n readonly request: WidgetPackagePublishRequest;\n readonly fetchImpl?: WidgetPackageUploadFetch;\n}\n\nexport interface DryRunPublishWidgetPackageVersionOptions extends BasePublishWidgetPackageVersionOptions {\n readonly client?: FetchClient;\n readonly dryRun: true;\n}\n\nexport interface PublishWidgetPackageVersionUploadOptions extends BasePublishWidgetPackageVersionOptions {\n readonly client: FetchClient;\n readonly dryRun?: boolean;\n}\n\nexport type PublishWidgetPackageVersionOptions =\n | DryRunPublishWidgetPackageVersionOptions\n | PublishWidgetPackageVersionUploadOptions;\n\ntype FluidOsCreateWidgetPackageVersionPayload = Parameters<\n typeof fluidOs.fluid_os_v0_create_widget_package_version\n>[1];\n\ntype FluidOsCompleteWidgetPackageVersionUploadPayload = NonNullable<\n Parameters<\n typeof fluidOs.fluid_os_v0_complete_widget_package_version_upload\n >[2]\n>;\n\nexport type WidgetPackageUploadFetch = (\n input: string | URL,\n init: RequestInit,\n) => Promise<Response>;\n\nexport class WidgetPackageUploadError extends Error {\n public readonly code: string;\n\n constructor(code: string, message: string) {\n super(message);\n this.name = \"WidgetPackageUploadError\";\n this.code = code;\n }\n}\n\nexport function buildWidgetPackageArtifactPayload(\n artifact: WidgetPackageArtifactMetadata,\n): WidgetPackageArtifactPayload {\n return {\n path: artifact.path,\n sha256: artifact.sha256,\n bytes: artifact.bytes,\n contentType: artifact.contentType,\n };\n}\n\nexport function buildWidgetPackageUploadSessionPayload(\n request: WidgetPackageSessionRequest,\n): WidgetPackageUploadSessionPayload {\n return {\n version: request.version,\n package_key: request.packageKey,\n builder_version: request.builderVersion,\n cli_version: request.cliVersion,\n runtime_version: request.runtimeVersion,\n manifest_hash: request.manifestHash,\n manifest: request.manifest,\n artifacts: request.artifacts.map(buildWidgetPackageArtifactPayload),\n };\n}\n\nexport function buildWidgetPackageCompleteUploadPayload(\n packageKey: string,\n artifacts: readonly WidgetPackageArtifactMetadata[],\n): WidgetPackageCompleteUploadPayload {\n return {\n package_key: packageKey,\n artifacts: artifacts.map(buildWidgetPackageArtifactPayload),\n };\n}\n\nexport function getWidgetPackageUploadSessionRoute(\n owner: WidgetPackageUploadOwner,\n): string {\n switch (owner.kind) {\n case \"droplet\":\n return `/api/droplets/${encodeURIComponent(owner.uuid)}/widget_package_versions`;\n case \"company\":\n return \"/api/company/fluid_os/widget_package_versions\";\n }\n}\n\nexport function getWidgetPackageCompleteUploadRoute(\n owner: WidgetPackageUploadOwner,\n version: string,\n): string {\n switch (owner.kind) {\n case \"droplet\":\n return `/api/droplets/${encodeURIComponent(owner.uuid)}/widget_package_versions/${encodeURIComponent(version)}/complete_upload`;\n case \"company\":\n return `/api/company/fluid_os/widget_package_versions/${encodeURIComponent(version)}/complete_upload`;\n }\n}\n\nexport async function requestWidgetPackageUploadSession(\n client: FetchClient,\n owner: WidgetPackageUploadOwner,\n request: WidgetPackageSessionRequest,\n): Promise<WidgetPackageUploadSession> {\n const payload = buildWidgetPackageUploadSessionPayload(request);\n\n try {\n const response =\n owner.kind === \"company\"\n ? await fluidOs.fluid_os_v0_create_widget_package_version(\n client,\n payload as FluidOsCreateWidgetPackageVersionPayload,\n )\n : await client.post<unknown>(\n getWidgetPackageUploadSessionRoute(owner),\n payload,\n );\n return normalizeWidgetPackageUploadSession(\n response,\n request.version,\n request.artifacts,\n );\n } catch (err) {\n if (err instanceof WidgetPackageUploadError) {\n throw err;\n }\n\n throw new WidgetPackageUploadError(\n \"SESSION_REQUEST_FAILED\",\n `Failed to create widget package upload session: ${formatUnknownError(err)}`,\n );\n }\n}\n\nexport async function uploadWidgetPackageArtifacts(\n session: WidgetPackageUploadSession,\n artifacts: readonly WidgetPackageUploadArtifact[],\n fetchImpl: WidgetPackageUploadFetch = fetch,\n): Promise<WidgetPackageArtifactUploadResult[]> {\n const uploadsByPath = new Map(\n session.uploads.map((upload) => [upload.path, upload] as const),\n );\n const results: WidgetPackageArtifactUploadResult[] = [];\n\n for (const artifact of artifacts) {\n const upload = uploadsByPath.get(artifact.path);\n if (!upload) {\n throw new WidgetPackageUploadError(\n \"MISSING_SIGNED_URL\",\n `Upload session did not include a signed URL for artifact \"${artifact.path}\".`,\n );\n }\n\n if (upload.contentType !== artifact.contentType) {\n throw new WidgetPackageUploadError(\n \"CONTENT_TYPE_MISMATCH\",\n `Upload session expected artifact \"${artifact.path}\" to use content type \"${upload.contentType}\", but local artifact uses \"${artifact.contentType}\".`,\n );\n }\n\n let response: Response;\n try {\n response = await fetchImpl(upload.url, {\n method: \"PUT\",\n headers: mergeUploadHeaders(upload.headers, upload.contentType),\n body: artifact.body,\n });\n } catch (err) {\n throw new WidgetPackageUploadError(\n \"ARTIFACT_UPLOAD_FAILED\",\n `Failed to upload artifact \"${artifact.path}\": ${formatUnknownError(err)}`,\n );\n }\n\n if (!response.ok) {\n const body = await response.text().catch(() => \"\");\n const details = body.trim() ? `: ${body.trim().slice(0, 500)}` : \"\";\n throw new WidgetPackageUploadError(\n \"ARTIFACT_UPLOAD_FAILED\",\n `Failed to upload artifact \"${artifact.path}\": PUT returned HTTP ${response.status}${details}`,\n );\n }\n\n results.push({\n path: artifact.path,\n status: response.status,\n contentType: upload.contentType,\n });\n }\n\n return results;\n}\n\nexport async function completeWidgetPackageUpload(\n client: FetchClient,\n owner: WidgetPackageUploadOwner,\n version: string,\n packageKey: string,\n artifacts: readonly WidgetPackageArtifactMetadata[],\n): Promise<WidgetPackageCompleteResult> {\n const payload = buildWidgetPackageCompleteUploadPayload(\n packageKey,\n artifacts,\n );\n\n try {\n const response =\n owner.kind === \"company\"\n ? await fluidOs.fluid_os_v0_complete_widget_package_version_upload(\n client,\n version,\n payload as FluidOsCompleteWidgetPackageVersionUploadPayload,\n )\n : await client.post<unknown>(\n getWidgetPackageCompleteUploadRoute(owner, version),\n payload,\n );\n return normalizeWidgetPackageCompleteResponse(response, version);\n } catch (err) {\n if (err instanceof WidgetPackageUploadError) {\n throw err;\n }\n\n throw new WidgetPackageUploadError(\n \"COMPLETE_UPLOAD_FAILED\",\n `Failed to complete widget package upload: ${formatUnknownError(err)}`,\n );\n }\n}\n\nexport async function publishWidgetPackageVersion(\n options: PublishWidgetPackageVersionOptions,\n): Promise<WidgetPackagePublishResult> {\n const sessionPayload = buildWidgetPackageUploadSessionPayload(\n options.request,\n );\n\n if (options.dryRun) {\n return { dryRun: true, sessionPayload };\n }\n\n if (!options.client) {\n throw new Error(\"A FetchClient is required when dryRun is false.\");\n }\n\n const session = await requestWidgetPackageUploadSession(\n options.client,\n options.owner,\n options.request,\n );\n const uploadedArtifacts = await uploadWidgetPackageArtifacts(\n session,\n options.request.artifacts,\n options.fetchImpl,\n );\n const complete = await completeWidgetPackageUpload(\n options.client,\n options.owner,\n session.version,\n options.request.packageKey,\n options.request.artifacts,\n );\n\n return {\n dryRun: false,\n sessionPayload,\n session,\n uploadedArtifacts,\n complete,\n };\n}\n\nexport function normalizeWidgetPackageUploadSession(\n response: unknown,\n requestedVersion: string,\n requestedArtifacts: readonly WidgetPackageArtifactMetadata[],\n): WidgetPackageUploadSession {\n const record = requireRecord(\n response,\n \"INVALID_SESSION_RESPONSE\",\n \"Upload session response must be a JSON object.\",\n );\n requireFiniteNumberProperty(record, \"version_id\", \"INVALID_SESSION_RESPONSE\");\n requireStringProperty(\n record,\n \"artifact_base_url\",\n \"INVALID_SESSION_RESPONSE\",\n );\n\n const uploadValues = record[\"uploads\"];\n if (!Array.isArray(uploadValues)) {\n throw new WidgetPackageUploadError(\n \"INVALID_SESSION_RESPONSE\",\n 'Upload session response must contain an \"uploads\" array.',\n );\n }\n\n const requestedArtifactsByPath = new Map(\n requestedArtifacts.map((artifact) => [artifact.path, artifact] as const),\n );\n const seenUploadPaths = new Set<string>();\n const uploads = uploadValues.map((value, index) => {\n const upload = requireRecord(\n value,\n \"INVALID_SESSION_RESPONSE\",\n `Upload session uploads[${index}] must be a JSON object.`,\n );\n const path = requireStringProperty(\n upload,\n \"path\",\n \"INVALID_SESSION_RESPONSE\",\n );\n const url = requireStringProperty(\n upload,\n \"upload_url\",\n \"INVALID_SESSION_RESPONSE\",\n );\n requireStringProperty(upload, \"public_url\", \"INVALID_SESSION_RESPONSE\");\n\n const requestedArtifact = requestedArtifactsByPath.get(path);\n if (!requestedArtifact) {\n throw new WidgetPackageUploadError(\n \"INVALID_SESSION_RESPONSE\",\n `Upload session response included unexpected artifact \"${path}\".`,\n );\n }\n if (seenUploadPaths.has(path)) {\n throw new WidgetPackageUploadError(\n \"INVALID_SESSION_RESPONSE\",\n `Upload session response included duplicate artifact \"${path}\".`,\n );\n }\n seenUploadPaths.add(path);\n\n return {\n path,\n url,\n contentType: requestedArtifact.contentType,\n headers: {},\n };\n });\n const missingUploadPaths = requestedArtifacts\n .map((artifact) => artifact.path)\n .filter((artifactPath) => !seenUploadPaths.has(artifactPath));\n\n if (missingUploadPaths.length > 0) {\n throw new WidgetPackageUploadError(\n \"INVALID_SESSION_RESPONSE\",\n `Upload session response is missing signed URLs for artifact(s): ${missingUploadPaths.join(\", \")}.`,\n );\n }\n\n return { version: requestedVersion, uploads, raw: response };\n}\n\nexport function normalizeWidgetPackageCompleteResponse(\n response: unknown,\n requestedVersion: string,\n): WidgetPackageCompleteResult {\n const record = requireRecord(\n response,\n \"INVALID_COMPLETE_RESPONSE\",\n \"Complete upload response must be a JSON object.\",\n );\n const responseVersion = requireStringProperty(\n record,\n \"version\",\n \"INVALID_COMPLETE_RESPONSE\",\n );\n\n if (responseVersion !== requestedVersion) {\n throw new WidgetPackageUploadError(\n \"INVALID_COMPLETE_RESPONSE\",\n `Complete upload response version \"${responseVersion}\" did not match requested version \"${requestedVersion}\".`,\n );\n }\n\n const packageKey = requireStringProperty(\n record,\n \"packageId\",\n \"INVALID_COMPLETE_RESPONSE\",\n );\n const status = requireStringProperty(\n record,\n \"status\",\n \"INVALID_COMPLETE_RESPONSE\",\n );\n requireStringProperty(record, \"scriptUrl\", \"INVALID_COMPLETE_RESPONSE\");\n requireStringProperty(record, \"manifestUrl\", \"INVALID_COMPLETE_RESPONSE\");\n requireStringArrayProperty(record, \"cssUrls\", \"INVALID_COMPLETE_RESPONSE\");\n\n return {\n version: responseVersion,\n status,\n packageKey,\n raw: response,\n };\n}\n\nfunction normalizeHeaderName(name: string): string {\n return name.toLowerCase();\n}\n\nfunction mergeUploadHeaders(\n headers: Readonly<Record<string, string>>,\n contentType: string,\n): Record<string, string> {\n const merged: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(headers)) {\n if (normalizeHeaderName(key) === \"content-type\") continue;\n merged[key] = value;\n }\n\n merged[\"Content-Type\"] = contentType;\n return merged;\n}\n\nfunction requireRecord(\n value: unknown,\n code: string,\n message: string,\n): Record<string, unknown> {\n if (!isRecord(value) || Array.isArray(value)) {\n throw new WidgetPackageUploadError(code, message);\n }\n return value;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction requireStringProperty(\n record: Record<string, unknown>,\n key: string,\n code: string,\n): string {\n const value = record[key];\n if (typeof value === \"string\" && value.trim().length > 0) {\n return value;\n }\n throw new WidgetPackageUploadError(\n code,\n `Response field \"${key}\" must be a non-empty string.`,\n );\n}\n\nfunction requireFiniteNumberProperty(\n record: Record<string, unknown>,\n key: string,\n code: string,\n): number {\n const value = record[key];\n if (typeof value === \"number\" && Number.isFinite(value)) {\n return value;\n }\n throw new WidgetPackageUploadError(\n code,\n `Response field \"${key}\" must be a finite number.`,\n );\n}\n\nfunction requireStringArrayProperty(\n record: Record<string, unknown>,\n key: string,\n code: string,\n): readonly string[] {\n const value = record[key];\n if (\n Array.isArray(value) &&\n value.every((entry) => typeof entry === \"string\" && entry.trim().length > 0)\n ) {\n return value;\n }\n throw new WidgetPackageUploadError(\n code,\n `Response field \"${key}\" must be an array of non-empty strings.`,\n );\n}\n\nfunction formatUnknownError(err: unknown): string {\n const base = err instanceof Error ? err.message : String(err);\n\n if (isRecord(err) && \"data\" in err) {\n return `${base} — ${safeStringify(err.data)}`;\n }\n\n return base;\n}\n\nfunction safeStringify(value: unknown): string {\n const seen = new WeakSet<object>();\n\n try {\n const serialized = JSON.stringify(value, (_key, item: unknown) => {\n if (typeof item === \"bigint\") return `${item}n`;\n if (typeof item !== \"object\" || item === null) return item;\n if (seen.has(item)) return \"[Circular]\";\n seen.add(item);\n return item;\n });\n return serialized ?? String(value);\n } catch {\n return \"[unserializable error data]\";\n }\n}\n","import { findProjectConfig, readConfig } from \"@fluid-app/fluid-cli\";\nimport type { FetchClient } from \"@fluid-app/fluid-cli\";\nimport { createFetchClient } from \"@fluid-app/fluidos-api-client\";\nimport chalk from \"chalk\";\nimport fs from \"fs-extra\";\nimport path from \"node:path\";\nimport {\n buildSharedWidgetPackage,\n validateSharedWidgetPackagePublishDir,\n} from \"../utils/widget-package-builder.js\";\nimport type {\n BuildSharedWidgetPackageOptions,\n SharedWidgetPackageBuildResult,\n} from \"../utils/widget-package-builder.js\";\nimport { publishWidgetPackageVersion } from \"../utils/widget-package-upload.js\";\nimport type {\n WidgetPackagePublishRequest,\n WidgetPackagePublishResult,\n WidgetPackageUploadOwner,\n} from \"../utils/widget-package-upload.js\";\nimport {\n collectWidgetPackageArtifacts,\n computeArtifactMetadata,\n sha256,\n type ArtifactMetadata,\n type PublishManifestMetadata,\n} from \"../utils/widget-package-artifacts.js\";\nimport type { WidgetPackageOwnerKind as WidgetPackageBuildOwner } from \"../utils/widget-package-validation.js\";\n\nexport interface PublishWidgetRuntimeArtifactsOptions {\n readonly projectDir: string;\n readonly outDir: string;\n readonly buildOwner: WidgetPackageBuildOwner;\n readonly uploadOwner: WidgetPackageUploadOwner;\n readonly dryRun?: boolean;\n readonly client?: FetchClient;\n}\n\nexport interface PublishWidgetRuntimeArtifactsResult {\n readonly build: SharedWidgetPackageBuildResult;\n readonly request: WidgetPackagePublishRequest;\n readonly publish: WidgetPackagePublishResult;\n}\n\nexport interface PublishWidgetRuntimeArtifactsDependencies {\n readonly buildSharedWidgetPackage?: typeof buildSharedWidgetPackage;\n readonly publishWidgetPackageVersion?: typeof publishWidgetPackageVersion;\n}\n\nexport async function publishWidgetRuntimeArtifacts(\n options: PublishWidgetRuntimeArtifactsOptions,\n dependencies: PublishWidgetRuntimeArtifactsDependencies = {},\n): Promise<PublishWidgetRuntimeArtifactsResult> {\n const buildPackage =\n dependencies.buildSharedWidgetPackage ?? buildSharedWidgetPackage;\n const publishVersion =\n dependencies.publishWidgetPackageVersion ?? publishWidgetPackageVersion;\n\n const isDryRun = options.dryRun === true;\n const client = options.client;\n\n if (!isDryRun && !client) {\n throw new Error(\"A FetchClient is required when dryRun is false.\");\n }\n\n const safeOutDir = validateWidgetPublishOutDir(\n options.projectDir,\n options.outDir,\n );\n const buildResult = await buildPackage({\n projectDir: options.projectDir,\n publishDir: safeOutDir,\n owner: options.buildOwner,\n } satisfies BuildSharedWidgetPackageOptions);\n\n if (!buildResult.success) {\n const details = buildResult.error.details\n ? `\\n${buildResult.error.details}`\n : \"\";\n throw new Error(`${buildResult.error.message}${details}`);\n }\n\n const request = await createWidgetPackagePublishRequest({\n publishDir: buildResult.value.publishDir,\n manifestPath: buildResult.value.manifestPath,\n publishManifestPath: buildResult.value.publishManifestPath,\n owner: options.buildOwner,\n });\n\n let publish: WidgetPackagePublishResult;\n if (isDryRun) {\n publish = await publishVersion({\n owner: options.uploadOwner,\n request,\n dryRun: true,\n });\n } else {\n if (!client) {\n throw new Error(\"A FetchClient is required when dryRun is false.\");\n }\n\n publish = await publishVersion({\n client,\n owner: options.uploadOwner,\n request,\n });\n }\n\n return { build: buildResult.value, request, publish };\n}\n\nexport async function createWidgetPackagePublishRequest(options: {\n readonly publishDir: string;\n readonly manifestPath: string;\n readonly publishManifestPath: string;\n readonly owner: WidgetPackageBuildOwner;\n}): Promise<WidgetPackagePublishRequest> {\n const manifestContent = await fs.readFile(options.manifestPath);\n const manifest = parseJsonRecord(\n manifestContent.toString(\"utf-8\"),\n options.manifestPath,\n );\n const publishManifest = await readPublishManifest(\n options.publishManifestPath,\n );\n assertManifestIdentityMatches(\n manifest,\n publishManifest,\n options.manifestPath,\n );\n assertMetadataMatches({\n label: \"manifestHash\",\n path: \"manifest.json\",\n expected: publishManifest.manifestHash,\n actual: sha256(manifestContent),\n });\n const collectedArtifacts = await collectWidgetPackageArtifacts(\n options.publishDir,\n );\n assertArtifactInventoryMatches(publishManifest.artifacts, collectedArtifacts);\n const artifacts = await readUploadArtifacts(\n options.publishDir,\n publishManifest.artifacts,\n );\n\n return {\n version: publishManifest.version,\n packageKey: stripOwnerPrefix(publishManifest.packageId, options.owner),\n builderVersion: publishManifest.builderVersion,\n cliVersion: publishManifest.cliVersion,\n runtimeVersion: publishManifest.runtimeVersion,\n manifestHash: publishManifest.manifestHash,\n manifest,\n artifacts,\n };\n}\n\nexport function createAuthenticatedWidgetPackageClient(): FetchClient {\n const { profile, profileName, token } = resolveAuthenticatedProfile();\n if (!token) {\n if (!profileName) {\n throw new Error(\n \"Not logged in. Run \" + chalk.cyan(\"fluid login\") + \" first.\",\n );\n }\n throw new Error(\n \"No auth token found for profile \" +\n chalk.cyan(profileName) +\n \". Run \" +\n chalk.cyan(\"fluid login\") +\n \" to re-authenticate.\",\n );\n }\n\n return createFetchClient({\n baseUrl:\n profile?.baseUrl ??\n process.env[\"FLUID_API_BASE\"] ??\n \"https://api.fluid.app\",\n getAuthToken: () => token,\n });\n}\n\ntype ProfileWithBaseUrl = ReturnType<typeof readConfig>[\"profiles\"][string] & {\n readonly baseUrl?: string;\n};\n\nfunction resolveAuthenticatedProfile(): {\n readonly profile?: ProfileWithBaseUrl;\n readonly profileName?: string;\n readonly token?: string;\n} {\n const config = readConfig();\n const projectConfig = findProjectConfig(process.cwd());\n\n if (projectConfig?.profile) {\n const profile = config.profiles[projectConfig.profile] as\n | ProfileWithBaseUrl\n | undefined;\n if (profile) {\n return {\n profile,\n profileName: projectConfig.profile,\n token: profile.token,\n };\n }\n }\n\n if (!config.activeProfile) return {};\n const profile = config.profiles[config.activeProfile] as\n | ProfileWithBaseUrl\n | undefined;\n if (!profile) return {};\n\n return {\n profile,\n profileName: config.activeProfile,\n token: profile.token,\n };\n}\n\nexport function printWidgetPublishSummary(options: {\n readonly title: string;\n readonly ownerLabel: string;\n readonly environment?: string;\n readonly outDir: string;\n readonly result: PublishWidgetRuntimeArtifactsResult;\n}): void {\n console.log();\n console.log(chalk.green.bold(`${options.title} complete`));\n console.log();\n console.log(chalk.gray(\"Owner: \") + chalk.white(options.ownerLabel));\n if (options.environment) {\n console.log(chalk.gray(\"Environment: \") + chalk.white(options.environment));\n }\n console.log(\n chalk.gray(\"Package: \") + chalk.white(options.result.build.packageId),\n );\n console.log(\n chalk.gray(\"Version: \") + chalk.white(options.result.build.version),\n );\n console.log(chalk.gray(\"Output: \") + chalk.cyan(options.outDir));\n console.log(\n chalk.gray(\"Artifacts: \") +\n chalk.white(String(options.result.request.artifacts.length)),\n );\n console.log();\n}\n\nexport function printRuntimeOnlyNotice(): void {\n console.log(\n chalk.yellow(\n \"This publishes widget runtime artifacts only; it does not deploy the hosted portal shell.\",\n ),\n );\n console.log();\n}\n\nexport function printDryRunSessionPayload(\n result: PublishWidgetRuntimeArtifactsResult,\n): void {\n console.log(chalk.bold(\"Dry-run upload session payload:\"));\n console.log(JSON.stringify(result.publish.sessionPayload, null, 2));\n console.log();\n}\n\nexport function validateWidgetPublishOutDir(\n projectDir: string,\n outDir: string,\n): string {\n validateSharedWidgetPackagePublishDir(projectDir, outDir, {\n optionName: \"--out-dir\",\n });\n\n return outDir;\n}\n\nasync function readJsonRecord(\n filePath: string,\n): Promise<Record<string, unknown>> {\n return parseJsonRecord(await fs.readFile(filePath, \"utf-8\"), filePath);\n}\n\nfunction parseJsonRecord(\n content: string,\n filePath: string,\n): Record<string, unknown> {\n const value = JSON.parse(content) as unknown;\n if (!isRecord(value) || Array.isArray(value)) {\n throw new Error(`${path.basename(filePath)} must contain a JSON object.`);\n }\n return value;\n}\n\nasync function readPublishManifest(\n filePath: string,\n): Promise<PublishManifestMetadata> {\n const record = await readJsonRecord(filePath);\n const artifactsValue = record[\"artifacts\"];\n if (!Array.isArray(artifactsValue)) {\n throw new Error(\"publish-manifest.json must include an artifacts array.\");\n }\n\n return {\n packageId: readRequiredString(record, \"packageId\", filePath),\n version: readRequiredString(record, \"version\", filePath),\n builderVersion: readRequiredString(record, \"builderVersion\", filePath),\n cliVersion: readRequiredString(record, \"cliVersion\", filePath),\n runtimeVersion: readRequiredString(record, \"runtimeVersion\", filePath),\n manifestHash: readRequiredString(record, \"manifestHash\", filePath),\n artifacts: artifactsValue.map((artifact, index) =>\n readArtifactMetadata(artifact, index),\n ),\n };\n}\n\nfunction readArtifactMetadata(value: unknown, index: number): ArtifactMetadata {\n if (!isRecord(value) || Array.isArray(value)) {\n throw new Error(\n `publish-manifest.json artifacts[${index}] must be a JSON object.`,\n );\n }\n\n return {\n path: readRequiredString(value, \"path\", \"publish-manifest.json\"),\n sha256: readRequiredString(value, \"sha256\", \"publish-manifest.json\"),\n bytes: readRequiredNumber(value, \"bytes\", \"publish-manifest.json\"),\n contentType: readRequiredString(\n value,\n \"contentType\",\n \"publish-manifest.json\",\n ),\n };\n}\n\nasync function readUploadArtifacts(\n publishDir: string,\n artifacts: readonly ArtifactMetadata[],\n): Promise<WidgetPackagePublishRequest[\"artifacts\"]> {\n const resolvedPublishDir = path.resolve(publishDir);\n\n return Promise.all(\n artifacts.map(async (artifact) => {\n const artifactPath = resolveArtifactPath(\n resolvedPublishDir,\n artifact.path,\n );\n const actual = await computeArtifactMetadata(\n artifactPath,\n resolvedPublishDir,\n );\n assertArtifactMetadataMatches(artifact, actual);\n\n return {\n ...actual,\n body: await fs.readFile(artifactPath),\n };\n }),\n );\n}\n\nfunction assertManifestIdentityMatches(\n manifest: Record<string, unknown>,\n publishManifest: PublishManifestMetadata,\n manifestPath: string,\n): void {\n assertMetadataMatches({\n label: \"packageId\",\n path: \"manifest.json\",\n expected: publishManifest.packageId,\n actual: readRequiredString(manifest, \"packageId\", manifestPath),\n });\n assertMetadataMatches({\n label: \"version\",\n path: \"manifest.json\",\n expected: publishManifest.version,\n actual: readRequiredString(manifest, \"version\", manifestPath),\n });\n}\n\nfunction assertArtifactInventoryMatches(\n expected: readonly ArtifactMetadata[],\n actual: readonly ArtifactMetadata[],\n): void {\n const expectedByPath = new Map<string, ArtifactMetadata>();\n for (const artifact of expected) {\n if (expectedByPath.has(artifact.path)) {\n throw new Error(\n `publish-manifest.json artifacts include duplicate path ${JSON.stringify(artifact.path)}. ` +\n \"Rebuild the widget package before publishing.\",\n );\n }\n expectedByPath.set(artifact.path, artifact);\n }\n\n const actualPaths = new Set<string>();\n for (const actualArtifact of actual) {\n actualPaths.add(actualArtifact.path);\n const expectedArtifact = expectedByPath.get(actualArtifact.path);\n if (!expectedArtifact) {\n throw new Error(\n `publish-manifest.json artifacts omit ${JSON.stringify(actualArtifact.path)}. ` +\n \"Rebuild the widget package before publishing.\",\n );\n }\n assertArtifactMetadataMatches(expectedArtifact, actualArtifact);\n }\n\n for (const expectedArtifact of expected) {\n if (actualPaths.has(expectedArtifact.path)) continue;\n throw new Error(\n `publish-manifest.json artifacts include ${JSON.stringify(expectedArtifact.path)}, but that file is not present on disk. ` +\n \"Rebuild the widget package before publishing.\",\n );\n }\n}\n\nfunction assertArtifactMetadataMatches(\n expected: ArtifactMetadata,\n actual: ArtifactMetadata,\n): void {\n assertMetadataMatches({\n label: \"path\",\n path: expected.path,\n expected: expected.path,\n actual: actual.path,\n });\n assertMetadataMatches({\n label: \"sha256\",\n path: expected.path,\n expected: expected.sha256,\n actual: actual.sha256,\n });\n assertMetadataMatches({\n label: \"bytes\",\n path: expected.path,\n expected: expected.bytes,\n actual: actual.bytes,\n });\n assertMetadataMatches({\n label: \"contentType\",\n path: expected.path,\n expected: expected.contentType,\n actual: actual.contentType,\n });\n}\n\nfunction assertMetadataMatches(options: {\n readonly label: string;\n readonly path: string;\n readonly expected: string | number;\n readonly actual: string | number;\n}): void {\n if (options.expected === options.actual) return;\n\n throw new Error(\n `publish-manifest.json ${options.label} for ${JSON.stringify(options.path)} does not match the file on disk. ` +\n `Recorded ${JSON.stringify(options.expected)}, computed ${JSON.stringify(options.actual)}. ` +\n \"Rebuild the widget package before publishing.\",\n );\n}\n\nfunction resolveArtifactPath(publishDir: string, artifactPath: string): string {\n const resolved = path.resolve(publishDir, artifactPath);\n const relative = path.relative(publishDir, resolved);\n if (relative.startsWith(\"..\") || path.isAbsolute(relative)) {\n throw new Error(\n `Artifact path ${JSON.stringify(artifactPath)} escapes the publish directory.`,\n );\n }\n return resolved;\n}\n\nfunction stripOwnerPrefix(\n packageId: string,\n owner: WidgetPackageBuildOwner,\n): string {\n const prefix = `${owner}.`;\n if (packageId.startsWith(prefix) && packageId.length > prefix.length) {\n return packageId.slice(prefix.length);\n }\n return packageId;\n}\n\nfunction readRequiredString(\n record: Record<string, unknown>,\n key: string,\n filePath: string,\n): string {\n const value = record[key];\n if (typeof value === \"string\" && value.trim().length > 0) return value;\n throw new Error(`${path.basename(filePath)} must include string ${key}.`);\n}\n\nfunction readRequiredNumber(\n record: Record<string, unknown>,\n key: string,\n filePath: string,\n): number {\n const value = record[key];\n if (typeof value === \"number\" && Number.isFinite(value)) return value;\n throw new Error(`${path.basename(filePath)} must include number ${key}.`);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n","import { Command } from \"commander\";\nimport type { PluginContext } from \"@fluid-app/fluid-cli\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport type { PortalDeployOptions } from \"../types.js\";\nimport {\n createAuthenticatedWidgetPackageClient,\n printRuntimeOnlyNotice,\n printDryRunSessionPayload,\n printWidgetPublishSummary,\n publishWidgetRuntimeArtifacts,\n} from \"./widget-package-publish.js\";\n\nconst DEFAULT_OUT_DIR = \".fluid/widget-dist\";\nconst DEFAULT_ENVIRONMENT = \"production\";\n\nexport const deployCommand: Command = new Command(\"deploy\")\n .description(\"Publish company-owned portal widget runtime artifacts\")\n .option(\n \"-e, --environment <name>\",\n \"Target environment label for output reporting\",\n DEFAULT_ENVIRONMENT,\n )\n .option(\n \"-o, --out-dir <dir>\",\n \"Widget artifact output directory\",\n DEFAULT_OUT_DIR,\n )\n .option(\"--dry-run\", \"Build and validate upload payload without publishing\")\n .action(async (options: PortalDeployOptions) => {\n const environment = options.environment ?? DEFAULT_ENVIRONMENT;\n const outDir = options.outDir ?? DEFAULT_OUT_DIR;\n const dryRun = options.dryRun === true;\n\n console.log();\n console.log(chalk.blue.bold(\"Fluid Portal Deploy\"));\n console.log();\n printRuntimeOnlyNotice();\n console.log(chalk.gray(\"Environment: \") + chalk.white(environment));\n console.log(chalk.gray(\"Output: \") + chalk.cyan(outDir));\n if (dryRun)\n console.log(chalk.yellow(\"Dry run: no upload will be created.\"));\n console.log();\n\n const spinner = ora(\"Building company-owned widget package...\").start();\n\n try {\n const result = await publishWidgetRuntimeArtifacts({\n projectDir: process.cwd(),\n outDir,\n buildOwner: \"company\",\n uploadOwner: { kind: \"company\" },\n dryRun,\n ...(dryRun ? {} : { client: createAuthenticatedWidgetPackageClient() }),\n });\n\n spinner.succeed(\"Built widget runtime artifacts\");\n\n if (dryRun) {\n console.log(\n chalk.yellow(\"Dry run complete — upload session was not requested.\"),\n );\n console.log();\n printDryRunSessionPayload(result);\n } else {\n console.log(chalk.green(\"Published widget package version.\"));\n }\n\n printWidgetPublishSummary({\n title: dryRun ? \"Portal deploy dry run\" : \"Portal deploy\",\n ownerLabel: \"company\",\n environment,\n outDir,\n result,\n });\n } catch (err) {\n spinner.fail(\"Portal deploy failed\");\n console.error(\n chalk.red(\"Error:\") +\n \" \" +\n (err instanceof Error ? err.message : String(err)),\n );\n process.exit(1);\n }\n });\n\nexport function registerDeployCommand(ctx: PluginContext): void {\n ctx.program.addCommand(deployCommand);\n}\n","import { Command } from \"commander\";\nimport type { PluginContext } from \"@fluid-app/fluid-cli\";\nimport chalk from \"chalk\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n/**\n * Severity levels for drift diagnostics.\n * - ERROR: Security-critical or breaking drift (e.g., missing CSP)\n * - WARN: Stale config that may cause subtle issues\n * - INFO: Cosmetic or non-functional differences\n */\nexport type Severity = \"error\" | \"warn\" | \"info\";\n\nexport interface Diagnostic {\n file: string;\n severity: Severity;\n message: string;\n}\n\n/** Files that are managed by the SDK and should match the canonical template. */\nconst INFRASTRUCTURE_FILES = [\n \"index.html\",\n \"tsconfig.json\",\n \"vite.config.ts\",\n \".oxlintrc.json\",\n] as const;\n\n/** Entry files with expected content patterns after Phase 2 absorption. */\nconst ENTRY_CHECKS = [\n {\n file: \"src/main.tsx\",\n expectedPattern: \"createPortal\",\n hint: 'main.tsx should use createPortal() from @fluid-app/portal-sdk. Run \"fluid create\" to see the latest template.',\n },\n {\n file: \"src/index.css\",\n expectedPattern: \"@fluid-app/portal-sdk/globals.css\",\n hint: \"index.css should import @fluid-app/portal-sdk/globals.css instead of inline theme tokens.\",\n },\n] as const;\n\n/** Files that should NOT exist after Phase 2 (absorbed into SDK). */\nconst REMOVED_FILES = [\n {\n file: \"src/App.tsx\",\n hint: \"App.tsx is no longer needed — createPortal() handles the AppShell wiring internally.\",\n },\n {\n file: \"src/fluid.config.ts\",\n hint: \"fluid.config.ts is no longer needed — pass config overrides to createPortal() directly.\",\n },\n] as const;\n\nfunction readFileOrNull(filePath: string): string | null {\n try {\n return readFileSync(filePath, \"utf-8\");\n } catch {\n return null;\n }\n}\n\nconst _currentFile = fileURLToPath(import.meta.url);\nconst _currentDir = dirname(_currentFile);\n\nfunction findTemplateDir(): string | null {\n // Walk up from current file to find package root (works in both dist/ and src/)\n let dir = _currentDir;\n while (!existsSync(join(dir, \"package.json\"))) {\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n\n const templateDir = join(dir, \"templates\");\n if (existsSync(join(templateDir, \"base\"))) return templateDir;\n return null;\n}\n\nexport function checkInfrastructureDrift(\n cwd: string,\n templateDir: string,\n): Diagnostic[] {\n const diagnostics: Diagnostic[] = [];\n\n for (const file of INFRASTRUCTURE_FILES) {\n const portalPath = join(cwd, file);\n const templatePath = join(templateDir, \"base\", file);\n\n if (!existsSync(portalPath)) {\n diagnostics.push({\n file,\n severity: \"warn\",\n message: `Missing file: ${file}`,\n });\n continue;\n }\n\n if (!existsSync(templatePath)) {\n // Template doesn't have this file — skip\n continue;\n }\n\n const portalContent = readFileOrNull(portalPath);\n const templateContent = readFileOrNull(templatePath);\n\n if (portalContent !== null && templateContent !== null) {\n if (portalContent.trim() !== templateContent.trim()) {\n diagnostics.push({\n file,\n severity: \"warn\",\n message: `Content differs from canonical template. Review and update if needed.`,\n });\n }\n }\n }\n\n return diagnostics;\n}\n\nexport function checkEntryPatterns(cwd: string): Diagnostic[] {\n const diagnostics: Diagnostic[] = [];\n\n for (const check of ENTRY_CHECKS) {\n const filePath = join(cwd, check.file);\n const content = readFileOrNull(filePath);\n\n if (content === null) {\n diagnostics.push({\n file: check.file,\n severity: \"error\",\n message: `Missing file. ${check.hint}`,\n });\n continue;\n }\n\n if (!content.includes(check.expectedPattern)) {\n diagnostics.push({\n file: check.file,\n severity: \"warn\",\n message: `Does not contain expected pattern \"${check.expectedPattern}\". ${check.hint}`,\n });\n }\n }\n\n return diagnostics;\n}\n\nexport function checkRemovedFiles(cwd: string): Diagnostic[] {\n const diagnostics: Diagnostic[] = [];\n\n for (const check of REMOVED_FILES) {\n if (existsSync(join(cwd, check.file))) {\n diagnostics.push({\n file: check.file,\n severity: \"info\",\n message: `File can be removed. ${check.hint}`,\n });\n }\n }\n\n return diagnostics;\n}\n\nfunction formatDiagnostic(d: Diagnostic): string {\n const icon =\n d.severity === \"error\"\n ? chalk.red(\"ERROR\")\n : d.severity === \"warn\"\n ? chalk.yellow(\" WARN\")\n : chalk.blue(\" INFO\");\n\n return ` ${icon} ${chalk.bold(d.file)}\\n ${chalk.dim(d.message)}`;\n}\n\nexport const doctorCommand: Command = new Command(\"doctor\")\n .description(\n \"Check portal for scaffold drift and report stale infrastructure files\",\n )\n .action(async () => {\n const cwd = process.cwd();\n\n // Validate we're in a portal project\n const packageJsonPath = join(cwd, \"package.json\");\n if (!existsSync(packageJsonPath)) {\n console.error(\n chalk.red(\"Error: No package.json found in current directory\"),\n );\n console.error(\n chalk.yellow(\"Make sure you're in a Fluid portal project directory\"),\n );\n process.exit(1);\n }\n\n let packageJson: {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n };\n try {\n packageJson = JSON.parse(readFileSync(packageJsonPath, \"utf-8\"));\n } catch {\n console.error(chalk.red(\"Error: Could not parse package.json\"));\n console.error(chalk.yellow(\"Ensure package.json contains valid JSON\"));\n process.exit(1);\n }\n const deps = {\n ...packageJson.dependencies,\n ...packageJson.devDependencies,\n };\n\n if (!deps[\"@fluid-app/portal-sdk\"]) {\n console.error(\n chalk.red(\"Error: @fluid-app/portal-sdk not found in dependencies\"),\n );\n console.error(\n chalk.yellow(\n \"This command must be run from a Fluid portal project directory\",\n ),\n );\n process.exit(1);\n }\n\n console.log();\n console.log(chalk.bold(\"Fluid Portal Doctor\"));\n console.log(chalk.dim(\"Checking for scaffold drift...\\n\"));\n\n const diagnostics: Diagnostic[] = [];\n\n // Check entry file patterns\n diagnostics.push(...checkEntryPatterns(cwd));\n\n // Check for files that should be removed\n diagnostics.push(...checkRemovedFiles(cwd));\n\n // Check infrastructure drift against templates (if available)\n const templateDir = findTemplateDir();\n if (templateDir) {\n diagnostics.push(...checkInfrastructureDrift(cwd, templateDir));\n } else {\n console.log(\n chalk.dim(\n \" (Skipping infrastructure diff — template files not available)\\n\",\n ),\n );\n }\n\n // Report results\n if (diagnostics.length === 0) {\n console.log(chalk.green(\" All clear — no drift detected.\\n\"));\n return;\n }\n\n const errors = diagnostics.filter((d) => d.severity === \"error\");\n const warns = diagnostics.filter((d) => d.severity === \"warn\");\n const infos = diagnostics.filter((d) => d.severity === \"info\");\n\n for (const d of [...errors, ...warns, ...infos]) {\n console.log(formatDiagnostic(d));\n console.log();\n }\n\n // Summary\n const parts: string[] = [];\n if (errors.length > 0) parts.push(chalk.red(`${errors.length} error(s)`));\n if (warns.length > 0)\n parts.push(chalk.yellow(`${warns.length} warning(s)`));\n if (infos.length > 0) parts.push(chalk.blue(`${infos.length} info`));\n console.log(` ${parts.join(\", \")}\\n`);\n\n if (errors.length > 0) {\n process.exit(1);\n }\n });\n\nexport function registerDoctorCommand(ctx: PluginContext): void {\n ctx.program.addCommand(doctorCommand);\n}\n","import { Command } from \"commander\";\nimport type { PluginContext } from \"@fluid-app/fluid-cli\";\nimport { getAuthToken } from \"@fluid-app/fluid-cli\";\nimport chalk from \"chalk\";\nimport { createFetchClient, fluidOs } from \"@fluid-app/fluidos-api-client\";\nimport prompts from \"prompts\";\nimport path from \"node:path\";\nimport { readMappings } from \"../utils/mappings.js\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst PORTAL_SYNC_DIR = \".portal-sync\";\n\nfunction getApiBase(): string {\n return process.env[\"FLUID_API_BASE\"] ?? \"https://api.fluid.app\";\n}\n\nfunction requireToken(): string {\n const token = getAuthToken();\n if (!token) {\n console.error(\n chalk.red(\"Error:\") +\n \" Not logged in. Run \" +\n chalk.cyan(\"`fluid login`\") +\n \" first.\",\n );\n process.exit(1);\n }\n return token;\n}\n\nfunction createClient(token: string) {\n return createFetchClient({\n baseUrl: getApiBase(),\n getAuthToken: () => token,\n });\n}\n\nasync function requireDefinitionId(): Promise<number> {\n const cwd = process.cwd();\n const mappings = await readMappings(path.join(cwd, PORTAL_SYNC_DIR));\n if (!mappings) {\n console.error(\n chalk.red(\"Error:\") +\n \" No definition pulled. Run \" +\n chalk.cyan(\"`fluid portal pull`\") +\n \" first.\",\n );\n process.exit(1);\n }\n return mappings.definition.id;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Subcommands\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst createVersionCommand = new Command(\"create\")\n .description(\"Create a new version (snapshot) of the portal definition\")\n .option(\"--activate\", \"Activate the version immediately after creation\")\n .action(async (options: { activate?: boolean }) => {\n const token = requireToken();\n const client = createClient(token);\n const definitionId = await requireDefinitionId();\n\n console.log();\n console.log(chalk.bold(\"Creating version...\"));\n\n let result;\n try {\n result = await fluidOs.fluid_os_v0_create_fluid_osversion(\n client,\n definitionId,\n );\n } catch (err) {\n console.error(\n chalk.red(\"Error:\") +\n \" Failed to create version — \" +\n (err instanceof Error ? err.message : String(err)),\n );\n process.exit(1);\n }\n\n const version = result.version;\n\n if (!version?.id) {\n console.error(\n chalk.red(\"Error:\") +\n \" Failed to create version — unexpected response.\",\n );\n process.exit(1);\n }\n\n console.log();\n console.log(chalk.green(\"Version created successfully.\"));\n console.log();\n console.log(chalk.gray(\"Version ID: \") + chalk.white(version.id));\n\n let active = version.active ?? false;\n\n if (options.activate) {\n console.log(\"Activating version...\");\n try {\n await fluidOs.fluid_os_v0_update_fluid_osversion(\n client,\n definitionId,\n version.id,\n {\n version: { active: true },\n },\n );\n } catch (err) {\n console.error(\n chalk.red(\"Error:\") +\n \" Failed to activate version — \" +\n (err instanceof Error ? err.message : String(err)),\n );\n process.exit(1);\n }\n active = true;\n }\n\n console.log(\n chalk.gray(\"Active: \") +\n (active ? chalk.green(\"yes\") : chalk.gray(\"no\")),\n );\n console.log();\n });\n\nconst listVersionCommand = new Command(\"list\")\n .description(\"List all versions of the portal definition\")\n .action(async () => {\n const token = requireToken();\n const client = createClient(token);\n const definitionId = await requireDefinitionId();\n\n let result;\n try {\n result = await fluidOs.fluid_os_v0_list_fluid_osversions(\n client,\n definitionId,\n );\n } catch (err) {\n console.error(\n chalk.red(\"Error:\") +\n \" Failed to list versions — \" +\n (err instanceof Error ? err.message : String(err)),\n );\n process.exit(1);\n }\n\n const versions = result.version;\n\n if (!Array.isArray(versions) || versions.length === 0) {\n console.log();\n console.log(chalk.yellow(\"No versions found.\"));\n console.log(\n \"Run \" +\n chalk.cyan(\"`fluid portal version create`\") +\n \" to publish a version.\",\n );\n console.log();\n return;\n }\n\n console.log();\n\n // Print table header\n const COL_ID = \"Version ID\".padEnd(36);\n const COL_ACT = \"Active\".padEnd(6);\n const COL_PUB = \"Published\";\n console.log(chalk.gray(` ${COL_ID} ${COL_ACT} ${COL_PUB}`));\n\n for (const v of versions) {\n const id = String(v.id).padEnd(36);\n const active = v.active ? chalk.green(\"\\u2713\") + \" \" : \" \";\n const published = v.published_at\n ? new Date(v.published_at).toLocaleString()\n : \"\\u2014\";\n console.log(` ${id} ${active} ${published}`);\n }\n\n console.log();\n });\n\nconst activateVersionCommand = new Command(\"activate\")\n .description(\"Activate a specific version, making it the live version\")\n .argument(\"<version-id>\", \"The version ID to activate\")\n .option(\"-y, --yes\", \"Skip confirmation prompt\")\n .action(async (versionId: string, options: { yes?: boolean }) => {\n const token = requireToken();\n const client = createClient(token);\n const definitionId = await requireDefinitionId();\n\n if (!options.yes) {\n const { confirm } = await prompts({\n type: \"confirm\",\n name: \"confirm\",\n message: `Activate version ${versionId}? This will make it the live version.`,\n initial: false,\n });\n\n if (!confirm) {\n console.log(chalk.yellow(\"Aborted.\"));\n return;\n }\n }\n\n console.log();\n console.log(chalk.bold(\"Activating version...\"));\n\n try {\n await fluidOs.fluid_os_v0_update_fluid_osversion(\n client,\n definitionId,\n versionId,\n {\n version: { active: true },\n },\n );\n } catch (err) {\n console.error(\n chalk.red(\"Error:\") +\n \" Failed to activate version — \" +\n (err instanceof Error ? err.message : String(err)),\n );\n process.exit(1);\n }\n\n console.log();\n console.log(chalk.green(\"Version \" + versionId + \" is now active.\"));\n console.log();\n });\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Version command group\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport const versionCommand: Command = new Command(\"version\")\n .description(\"Manage portal definition versions\")\n .addCommand(createVersionCommand)\n .addCommand(listVersionCommand)\n .addCommand(activateVersionCommand);\n\nexport function registerVersionCommand(ctx: PluginContext): void {\n ctx.program.addCommand(versionCommand);\n}\n","import { Command } from \"commander\";\nimport type { PluginContext } from \"@fluid-app/fluid-cli\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport type { WidgetPublishOptions } from \"../types.js\";\nimport {\n createAuthenticatedWidgetPackageClient,\n printDryRunSessionPayload,\n printWidgetPublishSummary,\n printRuntimeOnlyNotice,\n publishWidgetRuntimeArtifacts,\n} from \"./widget-package-publish.js\";\n\nconst DEFAULT_OUT_DIR = \".fluid/widget-dist\";\n\nconst publishSubcommand = new Command(\"publish\")\n .description(\"Publish droplet-owned widget runtime artifacts\")\n .requiredOption(\n \"--droplet <uuid>\",\n \"Droplet UUID that owns the widget package\",\n )\n .option(\n \"-o, --out-dir <dir>\",\n \"Widget artifact output directory\",\n DEFAULT_OUT_DIR,\n )\n .option(\"--dry-run\", \"Build and validate upload payload without publishing\")\n .action(async (options: WidgetPublishOptions) => {\n const outDir = options.outDir ?? DEFAULT_OUT_DIR;\n const dryRun = options.dryRun === true;\n\n console.log();\n console.log(chalk.blue.bold(\"Fluid Widget Publish\"));\n console.log();\n printRuntimeOnlyNotice();\n console.log(chalk.gray(\"Droplet: \") + chalk.white(options.droplet));\n console.log(chalk.gray(\"Output: \") + chalk.cyan(outDir));\n if (dryRun)\n console.log(chalk.yellow(\"Dry run: no upload will be created.\"));\n console.log();\n\n const spinner = ora(\"Building droplet-owned widget package...\").start();\n\n try {\n const result = await publishWidgetRuntimeArtifacts({\n projectDir: process.cwd(),\n outDir,\n buildOwner: \"droplet\",\n uploadOwner: { kind: \"droplet\", uuid: options.droplet },\n dryRun,\n ...(dryRun ? {} : { client: createAuthenticatedWidgetPackageClient() }),\n });\n\n spinner.succeed(\"Built widget runtime artifacts\");\n\n if (dryRun) {\n console.log(\n chalk.yellow(\"Dry run complete — upload session was not requested.\"),\n );\n console.log();\n printDryRunSessionPayload(result);\n } else {\n console.log(chalk.green(\"Published widget package version.\"));\n }\n\n printWidgetPublishSummary({\n title: dryRun ? \"Widget publish dry run\" : \"Widget publish\",\n ownerLabel: `droplet ${options.droplet}`,\n outDir,\n result,\n });\n } catch (err) {\n spinner.fail(\"Widget publish failed\");\n console.error(\n chalk.red(\"Error:\") +\n \" \" +\n (err instanceof Error ? err.message : String(err)),\n );\n process.exit(1);\n }\n });\n\nexport const topLevelWidgetCommand: Command = new Command(\"widget\")\n .description(\"Publish Fluid widget packages\")\n .addCommand(publishSubcommand);\n\nexport function registerTopLevelWidgetCommand(ctx: PluginContext): void {\n ctx.program.addCommand(topLevelWidgetCommand);\n}\n","/**\n * @fluid-app/fluid-cli-portal\n *\n * Fluid CLI plugin for building portal applications.\n * Auto-discovered by @fluid-app/fluid-cli via the fluid-cli-* naming convention.\n */\n\nimport { Command } from \"commander\";\nimport type { FluidPlugin, PluginContext } from \"@fluid-app/fluid-cli\";\nimport { createCommand } from \"./commands/create.js\";\nimport { devCommand } from \"./commands/dev.js\";\nimport { buildCommand } from \"./commands/build.js\";\nimport { pullCommand } from \"./commands/pull.js\";\nimport { pushCommand } from \"./commands/push.js\";\nimport { cloneCommand } from \"./commands/clone.js\";\nimport { widgetCommand } from \"./commands/widget-create.js\";\nimport { deployCommand } from \"./commands/deploy.js\";\nimport { doctorCommand } from \"./commands/doctor.js\";\nimport { versionCommand } from \"./commands/version.js\";\n\nconst plugin: FluidPlugin = {\n name: \"fluid-cli-portal\",\n version: \"0.1.0\",\n async register(ctx: PluginContext) {\n const portal = new Command(\"portal\").description(\n \"Build and develop portal applications\",\n );\n\n portal.addCommand(createCommand);\n portal.addCommand(devCommand);\n portal.addCommand(buildCommand);\n portal.addCommand(pullCommand);\n portal.addCommand(pushCommand);\n portal.addCommand(cloneCommand);\n portal.addCommand(deployCommand);\n portal.addCommand(widgetCommand);\n portal.addCommand(doctorCommand);\n portal.addCommand(versionCommand);\n\n ctx.program.addCommand(portal);\n },\n};\n\nexport default plugin;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Re-exports for programmatic usage (e.g., @fluid-app/create-portal-app)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport type {\n ProjectConfig,\n CreateOptions,\n DevOptions,\n BuildOptions,\n TemplateVariables,\n SelectedPageTemplate,\n TemplateName,\n WidgetCreateOptions,\n PortalDeployOptions,\n WidgetPublishOptions,\n} from \"./types.js\";\n\nexport { TEMPLATES } from \"./types.js\";\n\nexport {\n getInstallCommand,\n getRunCommand,\n runPackageManager,\n installDependencies,\n} from \"./utils/package-manager.js\";\n\nexport {\n getTemplatePaths,\n copyTemplate,\n directoryExists,\n fileExists,\n pathExists,\n createDirectory,\n getSdkVersion,\n readFileSafe,\n writeFileSafe,\n createDirectorySafe,\n copyTemplateSafe,\n getSdkVersionSafe,\n FILE_SYSTEM_ERRORS,\n type FileSystemErrorCode,\n type FileSystemError,\n type TemplatePaths,\n} from \"./utils/file-system.js\";\n\nexport { promptProjectConfig } from \"./utils/prompts.js\";\n\nexport {\n scaffoldFromGitHub,\n rewriteProjectName,\n writeFluidrcProfile,\n TEMPLATE_REPO_URL,\n CANONICAL_PROJECT_NAME,\n type ScaffoldFromGitHubOptions,\n} from \"./utils/scaffold-github.js\";\n\nexport {\n GitError,\n gitMissingError,\n isGitAvailable,\n ensureGitAvailable,\n gitClone,\n gitInit,\n isGitRepo,\n getOriginUrl,\n hasUncommittedChanges,\n gitFetch,\n gitAheadBehindVsFetchHead,\n gitMergeFastForward,\n gitAutoCommit,\n gitPush,\n} from \"./lib/git.js\";\n\nexport {\n createPortalGitCredential,\n portalGitCredentialPath,\n type PortalGitCredential,\n type GitCredentialOutcome,\n type CreatePortalGitCredentialOptions,\n} from \"./api/git-credential.js\";\n\nexport {\n runPortalGitSync,\n type PortalGitSyncResult,\n type PortalGitSyncOptions,\n} from \"./lib/git-sync.js\";\n\nexport { cloneCommand } from \"./commands/clone.js\";\n\n// Expose the standalone Command for create-portal-app\nexport {\n createCommand,\n createPortalProject,\n type CreatePortalProjectDependencies,\n} from \"./commands/create.js\";\nexport { pullCommand } from \"./commands/pull.js\";\nexport { pushCommand } from \"./commands/push.js\";\nexport { widgetCommand } from \"./commands/widget-create.js\";\nexport { topLevelWidgetCommand } from \"./commands/widget-publish.js\";\nexport { deployCommand } from \"./commands/deploy.js\";\nexport { versionCommand } from \"./commands/version.js\";\nexport { doctorCommand } from \"./commands/doctor.js\";\n\nexport {\n readMappings,\n writeMappings,\n deriveSlug,\n resolveSlugToId,\n resolveIdToSlug,\n updateMapping,\n removeMapping,\n type PortalMappings,\n type DefinitionMapping,\n type MappedResourceType,\n} from \"./utils/mappings.js\";\n\nexport {\n computeFileHash,\n readSnapshot,\n writeSnapshot,\n diffAgainstSnapshot,\n buildSnapshot,\n type Snapshot,\n type SnapshotDiff,\n type FileHash,\n type FileHashMap,\n} from \"./utils/snapshot.js\";\n\nexport {\n transformScreen,\n deriveScreenSlug,\n transformTheme,\n transformNavigation,\n transformNavigationItems,\n transformProfile,\n buildIdToSlugMap,\n buildNavigationIdToSlugMap,\n buildThemeIdToSlugMap,\n type LocalScreen,\n type LocalTheme,\n type LocalNavigation,\n type LocalNavigationItem,\n type LocalProfile,\n} from \"./utils/transform.js\";\n\nexport {\n categorizeChanges,\n validateCrossReferences,\n slugFromPath,\n type CategorizedChanges,\n type ValidationError,\n} from \"./utils/push-validation.js\";\n\nexport {\n buildSharedWidgetPackage,\n createWidgetPackageEntrySource,\n type BuildSharedWidgetPackageOptions,\n type SharedWidgetPackageBuildResult,\n type SharedWidgetPackageBuildError,\n} from \"./utils/widget-package-builder.js\";\n\nexport {\n resolvePortalWidgetSourceConfig,\n loadSourceWidgetPackages,\n type WidgetSourceConfig,\n type WidgetSourceConfigLoadError,\n} from \"./utils/widget-package-config.js\";\n\nexport {\n validateSingleSourceWidgetPackage,\n buildWidgetPackagePublishManifest,\n type SourceWidgetMetadata,\n type SourceWidgetPackageMetadata,\n type WidgetDescriptorMetadata,\n type ValidatedWidgetPackageBuild,\n type WidgetPackageValidationErrorCode,\n type WidgetPackageValidationError,\n type WidgetPackageValidationResult,\n type ValidateSourceWidgetPackageOptions,\n type WidgetPackageOwnerKind,\n type WidgetPackageArtifactPath,\n type WidgetPackagePublishManifest,\n} from \"./utils/widget-package-validation.js\";\n\nexport type { RemoteDomWidgetPackageDescriptor } from \"@fluid-app/portal-core/remote-dom-widget-package\";\n\nexport {\n WIDGET_PACKAGE_BUILDER_VERSION,\n computeArtifactMetadata,\n collectWidgetPackageArtifacts,\n createPublishManifestMetadata,\n getArtifactContentType,\n sha256,\n type ArtifactMetadata,\n type PublishManifestMetadata,\n} from \"./utils/widget-package-artifacts.js\";\n\nexport {\n publishWidgetRuntimeArtifacts,\n createWidgetPackagePublishRequest,\n createAuthenticatedWidgetPackageClient,\n printDryRunSessionPayload,\n printWidgetPublishSummary,\n printRuntimeOnlyNotice,\n validateWidgetPublishOutDir,\n type PublishWidgetRuntimeArtifactsOptions,\n type PublishWidgetRuntimeArtifactsResult,\n type PublishWidgetRuntimeArtifactsDependencies,\n} from \"./commands/widget-package-publish.js\";\n\nexport {\n buildWidgetPackageUploadSessionPayload,\n getWidgetPackageUploadSessionRoute,\n getWidgetPackageCompleteUploadRoute,\n requestWidgetPackageUploadSession,\n uploadWidgetPackageArtifacts,\n completeWidgetPackageUpload,\n publishWidgetPackageVersion,\n normalizeWidgetPackageUploadSession,\n normalizeWidgetPackageCompleteResponse,\n buildWidgetPackageCompleteUploadPayload,\n WidgetPackageUploadError,\n type WidgetPackageUploadOwner,\n type WidgetPackageArtifactMetadata,\n type WidgetPackageUploadArtifact,\n type WidgetPackageSessionRequest,\n type WidgetPackagePublishRequest,\n type WidgetPackageArtifactPayload,\n type WidgetPackageUploadSessionPayload,\n type WidgetPackageCompleteUploadPayload,\n type WidgetPackageSignedUpload,\n type WidgetPackageUploadSession,\n type WidgetPackageArtifactUploadResult,\n type WidgetPackageCompleteResult,\n type WidgetPackagePublishResult,\n type PublishWidgetPackageVersionOptions,\n type WidgetPackageUploadFetch,\n} from \"./utils/widget-package-upload.js\";\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["_currentDir","PORTAL_DIR","readPortalFile","PORTAL_SYNC_DIR","createClient","fluidOs.fluid_os_v0_create_fluid_osscreen","fluidOs.fluid_os_v0_update_fluid_osscreen","fluidOs.fluid_os_v0_delete_fluid_osscreen","fluidOs.fluid_os_v0_create_fluid_ostheme","fluidOs.fluid_os_v0_update_fluid_ostheme","fluidOs.fluid_os_v0_delete_fluid_ostheme","fluidOs.fluid_os_v0_create_fluid_osnavigation","fluidOs.fluid_os_v0_create_fluid_osnavigation_item","fluidOs.fluid_os_v0_update_fluid_osnavigation","fluidOs.fluid_os_v0_list_fluid_osnavigation_items","fluidOs.fluid_os_v0_delete_fluid_osnavigation_item","fluidOs.fluid_os_v0_update_fluid_osnavigation_item","fluidOs.fluid_os_v0_delete_fluid_osnavigation","fluidOs.fluid_os_v0_create_fluid_osprofile","fluidOs.fluid_os_v0_update_fluid_osprofile","fluidOs.fluid_os_v0_delete_fluid_osprofile","createClient","fluidOs.fluid_os_v0_list_fluid_osdefinitions","WIDGET_CONFIG_PATH","isRecord","toPosixPath","isRecord","isRecord","isRecord","fluidOs.fluid_os_v0_create_widget_package_version","fluidOs.fluid_os_v0_complete_widget_package_version_upload","isRecord","DEFAULT_OUT_DIR","fluidOs.fluid_os_v0_create_fluid_osversion","fluidOs.fluid_os_v0_update_fluid_osversion","fluidOs.fluid_os_v0_list_fluid_osversions"],"sources":["../src/types.ts","../src/utils/prompts.ts","../src/utils/file-system.ts","../src/utils/package-manager.ts","../src/lib/git.ts","../src/utils/scaffold-github.ts","../src/commands/create.ts","../src/commands/dev.ts","../src/commands/build.ts","../src/utils/push-validation.ts","../src/api/git-credential.ts","../src/lib/git-sync.ts","../src/commands/push.ts","../src/commands/clone.ts","../src/utils/widget-helpers.ts","../src/commands/widget-create.ts","../src/utils/widget-package-config.ts","../src/utils/widget-package-artifacts.ts","../../../portal/core/src/remote-dom-widget-package.ts","../src/utils/widget-package-validation.ts","../src/utils/widget-package-builder.ts","../src/utils/widget-package-upload.ts","../src/commands/widget-package-publish.ts","../src/commands/deploy.ts","../src/commands/doctor.ts","../src/commands/version.ts","../src/commands/widget-publish.ts","../src/index.ts"],"sourcesContent":["// ─────────────────────────────────────────────────────────────────────────────\n// Template types - derived from const object\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Available project templates\n */\nexport const TEMPLATES = {\n starter: \"starter\",\n} as const;\n\n/**\n * Union type of valid template names\n */\nexport type TemplateName = (typeof TEMPLATES)[keyof typeof TEMPLATES];\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Project configuration types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Selected page template info\n */\nexport interface SelectedPageTemplate {\n readonly id: string;\n readonly slug: string;\n readonly name: string;\n}\n\n/**\n * Configuration options collected during project scaffolding\n */\nexport interface ProjectConfig {\n /** Project name (used for directory and package.json name) */\n readonly name: string;\n /** Whether to install dependencies after scaffolding */\n readonly installDeps: boolean;\n /** Selected optional page templates to include */\n readonly selectedPages: readonly SelectedPageTemplate[];\n /** CLI profile name for .fluidrc (empty string if none selected) */\n readonly profileName: string;\n}\n\n/**\n * Options for the create command (from CLI arguments)\n */\nexport interface CreateOptions {\n /** Create only the scaffold and skip dependency installation */\n readonly skipInstall?: boolean;\n /** Directory to create the project in (defaults to cwd) */\n readonly outputDir?: string;\n /** Use local monorepo packages via file: links instead of npm versions */\n readonly local?: boolean;\n /** Explicitly use the GitHub starter instead of the bundled template. */\n readonly remoteTemplate?: boolean;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Command option types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Options for the dev command\n */\nexport interface DevOptions {\n readonly port?: number;\n readonly host?: boolean;\n}\n\n/**\n * Options for the build command\n */\nexport interface BuildOptions {\n readonly outDir?: string;\n}\n\n/**\n * Options for the widget create command\n */\nexport interface WidgetCreateOptions {\n /** Category for palette grouping */\n readonly category?: string;\n}\n\n/**\n * Options for the portal deploy command\n */\nexport interface PortalDeployOptions {\n readonly environment?: string;\n readonly outDir?: string;\n readonly dryRun?: boolean;\n}\n\n/**\n * Options for the top-level widget publish command\n */\nexport interface WidgetPublishOptions {\n readonly droplet: string;\n readonly outDir?: string;\n readonly dryRun?: boolean;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Template processing types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Template variables for Handlebars processing\n */\nexport interface TemplateVariables {\n readonly projectName: string;\n readonly sdkVersion: string;\n /** portal-core link used only for --local scaffolds */\n readonly localCoreVersion?: string;\n /** CLI package version (versioned independently from the SDK) */\n readonly cliVersion: string;\n /** Portal CLI plugin version (versioned independently from CLI core) */\n readonly portalCliVersion: string;\n /** Selected page templates for the project */\n readonly selectedPages: readonly SelectedPageTemplate[];\n /** Whether any optional pages were selected */\n readonly hasSelectedPages: boolean;\n /** CLI profile name for .fluidrc */\n readonly profileName: string;\n}\n","import { getActiveProfile, listProfileNames } from \"@fluid-app/fluid-cli\";\nimport prompts from \"prompts\";\nimport {\n type ProjectConfig,\n type CreateOptions,\n type SelectedPageTemplate,\n} from \"../types.js\";\n\n/**\n * Optional page template shape\n */\ninterface OptionalPageTemplate {\n readonly id: string;\n readonly slug: string;\n readonly name: string;\n readonly description: string;\n}\n\n/**\n * Available optional page templates that can be selected during project creation.\n * Core pages (Messaging, Contacts, CRM) are always included automatically.\n */\nconst OPTIONAL_PAGE_TEMPLATES: readonly OptionalPageTemplate[] = [\n // Currently no optional pages - all pages are core\n // Future optional pages can be added here:\n // { id: 'orders', slug: 'orders', name: 'Orders', description: 'Order management page' },\n // { id: 'products', slug: 'products', name: 'Products', description: 'Product catalog page' },\n];\n\n/**\n * Prompts the user for project configuration\n * Pre-fills values from CLI options when provided\n */\nexport async function promptProjectConfig(\n projectName: string,\n options: CreateOptions,\n): Promise<ProjectConfig | null> {\n // Build questions based on what options are missing\n const questions: prompts.PromptObject[] = [];\n\n // Page template selection (only if there are optional templates)\n if (OPTIONAL_PAGE_TEMPLATES.length > 0) {\n questions.push({\n type: \"multiselect\",\n name: \"selectedPages\",\n message: \"Select additional page templates to include\",\n instructions:\n \"\\n Space to select, Enter to confirm. Core pages (Messaging, Contacts, CRM) are always included.\",\n choices: OPTIONAL_PAGE_TEMPLATES.map((page) => ({\n title: page.name,\n value: { id: page.id, slug: page.slug, name: page.name },\n description: page.description,\n })),\n });\n }\n\n // CLI profile for .fluidrc\n const existingProfiles = listProfileNames();\n if (existingProfiles.length > 0) {\n const active = getActiveProfile();\n questions.push({\n type: \"select\",\n name: \"profileName\",\n message: \"CLI profile for this project (.fluidrc)\",\n choices: existingProfiles.map((name) => ({\n title: name === active?.name ? `${name} (active)` : name,\n value: name,\n })),\n });\n }\n\n // Install dependencies\n if (!options.skipInstall) {\n questions.push({\n type: \"confirm\",\n name: \"installDeps\",\n message: \"Install dependencies?\",\n initial: true,\n });\n }\n\n // Non-interactive mode: if stdin is not a TTY and there are remaining\n // prompts, return safe defaults instead of hanging on interactive input.\n if (!process.stdin.isTTY && questions.length > 0) {\n return {\n name: projectName,\n installDeps: false,\n selectedPages: [],\n profileName: getActiveProfile()?.name ?? \"\",\n } satisfies ProjectConfig;\n }\n\n // Fast-path: all options provided via CLI flags, no prompts needed\n if (questions.length === 0) {\n return {\n name: projectName,\n installDeps: options.skipInstall ? false : true,\n selectedPages: [],\n profileName: getActiveProfile()?.name ?? \"\",\n } satisfies ProjectConfig;\n }\n\n // Handle Ctrl+C gracefully\n let cancelled = false;\n const response = await prompts(questions, {\n onCancel: () => {\n cancelled = true;\n return false;\n },\n });\n\n if (cancelled) {\n return null;\n }\n\n // Parse selected pages\n const selectedPages: readonly SelectedPageTemplate[] =\n response.selectedPages ?? [];\n\n return {\n name: projectName,\n installDeps: options.skipInstall ? false : (response.installDeps ?? true),\n selectedPages,\n profileName:\n (response.profileName as string | undefined) ??\n getActiveProfile()?.name ??\n \"\",\n } satisfies ProjectConfig;\n}\n","import type { CliError } from \"@fluid-app/fluid-cli\";\nimport { readdir, readFile, stat, mkdir, writeFile } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { join, dirname, relative } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport Handlebars from \"handlebars\";\nimport type { TemplateVariables } from \"../types.js\";\nimport { type Result, success, failure } from \"@fluid-app/fluid-cli\";\n\nconst _currentFile = fileURLToPath(import.meta.url);\nconst _currentDir = dirname(_currentFile);\n\ndeclare const __FLUID_PORTAL_SDK_VERSION__: string | undefined;\ndeclare const __FLUID_CLI_CORE_VERSION__: string | undefined;\ndeclare const __FLUID_CLI_PORTAL_VERSION__: string | undefined;\n\nconst EMBEDDED_PORTAL_SDK_VERSION =\n typeof __FLUID_PORTAL_SDK_VERSION__ === \"string\"\n ? __FLUID_PORTAL_SDK_VERSION__\n : undefined;\nconst EMBEDDED_CLI_CORE_VERSION =\n typeof __FLUID_CLI_CORE_VERSION__ === \"string\"\n ? __FLUID_CLI_CORE_VERSION__\n : undefined;\nconst EMBEDDED_CLI_PORTAL_VERSION =\n typeof __FLUID_CLI_PORTAL_VERSION__ === \"string\"\n ? __FLUID_CLI_PORTAL_VERSION__\n : undefined;\n\n/**\n * Find the package root by walking up from the current directory to the nearest package.json.\n * Works whether running from dist/ (bundled) or src/utils/ (tsx dev mode).\n */\nfunction findPackageRoot(): string {\n let dir = _currentDir;\n while (!existsSync(join(dir, \"package.json\"))) {\n const parent = dirname(dir);\n if (parent === dir) throw new Error(\"Could not find package root\");\n dir = parent;\n }\n return dir;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// File system operation error types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Error types for file system operations\n */\nexport const FILE_SYSTEM_ERRORS = {\n directoryNotFound: \"DIRECTORY_NOT_FOUND\",\n fileNotFound: \"FILE_NOT_FOUND\",\n readError: \"READ_ERROR\",\n writeError: \"WRITE_ERROR\",\n templateError: \"TEMPLATE_ERROR\",\n} as const;\n\n/**\n * Union type for file system error codes\n */\nexport type FileSystemErrorCode =\n (typeof FILE_SYSTEM_ERRORS)[keyof typeof FILE_SYSTEM_ERRORS];\n\n/**\n * Structured file system error with code for pattern matching\n */\nexport interface FileSystemError extends CliError {\n readonly code: FileSystemErrorCode;\n readonly message: string;\n readonly path?: string;\n readonly cause?: Error;\n}\n\n/**\n * Create a file system error\n */\nfunction createFsError(\n code: FileSystemErrorCode,\n message: string,\n path?: string,\n cause?: Error,\n): FileSystemError {\n return { code, message, path, cause };\n}\n\n/**\n * Paths for the base + overlay template system\n */\nexport interface TemplatePaths {\n /** Path to shared frontend files used by all templates */\n readonly base: string;\n /** Path to template-specific overlay files */\n readonly overlay: string;\n}\n\n/**\n * Gets paths for the base + overlay template system.\n *\n * The create command copies `base` first, then the `overlay` on top.\n * Any overlay file with the same relative path overwrites the base version.\n */\nexport function getTemplatePaths(templateName: string): TemplatePaths {\n const packageRoot = findPackageRoot();\n const templatesDir = join(packageRoot, \"templates\");\n return {\n base: join(templatesDir, \"base\"),\n overlay: join(templatesDir, templateName),\n };\n}\n\n/**\n * Gets all files in a directory recursively\n */\nasync function getFiles(dir: string, baseDir: string = dir): Promise<string[]> {\n const entries = await readdir(dir, { withFileTypes: true });\n const files: string[] = [];\n\n for (const entry of entries) {\n const fullPath = join(dir, entry.name);\n if (entry.isDirectory()) {\n files.push(...(await getFiles(fullPath, baseDir)));\n } else {\n // Return relative path from baseDir\n files.push(fullPath.slice(baseDir.length + 1));\n }\n }\n\n return files;\n}\n\n/**\n * Processes a template file with Handlebars\n * Files ending in .template have the extension removed and content processed\n * Other files are copied as-is\n */\nfunction processTemplate(\n content: string,\n variables: TemplateVariables,\n isTemplate: boolean,\n filePath?: string,\n): string {\n if (!isTemplate) {\n return content;\n }\n\n try {\n const template = Handlebars.compile(content);\n return template(variables);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n throw new Error(\n `Template processing failed${filePath ? ` for ${filePath}` : \"\"}: ${message}`,\n );\n }\n}\n\n/**\n * Gets the output filename for a template file\n * Removes .template extension if present\n */\ninterface TemplateSkillFile {\n readonly relativePath: string;\n readonly content: string;\n}\n\nasync function getSharedTemplateSkillFiles(): Promise<TemplateSkillFile[]> {\n const packageRoot = findFluidCliPackageRoot();\n const skillsRoot = join(packageRoot, \"template-skills\");\n const files = await getFiles(skillsRoot);\n\n return Promise.all(\n files.map(async (file) => ({\n relativePath: join(\"skills\", file),\n content: await readFile(join(skillsRoot, file), \"utf-8\"),\n })),\n );\n}\n\nfunction findFluidCliPackageRoot(): string {\n const workspacePackageRoot = join(findPackageRoot(), \"..\", \"core\");\n if (existsSync(join(workspacePackageRoot, \"template-skills\"))) {\n return workspacePackageRoot;\n }\n\n let dir = dirname(fileURLToPath(import.meta.resolve(\"@fluid-app/fluid-cli\")));\n while (!existsSync(join(dir, \"package.json\"))) {\n const parent = dirname(dir);\n if (parent === dir)\n throw new Error(\"Could not find Fluid CLI package root\");\n dir = parent;\n }\n return dir;\n}\n\nasync function writeOutputFiles(\n targetPath: string,\n outputFiles: readonly string[],\n content: string,\n): Promise<void> {\n for (const outputFile of outputFiles) {\n const destPath = join(targetPath, outputFile);\n const destDir = dirname(destPath);\n await mkdir(destDir, { recursive: true });\n await writeFile(destPath, content, \"utf-8\");\n }\n}\n\nfunction getOutputFilenames(filename: string): string[] {\n const outputFilename = getOutputFilename(filename);\n const normalized = outputFilename.replace(/\\\\/g, \"/\");\n\n if (normalized === \"AGENTS.md\") return [\"AGENTS.md\", \"CLAUDE.md\"];\n\n if (normalized.startsWith(\"skills/\")) {\n return [join(\".agents\", outputFilename), join(\".claude\", outputFilename)];\n }\n\n return [outputFilename];\n}\n\nfunction getOutputFilename(filename: string): string {\n if (filename.endsWith(\".template\")) {\n return filename.slice(0, -\".template\".length);\n }\n return filename;\n}\n\n/**\n * Copies a template directory to the target directory\n * Processes .template files with Handlebars\n */\nexport async function copyTemplate(\n templatePath: string,\n targetPath: string,\n variables: TemplateVariables,\n): Promise<void> {\n const files = await getFiles(templatePath);\n\n for (const file of files) {\n const sourcePath = join(templatePath, file);\n const isTemplate = file.endsWith(\".template\");\n const outputFiles = getOutputFilenames(file);\n\n // Read source file\n const content = await readFile(sourcePath, \"utf-8\");\n\n // Process and write\n const processed = processTemplate(\n content,\n variables,\n isTemplate,\n sourcePath,\n );\n\n for (const outputFile of outputFiles) {\n const destPath = join(targetPath, outputFile);\n const destDir = dirname(destPath);\n await mkdir(destDir, { recursive: true });\n await writeFile(destPath, processed, \"utf-8\");\n }\n }\n\n for (const skillFile of await getSharedTemplateSkillFiles()) {\n await writeOutputFiles(\n targetPath,\n getOutputFilenames(skillFile.relativePath),\n skillFile.content,\n );\n }\n}\n\n/**\n * Checks if a directory exists\n */\nexport async function directoryExists(path: string): Promise<boolean> {\n try {\n const stats = await stat(path);\n return stats.isDirectory();\n } catch {\n return false;\n }\n}\n\n/**\n * Checks if a file exists\n */\nexport async function fileExists(path: string): Promise<boolean> {\n try {\n const stats = await stat(path);\n return stats.isFile();\n } catch {\n return false;\n }\n}\n\n/**\n * Checks if a path exists (file or directory)\n */\nexport async function pathExists(path: string): Promise<boolean> {\n try {\n await stat(path);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Creates a directory\n */\nexport async function createDirectory(path: string): Promise<void> {\n await mkdir(path, { recursive: true });\n}\n\n/**\n * Reads the exact SDK version embedded by the build, or from the workspace\n * package while running source directly.\n */\nexport async function getSdkVersion(): Promise<string> {\n return getTemplatePackageVersion(\n [\"portal\", \"sdk\"],\n EMBEDDED_PORTAL_SDK_VERSION,\n );\n}\n\n/**\n * Builds a pnpm link: specifier for local scaffolds.\n *\n * link: keeps the dependency pointed at the workspace package source instead of\n * packing only package.json \"files\" entries like file: does. That lets Vite's\n * development export resolve the SDK source and its workspace dependencies.\n */\nexport function getLocalPackageLinkVersion(\n targetPath: string,\n packagePath: string,\n): string {\n const linkPath = (relative(targetPath, packagePath) || \".\").replace(\n /\\\\/g,\n \"/\",\n );\n return `link:${linkPath}`;\n}\n\n/**\n * Reads the exact CLI core version embedded by the build, or from the\n * workspace package while running source directly.\n *\n * This is separate from getSdkVersion because the CLI and portal SDK\n * are versioned independently.\n */\nexport async function getCliVersion(): Promise<string> {\n return getTemplatePackageVersion([\"cli\", \"core\"], EMBEDDED_CLI_CORE_VERSION);\n}\n\n/** Reads the independently versioned portal CLI plugin version. */\nexport async function getPortalCliVersion(): Promise<string> {\n return getTemplatePackageVersion(\n [\"cli\", \"portal\"],\n EMBEDDED_CLI_PORTAL_VERSION,\n );\n}\n\nasync function getTemplatePackageVersion(\n packagePathSegments: readonly string[],\n embeddedVersion: string | undefined,\n): Promise<string> {\n if (embeddedVersion) return embeddedVersion;\n\n try {\n const packageRoot = findPackageRoot();\n const packagesRoot = join(packageRoot, \"..\", \"..\");\n const packageJsonPath = join(\n packagesRoot,\n ...packagePathSegments,\n \"package.json\",\n );\n\n const content = await readFile(packageJsonPath, \"utf-8\");\n const pkg = JSON.parse(content) as { version?: unknown };\n if (typeof pkg.version !== \"string\") {\n throw new Error(`Missing version in ${packageJsonPath}`);\n }\n return pkg.version;\n } catch (error) {\n throw new Error(\n `Could not resolve the exact template package version for ${packagePathSegments.join(\"/\")}`,\n { cause: error },\n );\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Result-based variants for type-safe error handling\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Read a file's content with Result-based error handling\n */\nexport async function readFileSafe(\n path: string,\n): Promise<Result<string, FileSystemError>> {\n try {\n const content = await readFile(path, \"utf-8\");\n return success(content);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n return failure(\n createFsError(\n FILE_SYSTEM_ERRORS.readError,\n `Failed to read file: ${path}`,\n path,\n error,\n ),\n );\n }\n}\n\n/**\n * Write content to a file with Result-based error handling\n */\nexport async function writeFileSafe(\n path: string,\n content: string,\n): Promise<Result<void, FileSystemError>> {\n try {\n await writeFile(path, content, \"utf-8\");\n return success(undefined);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n return failure(\n createFsError(\n FILE_SYSTEM_ERRORS.writeError,\n `Failed to write file: ${path}`,\n path,\n error,\n ),\n );\n }\n}\n\n/**\n * Create a directory with Result-based error handling\n */\nexport async function createDirectorySafe(\n path: string,\n): Promise<Result<void, FileSystemError>> {\n try {\n await mkdir(path, { recursive: true });\n return success(undefined);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n return failure(\n createFsError(\n FILE_SYSTEM_ERRORS.writeError,\n `Failed to create directory: ${path}`,\n path,\n error,\n ),\n );\n }\n}\n\n/**\n * Copy a template directory with Result-based error handling\n */\nexport async function copyTemplateSafe(\n templatePath: string,\n targetPath: string,\n variables: Readonly<TemplateVariables>,\n): Promise<Result<void, FileSystemError>> {\n try {\n const files = await getFiles(templatePath);\n\n for (const file of files) {\n const sourcePath = join(templatePath, file);\n const isTemplateFile = file.endsWith(\".template\");\n const outputFiles = getOutputFilenames(file);\n\n // Read source file\n const content = await readFile(sourcePath, \"utf-8\");\n\n // Process and write\n const processed = processTemplate(\n content,\n variables,\n isTemplateFile,\n sourcePath,\n );\n\n for (const outputFile of outputFiles) {\n const destPath = join(targetPath, outputFile);\n const destDir = dirname(destPath);\n await mkdir(destDir, { recursive: true });\n await writeFile(destPath, processed, \"utf-8\");\n }\n }\n\n for (const skillFile of await getSharedTemplateSkillFiles()) {\n await writeOutputFiles(\n targetPath,\n getOutputFilenames(skillFile.relativePath),\n skillFile.content,\n );\n }\n\n return success(undefined);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n return failure(\n createFsError(\n FILE_SYSTEM_ERRORS.templateError,\n `Failed to copy template from ${templatePath} to ${targetPath}`,\n templatePath,\n error,\n ),\n );\n }\n}\n\n/**\n * Get SDK version with Result-based error handling\n * Unlike getSdkVersion, this returns an error instead of a fallback\n */\nexport async function getSdkVersionSafe(): Promise<\n Result<string, FileSystemError>\n> {\n try {\n // CLI lives at packages/cli/portal/, SDK at packages/portal/sdk/\n const packageRoot = findPackageRoot();\n const packagesRoot = join(packageRoot, \"..\", \"..\");\n const sdkPackagePath = join(packagesRoot, \"portal\", \"sdk\", \"package.json\");\n\n const content = await readFile(sdkPackagePath, \"utf-8\");\n const pkg = JSON.parse(content) as { version?: string };\n const version = pkg.version;\n\n if (version === undefined) {\n return failure(\n createFsError(\n FILE_SYSTEM_ERRORS.readError,\n \"SDK package.json does not contain a version field\",\n sdkPackagePath,\n ),\n );\n }\n\n return success(`^${version}`);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n return failure(\n createFsError(\n FILE_SYSTEM_ERRORS.fileNotFound,\n \"Could not find SDK package.json\",\n undefined,\n error,\n ),\n );\n }\n}\n","import { execa } from \"execa\";\n\n/**\n * Returns the install command for pnpm\n */\nexport function getInstallCommand(): string {\n return \"pnpm install\";\n}\n\n/**\n * Returns the run command for pnpm\n */\nexport function getRunCommand(script: string): string {\n return `pnpm run ${script}`;\n}\n\n/**\n * Runs a pnpm command in the specified directory\n */\nexport async function runPackageManager(\n args: string[],\n cwd: string,\n): Promise<void> {\n await execa(\"pnpm\", args, {\n cwd,\n stdio: \"inherit\",\n });\n}\n\n/**\n * Installs dependencies using pnpm\n */\nexport async function installDependencies(cwd: string): Promise<void> {\n await runPackageManager([\"install\"], cwd);\n}\n","/**\n * Git wrapper for the portal CLI — shells out to the system `git` via\n * execa. We deliberately do NOT bundle git or use a pure-JS impl; users\n * are expected to have it installed (the same stance as `fluid mist`).\n *\n * Security: a token-bearing `remote_url` (minted by the portal\n * `git_credential` endpoint) must never be logged, persisted, *or*\n * exposed in the process table. The token is stripped from the URL and\n * handed to git through a GIT_ASKPASS script via env vars\n * (FLUID_PORTAL_GIT_USER / FLUID_PORTAL_GIT_PASS) — so it never appears\n * in argv (visible to `ps aux` / `/proc/<pid>/cmdline`) and is never\n * written to `.git/config`.\n *\n * This mirrors `packages/cli/mist/src/lib/git.ts`. Kept portal-local for\n * now; if the widget/mist tracks extract a shared git lib we should\n * consume it instead (see overnight/track2-portal.md).\n */\n\nimport { chmodSync, existsSync, mkdirSync, writeFileSync } from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { execa, type ExecaError } from \"execa\";\n\nexport class GitError extends Error {\n readonly exitCode: number;\n\n constructor(message: string, exitCode = 1) {\n super(message);\n this.name = \"GitError\";\n this.exitCode = exitCode;\n }\n}\n\n/**\n * execa throws ENOENT when the git binary itself is missing — the single\n * most common failure on non-developer machines. Translate it into an\n * actionable message. Returns null for every other failure so call sites\n * keep their specific messages.\n */\nexport function gitMissingError(err: unknown): GitError | null {\n const code = (err as NodeJS.ErrnoException | undefined)?.code;\n if (code !== \"ENOENT\") return null;\n return new GitError(\n \"Git isn't installed (or isn't on your PATH), so this command can't run.\\n\" +\n \" Install it, then try again:\\n\" +\n \" macOS: xcode-select --install (or `brew install git`)\\n\" +\n \" Windows: https://git-scm.com/download/win\\n\" +\n \" Linux: use your package manager, e.g. `apt install git`\",\n 127,\n );\n}\n\n/**\n * Is `git` available on this machine? Cheap, never throws — used to\n * decide whether to take the GitHub-template path or fall back to the\n * bundled scaffold.\n */\nexport async function isGitAvailable(): Promise<boolean> {\n try {\n await execa(\"git\", [\"--version\"]);\n return true;\n } catch {\n return false;\n }\n}\n\n/** Confirm git is installed, throwing the friendly message otherwise. */\nexport async function ensureGitAvailable(): Promise<void> {\n if (!(await isGitAvailable())) {\n throw new GitError(\n \"Git isn't installed (or isn't on your PATH), so this command can't run.\\n\" +\n \" Install it from https://git-scm.com/downloads (or `brew install git`), then try again.\",\n );\n }\n}\n\n/**\n * Split a token-bearing remote URL into the userless URL + creds. Keeps\n * the token off git's argv: we pass `cleanUrl` to git and feed\n * `username` / `password` back through GIT_ASKPASS env vars.\n *\n * If the URL has no embedded credentials, returns empty strings — fine\n * for public repos where git won't invoke askpass at all.\n */\nfunction splitRemoteCredentials(remoteUrl: string): {\n cleanUrl: string;\n username: string;\n password: string;\n} {\n const u = new URL(remoteUrl);\n const username = decodeURIComponent(u.username);\n const password = decodeURIComponent(u.password);\n u.username = \"\";\n u.password = \"\";\n return { cleanUrl: u.toString(), username, password };\n}\n\n/**\n * Write a stub GIT_ASKPASS shell script to a per-process tmpdir and\n * return its path. The script contains no secret — it echoes back\n * whichever env var (FLUID_PORTAL_GIT_USER / FLUID_PORTAL_GIT_PASS)\n * matches git's prompt. The credentials only live in the spawned git\n * process's env for the duration of the call.\n *\n * One POSIX script for all platforms: Git for Windows executes\n * GIT_ASKPASS through its bundled sh, so a plain sh script works\n * everywhere. Idempotent: subsequent calls reuse the same path.\n */\nlet askpassPath: string | null = null;\nfunction ensureAskpassScript(): string {\n if (askpassPath && existsSync(askpassPath)) return askpassPath;\n const dir = path.join(os.tmpdir(), `fluid-portal-askpass-${process.pid}`);\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true, mode: 0o700 });\n const p = path.join(dir, \"askpass.sh\");\n writeFileSync(\n p,\n `#!/bin/sh\\ncase \"$1\" in\\n Username*) printf '%s' \"$FLUID_PORTAL_GIT_USER\" ;;\\n *) printf '%s' \"$FLUID_PORTAL_GIT_PASS\" ;;\\nesac\\n`,\n { mode: 0o700 },\n );\n chmodSync(p, 0o700);\n askpassPath = p;\n return askpassPath;\n}\n\n/**\n * Build the spawn env + cleaned URL for a credentialed git invocation.\n * - `cleanUrl`: URL with userinfo stripped — safe to pass as argv.\n * - `env`: parent env plus GIT_ASKPASS + FLUID_PORTAL_GIT_USER/PASS.\n * GIT_TERMINAL_PROMPT=0 prevents the TTY-prompt fallback if the\n * credentials are empty (auth should fail fast, not hang).\n */\nfunction gitAuthSpawn(remoteUrl: string): {\n cleanUrl: string;\n env: NodeJS.ProcessEnv;\n} {\n const { cleanUrl, username, password } = splitRemoteCredentials(remoteUrl);\n return {\n cleanUrl,\n env: {\n ...process.env,\n GIT_ASKPASS: ensureAskpassScript(),\n GIT_TERMINAL_PROMPT: \"0\",\n FLUID_PORTAL_GIT_USER: username,\n FLUID_PORTAL_GIT_PASS: password,\n },\n };\n}\n\n/** Map an execa failure to a GitError, preferring the friendly\n * git-missing message when the binary itself is absent. */\nfunction toGitError(err: unknown, verb: string): GitError {\n const missing = gitMissingError(err);\n if (missing) return missing;\n const e = err as ExecaError;\n return new GitError(\n `git ${verb} failed${e.exitCode != null ? ` (exit ${e.exitCode})` : \"\"}`,\n e.exitCode ?? 1,\n );\n}\n\n/**\n * `git clone <cleanUrl> <targetDir>`. When `remoteUrl` carries a token,\n * gitAuthSpawn strips it from the URL and re-injects it via GIT_ASKPASS\n * so it never appears in argv. `credential.helper=` stops git from\n * caching what askpass returned to any system credential store.\n *\n * `depth` shallow-clones (used by the create flow, which discards `.git`\n * afterwards anyway).\n */\nexport async function gitClone(\n remoteUrl: string,\n targetDir: string,\n opts: { depth?: number } = {},\n): Promise<void> {\n const { cleanUrl, env } = gitAuthSpawn(remoteUrl);\n const depthArgs = opts.depth ? [\"--depth\", String(opts.depth)] : [];\n try {\n await execa(\n \"git\",\n [\n \"-c\",\n \"credential.helper=\",\n \"clone\",\n \"--quiet\",\n ...depthArgs,\n cleanUrl,\n targetDir,\n ],\n { env, stdio: [\"ignore\", \"inherit\", \"inherit\"] },\n );\n } catch (err) {\n throw toGitError(err, \"clone\");\n }\n}\n\n/** Initialize a fresh repo (used after scaffolding so the project is\n * git-ready). Best-effort: never throws — a scaffold without a repo is\n * still usable, and a missing git binary is handled by the caller. */\nexport async function gitInit(cwd: string): Promise<boolean> {\n try {\n await execa(\"git\", [\"init\", \"--quiet\"], { cwd });\n return true;\n } catch {\n return false;\n }\n}\n\n/** Is `cwd` inside a git work tree? Cheap, never throws. */\nexport async function isGitRepo(cwd: string): Promise<boolean> {\n try {\n const { stdout } = await execa(\n \"git\",\n [\"rev-parse\", \"--is-inside-work-tree\"],\n { cwd },\n );\n return stdout.trim() === \"true\";\n } catch {\n return false;\n }\n}\n\n/** Read `git remote get-url origin` (or null if no origin / not a repo). */\nexport async function getOriginUrl(cwd: string): Promise<string | null> {\n try {\n const { stdout } = await execa(\"git\", [\"remote\", \"get-url\", \"origin\"], {\n cwd,\n });\n return stdout.trim();\n } catch {\n return null;\n }\n}\n\n/**\n * Are there uncommitted changes (working tree or index)? `git status\n * --porcelain` is empty when clean. Returns false (never throws) when\n * `cwd` isn't a repo.\n */\nexport async function hasUncommittedChanges(cwd: string): Promise<boolean> {\n try {\n const { stdout } = await execa(\"git\", [\"status\", \"--porcelain\"], { cwd });\n return stdout.trim().length > 0;\n } catch {\n return false;\n }\n}\n\n/**\n * Fetch `branch` from a token-bearing remote URL, writing FETCH_HEAD so\n * `gitAheadBehindVsFetchHead` is meaningful. Token comes through\n * GIT_ASKPASS, never argv or `.git/config`.\n */\nexport async function gitFetch(\n cwd: string,\n remoteUrl: string,\n branch = \"main\",\n): Promise<void> {\n const { cleanUrl, env } = gitAuthSpawn(remoteUrl);\n try {\n await execa(\n \"git\",\n [\"-c\", \"credential.helper=\", \"fetch\", \"--quiet\", cleanUrl, branch],\n { cwd, env },\n );\n } catch (err) {\n throw toGitError(err, \"fetch\");\n }\n}\n\n/**\n * Count commits in local HEAD vs FETCH_HEAD (after `gitFetch`). Returns\n * `{ahead, behind}`. `0/0` means \"in sync\" (or no FETCH_HEAD yet).\n */\nexport async function gitAheadBehindVsFetchHead(\n cwd: string,\n): Promise<{ ahead: number; behind: number }> {\n try {\n const { stdout } = await execa(\n \"git\",\n [\"rev-list\", \"--left-right\", \"--count\", \"HEAD...FETCH_HEAD\"],\n { cwd },\n );\n const [a, b] = stdout.trim().split(/\\s+/);\n const ahead = Number.parseInt(a ?? \"0\", 10);\n const behind = Number.parseInt(b ?? \"0\", 10);\n return {\n ahead: Number.isFinite(ahead) ? ahead : 0,\n behind: Number.isFinite(behind) ? behind : 0,\n };\n } catch {\n return { ahead: 0, behind: 0 };\n }\n}\n\n/** Does `cwd` have any commits yet? (false right after `git init`.) */\nexport async function hasCommits(cwd: string): Promise<boolean> {\n try {\n await execa(\"git\", [\"rev-parse\", \"--verify\", \"HEAD\"], { cwd });\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Does `branch` exist on the remote? Throws on transport/auth failure so\n * a network blip is never mistaken for an empty remote (which would skip\n * drift detection). Token via GIT_ASKPASS, never argv or .git/config.\n */\nexport async function gitRemoteBranchExists(\n cwd: string,\n remoteUrl: string,\n branch = \"main\",\n): Promise<boolean> {\n const { cleanUrl, env } = gitAuthSpawn(remoteUrl);\n try {\n const { stdout } = await execa(\n \"git\",\n [\n \"-c\",\n \"credential.helper=\",\n \"ls-remote\",\n \"--heads\",\n cleanUrl,\n `refs/heads/${branch}`,\n ],\n { cwd, env },\n );\n return stdout.trim().length > 0;\n } catch (err) {\n throw toGitError(err, \"ls-remote\");\n }\n}\n\n/**\n * Adopt `FETCH_HEAD` as the current (unborn) branch's base: point the\n * branch ref at it and load its tree into the index, leaving the working\n * tree untouched. The next auto-commit (run with `ignoreRemovals`) then\n * records the local project tree on top of the remote history — local\n * additions/edits win, and remote-only files survive because a\n * content-only local dir (e.g. populated by `fluid portal pull`) is no\n * statement about what the full repo tracks — so the push fast-forwards\n * instead of being rejected for unrelated history. Only call when\n * `hasCommits` is false; with local commits it would silently rewrite\n * the branch ref.\n * Mirrors the widget CLI's first-push adoption (the backend provisions\n * repos with an auto-init/template commit, so a fresh root commit is\n * always unrelated history).\n *\n * Returns the number of remote commits adopted.\n */\nexport async function gitAdoptFetchHead(cwd: string): Promise<number> {\n try {\n await execa(\"git\", [\"reset\", \"--mixed\", \"FETCH_HEAD\"], { cwd });\n const { stdout } = await execa(\n \"git\",\n [\"rev-list\", \"--count\", \"FETCH_HEAD\"],\n { cwd },\n );\n const count = Number.parseInt(stdout.trim(), 10);\n return Number.isFinite(count) ? count : 0;\n } catch (err) {\n throw toGitError(err, \"adopt remote history\");\n }\n}\n\n/**\n * Normalize a git remote URL to `host/owner/repo` for identity\n * comparison — strips credentials, protocol, `.git`, and trailing\n * slashes so a token-bearing minted URL compares equal to the stored\n * origin. Returns null when the URL doesn't parse.\n */\nexport function normalizedRepoIdentity(remoteUrl: string): string | null {\n // scp-style SSH remotes (`git@github.com:owner/repo.git`) aren't URLs —\n // normalize them by hand so an SSH origin still verifies against the\n // https minted URL.\n const scp = remoteUrl.match(/^(?:[^@/\\s]+@)?([^:/\\s]+):([^/].*)$/);\n if (scp?.[1] && scp[2] && !remoteUrl.includes(\"://\")) {\n const path = scp[2].replace(/\\.git$/, \"\").replace(/\\/+$/, \"\");\n return `${scp[1]}/${path}`.toLowerCase();\n }\n // Plain filesystem remotes (local bare repos — tests, offline mirrors):\n // identity is the normalized path itself.\n if (remoteUrl.startsWith(\"/\") || remoteUrl.startsWith(\".\")) {\n return remoteUrl\n .replace(/\\.git$/, \"\")\n .replace(/\\/+$/, \"\")\n .toLowerCase();\n }\n try {\n const u = new URL(remoteUrl);\n const path = u.pathname\n .replace(/\\.git$/, \"\")\n .replace(/\\/+$/, \"\")\n .replace(/^\\/+/, \"\");\n return `${u.host}/${path}`.toLowerCase();\n } catch {\n return null;\n }\n}\n\n/**\n * Do HEAD and FETCH_HEAD share any common ancestor? False for unrelated\n * histories — the guard that keeps a whole-tree auto-commit of the WRONG\n * repository from being pushed to a portal's remote. Never throws.\n */\nexport async function hasCommonAncestorWithFetchHead(\n cwd: string,\n): Promise<boolean> {\n try {\n await execa(\"git\", [\"merge-base\", \"HEAD\", \"FETCH_HEAD\"], { cwd });\n return true;\n } catch {\n return false;\n }\n}\n\n/** Fast-forward HEAD to FETCH_HEAD. Fails (non-zero exit) if not\n * fast-forwardable — callers check the ahead-count first. */\nexport async function gitMergeFastForward(cwd: string): Promise<void> {\n try {\n await execa(\"git\", [\"merge\", \"--ff-only\", \"FETCH_HEAD\"], { cwd });\n } catch (err) {\n throw toGitError(err, \"fast-forward merge\");\n }\n}\n\nexport interface GitAutoCommitOptions {\n /**\n * Stage with `git add --ignore-removal .` (new + modified files only)\n * instead of `git add -A`, so files present in the index but absent\n * from the working tree are NOT recorded as deletions. Used by the\n * first-push adoption path in git-sync: a directory populated by\n * `fluid portal pull` (content-only sync) has no idea what the full\n * repo tracks, so local additions/edits win but remote-only files are\n * preserved — deletions only ever ship from full checkouts.\n */\n readonly ignoreRemovals?: boolean;\n}\n\n/**\n * `git add -A && git commit -m <message>` (or, with\n * `{ ignoreRemovals: true }`, `git add --ignore-removal .` — see\n * GitAutoCommitOptions).\n *\n * `git add -A` intentionally stages the whole work tree: a portal's\n * provisioned repo tracks the entire project (it's seeded from the\n * starter template), and the \"publish my changes\" UX expects every edit\n * to ship — the same stance as `fluid mist push`. The exclusion boundary\n * is the project's `.gitignore` (the starter ignores `.env`,\n * `node_modules/`, `dist/`, `.portal-sync/`, logs, `*.tsbuildinfo`), so\n * generated output and local secrets stay out of the commit.\n *\n * Uses the repo's configured identity, else a synthetic Fluid bot\n * identity so the commit isn't rejected. \"nothing to commit\" is treated\n * as a no-op, not an error.\n */\nexport async function gitAutoCommit(\n cwd: string,\n message: string,\n options: GitAutoCommitOptions = {},\n): Promise<void> {\n const addArgs = options.ignoreRemovals\n ? [\"add\", \"--ignore-removal\", \".\"]\n : [\"add\", \"-A\"];\n try {\n await execa(\"git\", addArgs, { cwd });\n } catch (err) {\n throw toGitError(err, \"add\");\n }\n\n let hasName = false;\n let hasEmail = false;\n try {\n const r = await execa(\"git\", [\"config\", \"user.name\"], { cwd });\n hasName = r.stdout.trim().length > 0;\n } catch {\n // exit 1 from `git config <key>` just means the key is unset.\n }\n try {\n const r = await execa(\"git\", [\"config\", \"user.email\"], { cwd });\n hasEmail = r.stdout.trim().length > 0;\n } catch {\n // ditto.\n }\n const identityArgs = [\n ...(hasName ? [] : [\"-c\", \"user.name=Fluid Portal CLI\"]),\n ...(hasEmail ? [] : [\"-c\", \"user.email=portal@fluid.app\"]),\n ];\n try {\n // BOTH output streams are piped (not inherited) so the catch below\n // can actually inspect them — with \"inherit\", execa's e.stdout /\n // e.stderr are undefined and the documented \"nothing to commit is a\n // no-op\" contract can never match. Notably git prints \"no changes\n // added to commit\" to STDOUT, so piping stderr alone isn't enough.\n // The sync is meant to be invisible, so swallowing the commit\n // summary on success is fine.\n await execa(\"git\", [...identityArgs, \"commit\", \"-m\", message], {\n cwd,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n } catch (err) {\n const missing = gitMissingError(err);\n if (missing) throw missing;\n const e = err as ExecaError;\n // git prints \"nothing to commit\" on stdout or stderr depending on\n // version/porcelain state — check both captured streams. With\n // ignoreRemovals, a tree whose only pending changes are deletions\n // stages nothing, and git phrases that as \"no changes added to\n // commit\" instead — equally a no-op here.\n const streams = `${typeof e.stdout === \"string\" ? e.stdout : \"\"}\\n${typeof e.stderr === \"string\" ? e.stderr : \"\"}`;\n if (/nothing to commit|no changes added to commit/i.test(streams)) {\n return;\n }\n throw toGitError(err, \"commit\");\n }\n}\n\n/**\n * `git push <cleanUrl> HEAD:<branch>` — credentials supplied via\n * GIT_ASKPASS, never argv. `http.…/.extraheader=` clears any cached\n * Authorization header a CI-style config might inject.\n */\nexport async function gitPush(\n cwd: string,\n remoteUrl: string,\n branch = \"main\",\n): Promise<void> {\n const { cleanUrl, env } = gitAuthSpawn(remoteUrl);\n try {\n await execa(\n \"git\",\n [\n \"-c\",\n \"credential.helper=\",\n \"-c\",\n \"http.https://github.com/.extraheader=\",\n \"push\",\n cleanUrl,\n `HEAD:${branch}`,\n ],\n { cwd, env, stdio: [\"ignore\", \"inherit\", \"inherit\"] },\n );\n } catch (err) {\n throw toGitError(err, \"push\");\n }\n}\n","/**\n * GitHub-template scaffolding for `fluid portal create`.\n *\n * The canonical starter lives at github.com/Fluid-WeCommerce/portal-template\n * as a **fully rendered, runnable** project (GitHub's generate-from-template\n * copies files verbatim, so it can't contain `{{handlebars}}` placeholders).\n * The project name is baked in as a distinctive token (CANONICAL_PROJECT_NAME);\n * after cloning we re-substitute it with the user's chosen app name.\n *\n * This is an explicit opt-in create path. The bundled template is the\n * deterministic default, so git or network availability cannot change the\n * normal scaffold output.\n */\n\nimport {\n readdir,\n readFile,\n writeFile,\n rm,\n copyFile,\n stat,\n} from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { gitClone } from \"../lib/git.js\";\n\n/** Public HTTPS URL of the portal starter template repo. */\nexport const TEMPLATE_REPO_URL =\n \"https://github.com/Fluid-WeCommerce/portal-template.git\";\n\n/**\n * The project name baked into the rendered template repo. `fluid portal\n * create` replaces every occurrence with the user's app name after\n * cloning. Must match the name the template was rendered with — keep in\n * sync with the render used to publish the repo.\n */\nexport const CANONICAL_PROJECT_NAME = \"fluid-portal-starter\";\n\n/**\n * Files/dirs we never rewrite when re-substituting the project name.\n * The lockfile keys packages by import path (`.`), not the root name, so\n * touching it would only risk corruption for zero benefit.\n */\nconst SUBSTITUTION_SKIP = new Set([\".git\", \"node_modules\", \"pnpm-lock.yaml\"]);\n\n/** Extensions we treat as text and safe to rewrite. Everything else is\n * left byte-for-byte (avoids mangling any binary asset a future template\n * might carry). */\nconst TEXT_EXTENSIONS = new Set([\n \".ts\",\n \".tsx\",\n \".js\",\n \".jsx\",\n \".mjs\",\n \".cjs\",\n \".json\",\n \".md\",\n \".yml\",\n \".yaml\",\n \".css\",\n \".html\",\n \".txt\",\n]);\n\nfunction hasTextExtension(file: string): boolean {\n const dot = file.lastIndexOf(\".\");\n if (dot < 0) return false;\n return TEXT_EXTENSIONS.has(file.slice(dot));\n}\n\n/** Recursively list absolute file paths, skipping SUBSTITUTION_SKIP dirs. */\nasync function listFiles(dir: string): Promise<string[]> {\n const entries = await readdir(dir, { withFileTypes: true });\n const files: string[] = [];\n for (const entry of entries) {\n if (SUBSTITUTION_SKIP.has(entry.name)) continue;\n const full = join(dir, entry.name);\n if (entry.isDirectory()) {\n files.push(...(await listFiles(full)));\n } else {\n files.push(full);\n }\n }\n return files;\n}\n\n/** Escape a string for use in a RegExp. */\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\n/**\n * Replace every occurrence of the canonical project name with `appName`\n * across the cloned tree's text files. Only files that actually contain\n * the token are rewritten.\n */\nexport async function rewriteProjectName(\n targetPath: string,\n appName: string,\n): Promise<void> {\n if (appName === CANONICAL_PROJECT_NAME) return;\n const token = new RegExp(escapeRegExp(CANONICAL_PROJECT_NAME), \"g\");\n // listFiles already prunes the SUBSTITUTION_SKIP dirs/files.\n const files = await listFiles(targetPath);\n for (const file of files) {\n if (!hasTextExtension(file)) continue;\n const content = await readFile(file, \"utf-8\");\n if (!content.includes(CANONICAL_PROJECT_NAME)) continue;\n // Function replacement so `$` sequences in the app name are taken\n // literally instead of as replacement patterns ($&, $$, …).\n await writeFile(\n file,\n content.replace(token, () => appName),\n \"utf-8\",\n );\n }\n}\n\n/** Rewrite `.fluidrc` with the chosen CLI profile (the template ships an\n * empty profile). No-op when the file is absent. */\nexport async function writeFluidrcProfile(\n targetPath: string,\n profileName: string,\n): Promise<void> {\n const fluidrcPath = join(targetPath, \".fluidrc\");\n if (!existsSync(fluidrcPath)) return;\n await writeFile(\n fluidrcPath,\n JSON.stringify({ profile: profileName }, null, 2) + \"\\n\",\n \"utf-8\",\n );\n}\n\nexport interface ScaffoldFromGitHubOptions {\n readonly targetPath: string;\n readonly appName: string;\n readonly profileName: string;\n /** Override the template repo URL (tests / private mirrors). */\n readonly repoUrl?: string;\n}\n\n/**\n * Clone the GitHub starter template into `targetPath`, discard its git\n * history, re-substitute the project name, wire `.fluidrc`, and copy\n * `.env.example` → `.env`.\n *\n * Throws (GitError or otherwise) on any failure so the explicit remote\n * template request is reported as a failure.\n */\nexport async function scaffoldFromGitHub(\n options: ScaffoldFromGitHubOptions,\n): Promise<void> {\n const { targetPath, appName, profileName } = options;\n const repoUrl = options.repoUrl ?? TEMPLATE_REPO_URL;\n\n // Shallow clone — we throw the history away immediately.\n await gitClone(repoUrl, targetPath, { depth: 1 });\n\n // Drop the template's git history so the new project starts clean.\n await rm(join(targetPath, \".git\"), { recursive: true, force: true });\n\n await rewriteProjectName(targetPath, appName);\n await writeFluidrcProfile(targetPath, profileName);\n\n // Copy .env.example → .env so dotenv works out of the box (matches the\n // bundled scaffold behaviour).\n const envExample = join(targetPath, \".env.example\");\n try {\n if ((await stat(envExample)).isFile()) {\n await copyFile(envExample, join(targetPath, \".env\"));\n }\n } catch {\n // No .env.example — nothing to seed.\n }\n}\n","import { Command } from \"commander\";\nimport type { PluginContext } from \"@fluid-app/fluid-cli\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport { join, dirname, relative, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { copyFile, rm } from \"node:fs/promises\";\nimport type { CreateOptions, ProjectConfig } from \"../types.js\";\nimport { promptProjectConfig } from \"../utils/prompts.js\";\nimport {\n getTemplatePaths,\n copyTemplate,\n directoryExists,\n createDirectory,\n getSdkVersion,\n getCliVersion,\n getPortalCliVersion,\n fileExists,\n getLocalPackageLinkVersion,\n} from \"../utils/file-system.js\";\nimport {\n installDependencies,\n getRunCommand,\n} from \"../utils/package-manager.js\";\nimport { isGitAvailable } from \"../lib/git.js\";\nimport { scaffoldFromGitHub } from \"../utils/scaffold-github.js\";\n\nexport interface CreatePortalProjectDependencies {\n readonly installDependencies: typeof installDependencies;\n readonly isGitAvailable: typeof isGitAvailable;\n readonly scaffoldFromGitHub: typeof scaffoldFromGitHub;\n}\n\nconst defaultCreatePortalProjectDependencies = {\n installDependencies,\n isGitAvailable,\n scaffoldFromGitHub,\n} satisfies CreatePortalProjectDependencies;\n\nexport const createCommand: Command = new Command(\"create\")\n .description(\"Create a new Fluid portal application\")\n .argument(\"<app-name>\", \"Name of the application to create\")\n .option(\n \"--skip-install\",\n \"Create the scaffold only; skip dependency installation\",\n )\n .option(\n \"-o, --output-dir <dir>\",\n \"Directory to create the project in (defaults to cwd)\",\n )\n .option(\n \"--local\",\n \"Use local monorepo packages via file: links (for development testing)\",\n )\n .option(\n \"--remote-template\",\n \"Use the GitHub starter template instead of the bundled template\",\n )\n .action(async (appName: string, options: CreateOptions) => {\n try {\n console.log();\n console.log(chalk.bold(\"Creating a new Fluid portal application\"));\n console.log();\n\n // Validate app name\n if (!/^[a-z0-9-]+$/.test(appName)) {\n console.error(\n chalk.red(\n \"Error: App name must contain only lowercase letters, numbers, and hyphens\",\n ),\n );\n process.exit(1);\n }\n\n // Check if directory already exists\n const targetPath = join(\n resolve(options.outputDir ?? process.cwd()),\n appName,\n );\n if (await directoryExists(targetPath)) {\n console.error(\n chalk.red(`Error: Directory \"${appName}\" already exists`),\n );\n process.exit(1);\n }\n\n // Prompt for configuration\n const config = await promptProjectConfig(appName, options);\n if (!config) {\n console.log();\n console.log(chalk.yellow(\"Cancelled\"));\n process.exit(0);\n }\n\n console.log();\n await createPortalProject(appName, options, config);\n } catch (error) {\n console.log();\n console.log(\n chalk.red(\"Error:\") +\n \" \" +\n (error instanceof Error ? error.message : String(error)),\n );\n console.log();\n process.exit(1);\n }\n });\n\nexport async function createPortalProject(\n appName: string,\n options: CreateOptions,\n config: ProjectConfig,\n dependencies: CreatePortalProjectDependencies = defaultCreatePortalProjectDependencies,\n): Promise<void> {\n const targetPath = join(resolve(options.outputDir ?? process.cwd()), appName);\n const isLocal = options.local === true;\n\n if (options.remoteTemplate === true) {\n if (isLocal) {\n throw new Error(\"--remote-template cannot be combined with --local\");\n }\n if (config.selectedPages.length > 0) {\n throw new Error(\n \"--remote-template cannot be used with optional page selections\",\n );\n }\n if (!(await dependencies.isGitAvailable())) {\n throw new Error(\n \"The remote template requires Git. Remove --remote-template to use the bundled template.\",\n );\n }\n\n const spinner = ora(\"Fetching starter template from GitHub...\").start();\n try {\n await dependencies.scaffoldFromGitHub({\n targetPath,\n appName: config.name,\n profileName: config.profileName,\n });\n spinner.succeed(\"Fetched starter template\");\n } catch (error) {\n spinner.fail(\"Failed to fetch the GitHub starter template\");\n await rm(targetPath, { recursive: true, force: true }).catch(() => {});\n throw error;\n }\n } else {\n await scaffoldFromBundled(targetPath, config, isLocal);\n }\n\n if (config.installDeps) {\n const installSpinner = ora(\"Installing dependencies with pnpm...\").start();\n try {\n await dependencies.installDependencies(targetPath);\n installSpinner.succeed(\"Installed dependencies\");\n } catch (error) {\n installSpinner.fail(\"Failed to install dependencies\");\n console.log();\n console.log(chalk.yellow(`The scaffold was retained at ${targetPath}.`));\n console.log(chalk.yellow(\"Install dependencies manually to continue:\"));\n console.log(chalk.cyan(` cd ${formatCdPath(targetPath)}`));\n console.log(chalk.cyan(\" pnpm install\"));\n throw error;\n }\n }\n\n console.log();\n if (!config.installDeps) {\n console.log(\n chalk.yellow(\n \"Scaffold only: dependencies were not installed and must be installed before development.\",\n ),\n );\n console.log();\n }\n console.log(chalk.green.bold(\"Success!\") + ` Created ${chalk.cyan(appName)}`);\n console.log();\n console.log(\"Next steps:\");\n console.log();\n console.log(chalk.cyan(` cd ${formatCdPath(targetPath)}`));\n if (!config.installDeps) {\n console.log(chalk.cyan(\" pnpm install\"));\n }\n console.log(chalk.cyan(` ${getRunCommand(\"dev\")}`));\n console.log();\n console.log(\n \"Then open \" + chalk.cyan(\"http://localhost:5173\") + \" in your browser.\",\n );\n console.log(\n chalk.dim(\n \" (port may differ if 5173 is in use — check the dev server output)\",\n ),\n );\n console.log();\n if (!config.profileName) {\n console.log(\n chalk.yellow(\n \" Run \" +\n chalk.cyan(\"fluid login\") +\n \" and update \" +\n chalk.cyan(\".fluidrc\") +\n \" with your profile name.\",\n ),\n );\n console.log();\n }\n console.log(\n \"Run \" +\n chalk.cyan(\"pnpm pull\") +\n \" and edit \" +\n chalk.cyan(\"portal/\") +\n \" JSON to customize your portal definition.\",\n );\n console.log();\n}\n\nfunction formatCdPath(targetPath: string): string {\n return relative(process.cwd(), targetPath) || \".\";\n}\n\n/**\n * Scaffold from the bundled base + overlay templates (Handlebars). Used\n * by default, or with `--local`\n * (which rewrites SDK/core deps to workspace `file:`/`link:` paths so the\n * generated project resolves unpublished monorepo source).\n *\n * Throws on failure; the caller reports and exits.\n */\nasync function scaffoldFromBundled(\n targetPath: string,\n config: ProjectConfig,\n isLocal: boolean,\n): Promise<void> {\n // Get template paths (base + overlay)\n const templatePaths = getTemplatePaths(\"starter\");\n if (!(await directoryExists(templatePaths.base))) {\n throw new Error(\"Base template not found\");\n }\n if (!(await directoryExists(templatePaths.overlay))) {\n throw new Error(\"Starter template not found\");\n }\n\n // Get package versions (SDK and CLI are versioned independently)\n let sdkVersion: string;\n let localCoreVersion: string | undefined;\n const [cliVersion, portalCliVersion] = await Promise.all([\n getCliVersion(),\n getPortalCliVersion(),\n ]);\n\n if (isLocal) {\n // Resolve relative file: paths so the generated project is portable\n // within the same monorepo clone (works regardless of absolute\n // location). The portal CLI lives at\n // packages/cli/portal/dist/index.mjs\n // so going up four levels lands us at the monorepo root, then\n // `packages/portal/{sdk,core}` is the canonical location for the\n // SDK + core packages.\n const currentDir = dirname(fileURLToPath(import.meta.url));\n const monorepoRoot = join(currentDir, \"..\", \"..\", \"..\", \"..\");\n const sdkPath = join(monorepoRoot, \"packages\", \"portal\", \"sdk\");\n const corePath = join(monorepoRoot, \"packages\", \"portal\", \"core\");\n\n if (\n !(await directoryExists(sdkPath)) ||\n !(await directoryExists(corePath))\n ) {\n throw new Error(\n \"--local requires running from within the fluid-mono monorepo\\n\" +\n \" Could not find packages/portal/sdk or packages/portal/core\",\n );\n }\n\n sdkVersion = getLocalPackageLinkVersion(targetPath, sdkPath);\n localCoreVersion = getLocalPackageLinkVersion(targetPath, corePath);\n console.log(chalk.cyan(\" Using local packages (--local mode)\"));\n } else {\n sdkVersion = await getSdkVersion();\n }\n\n // Create project directory\n const spinner = ora(\"Creating project directory...\").start();\n try {\n await createDirectory(targetPath);\n spinner.succeed(\"Created project directory\");\n } catch (error) {\n spinner.fail(\"Failed to create project directory\");\n throw error;\n }\n\n // Copy base template first, then overlay template-specific files on top\n const templateVariables = {\n projectName: config.name,\n sdkVersion,\n localCoreVersion,\n cliVersion,\n portalCliVersion,\n selectedPages: config.selectedPages,\n hasSelectedPages: config.selectedPages.length > 0,\n profileName: config.profileName,\n };\n\n spinner.start(\"Copying template files...\");\n try {\n await copyTemplate(templatePaths.base, targetPath, templateVariables);\n await copyTemplate(templatePaths.overlay, targetPath, templateVariables);\n\n // Copy .env.example → .env so dotenv works out of the box\n const envExamplePath = join(targetPath, \".env.example\");\n if (await fileExists(envExamplePath)) {\n await copyFile(envExamplePath, join(targetPath, \".env\"));\n }\n\n spinner.succeed(\"Copied template files\");\n } catch (error) {\n spinner.fail(\"Failed to copy template files\");\n throw error;\n }\n}\n\nexport function registerCreateCommand(ctx: PluginContext): void {\n ctx.program.addCommand(createCommand);\n}\n","/**\n * `fluid portal dev` command\n *\n * Starts the Vite development server with the portal dev plugin,\n * which intercepts manifest API requests and serves content from\n * the local `portal/` directory.\n *\n * If no `portal/` directory exists, prompts the user to run `fluid portal pull`\n * or auto-pulls if they are logged in.\n */\n\nimport { Command } from \"commander\";\nimport type { PluginContext } from \"@fluid-app/fluid-cli\";\nimport chalk from \"chalk\";\nimport { execa } from \"execa\";\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { DevOptions } from \"../types.js\";\n\nconst PORTAL_DIR = \"portal\";\n\n/**\n * Check if the portal directory exists and has content files.\n * Returns true if at minimum `portal/definition.json` exists.\n */\nfunction hasPortalContent(cwd: string): boolean {\n return existsSync(join(cwd, PORTAL_DIR, \"definition.json\"));\n}\n\n/**\n * Preflight check: does `<cwd>/node_modules` actually contain what this\n * command is about to `import()` (`vite`) and inject into the config\n * (`@fluid-app/portal-sdk`, pulled in transitively by the vite plugins)?\n *\n * Without this, a project whose install failed or was never run (no\n * `node_modules` at all) produces a cascade of confusing failures instead\n * of one clear message: `createServer()` below throws \"failed to load\n * config from .../vite.config.ts\", we catch that and warn \"falling back to\n * `vite`\", and the fallback then dies with a raw\n * `ERR_MODULE_NOT_FOUND: Cannot find package 'vite'` — never once saying\n * the actual problem is a missing `pnpm install`.\n */\nexport function checkDependenciesInstalled(\n cwd: string,\n): { ok: true } | { ok: false; message: string } {\n const nodeModulesPath = join(cwd, \"node_modules\");\n const hasVite = existsSync(join(nodeModulesPath, \"vite\"));\n const hasPortalSdk = existsSync(\n join(nodeModulesPath, \"@fluid-app\", \"portal-sdk\"),\n );\n\n if (existsSync(nodeModulesPath) && hasVite && hasPortalSdk) {\n return { ok: true };\n }\n\n return {\n ok: false,\n message:\n `Dependencies aren't installed in ${cwd} — run \\`pnpm install\\` ` +\n \"(or reopen the project in Mist Desktop, which installs \" +\n \"automatically), then retry.\",\n };\n}\n\n/**\n * Attempt to auto-pull portal content by invoking the pull command's action.\n * Falls back to a helpful error message if pull is not possible.\n */\nasync function autoPull(cwd: string): Promise<boolean> {\n console.log();\n console.log(\n chalk.yellow(\"No portal/ directory found.\") +\n \" Attempting to pull content...\",\n );\n console.log();\n\n try {\n // Dynamically import the pull command to avoid circular deps at module level.\n // Auth is handled internally by the pull command via stored CLI credentials\n // (getAuthToken / getActiveProfile), so no explicit token args are needed.\n // Note: the pull command may call process.exit(1) on failure (e.g. auth\n // errors), which will terminate the process rather than throwing. Use\n // --skip-pull to bypass this if auto-pull causes issues.\n const { pullCommand } = await import(\"./pull.js\");\n\n await pullCommand.parseAsync([], { from: \"user\" });\n\n // Verify content was pulled\n return hasPortalContent(cwd);\n } catch (err) {\n console.log();\n console.log(\n chalk.red(\"Auto-pull failed: \") +\n (err instanceof Error ? err.message : String(err)),\n );\n console.log();\n console.log(\n \"Run \" +\n chalk.cyan(\"fluid portal pull\") +\n \" manually to set up local content.\",\n );\n console.log();\n return false;\n }\n}\n\nexport const devCommand: Command = new Command(\"dev\")\n .description(\"Start the development server with local portal content serving\")\n .option(\"-p, --port <port>\", \"Port to run the dev server on\", \"5173\")\n .option(\"--host\", \"Expose the dev server to the network\")\n .option(\"--skip-pull\", \"Skip auto-pull if portal/ directory is missing\")\n .action(async (options: DevOptions & { skipPull?: boolean }) => {\n const cwd = process.cwd();\n\n // Check if we're in a Fluid project\n const packageJsonPath = join(cwd, \"package.json\");\n if (!existsSync(packageJsonPath)) {\n console.error(\n chalk.red(\"Error: No package.json found in current directory\"),\n );\n console.error(\n chalk.yellow(\"Make sure you're in a Fluid project directory\"),\n );\n process.exit(1);\n }\n\n // Check for vite.config\n const viteConfigPath = join(cwd, \"vite.config.ts\");\n if (!existsSync(viteConfigPath)) {\n console.error(chalk.red(\"Error: No vite.config.ts found\"));\n console.error(\n chalk.yellow(\"This command must be run from a Fluid project directory\"),\n );\n process.exit(1);\n }\n\n // ── Dependency preflight ──────────────────────────────────────────\n // Must run before anything below attempts to load `vite` or the\n // project's vite.config — those failures are confusing (stack\n // traces, ERR_MODULE_NOT_FOUND) where this one is not.\n const depsCheck = checkDependenciesInstalled(cwd);\n if (!depsCheck.ok) {\n console.error(chalk.red(`Error: ${depsCheck.message}`));\n process.exit(1);\n }\n\n // ── Auto-pull check ────────────────────────────────────────────────\n if (!hasPortalContent(cwd) && !options.skipPull) {\n const pulled = await autoPull(cwd);\n if (!pulled) {\n console.error(\n chalk.red(\"Cannot start dev server without portal content.\"),\n );\n console.error(\n chalk.yellow(\n \"Run \" +\n chalk.cyan(\"fluid portal pull\") +\n \" to download content first.\",\n ),\n );\n process.exit(1);\n }\n }\n\n if (hasPortalContent(cwd)) {\n console.log();\n console.log(\n chalk.green(\"Portal dev mode: \") +\n \"local content from \" +\n chalk.cyan(\"portal/\") +\n \" will be served\",\n );\n console.log(\n chalk.gray(\n \" Manifest requests intercepted at /api/app/manifest (+ /api/fluid_os/definitions/active)\",\n ),\n );\n console.log(\n chalk.gray(\" File changes in portal/ will trigger a page reload\"),\n );\n }\n\n console.log();\n console.log(chalk.bold(\"Starting development server...\"));\n console.log();\n\n // Fallback args for the degraded path (project's own `vite`).\n const viteArgs = [\"vite\"];\n if (options.port) viteArgs.push(\"--port\", String(options.port));\n if (options.host) viteArgs.push(\"--host\");\n\n // Start Vite programmatically and INJECT the portal dev plugins here, so\n // a portal never has to wire portalDevPlugin/backendDevPlugin into its own\n // vite.config — this command \"just serves the local portal/\". The user's\n // vite.config (react, tailwind, build output) is still loaded and merged\n // underneath. Falls back to spawning the project's `vite` if the\n // programmatic path fails, so behavior degrades rather than breaks.\n // Number.isFinite (not `|| 5173`) so an explicit `--port 0` (\"pick a free\n // port\") is honored rather than coerced back to the default.\n const parsedPort = Number(options.port);\n const port = Number.isFinite(parsedPort) ? parsedPort : 5173;\n\n try {\n const { createServer } = await import(\"vite\");\n const { portalDevPlugin } =\n await import(\"../vite-plugin/portal-dev-plugin.js\");\n const { backendDevPlugin } =\n await import(\"../vite-plugin/backend-dev-plugin.js\");\n\n // No inline `root` or `server.proxy` here: inline config takes\n // precedence over the project's vite.config, so setting them would\n // silently override a project's own root (e.g. root: \"web\") or a\n // local/staging /api proxy. portalDevPlugin's config() hook provides\n // the /api proxy fallback — resolved from the project's env, yielding\n // to any proxy the project already defines.\n const server = await createServer({\n configFile: viteConfigPath,\n envPrefix: [\"VITE_\", \"NEXT_PUBLIC_\"],\n plugins: [portalDevPlugin(), backendDevPlugin()],\n server: {\n port,\n host: options.host ? true : undefined,\n },\n });\n\n await server.listen();\n server.printUrls();\n\n const shutdown = () => {\n void server.close().finally(() => process.exit(0));\n };\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n\n // Keep the process alive until a signal tears the server down.\n await new Promise<never>(() => {});\n } catch (error) {\n console.warn(\n chalk.yellow(\n \"Could not start the enriched dev server; falling back to `vite`. \" +\n \"Local portal content requires portalDevPlugin in vite.config for the fallback path.\",\n ),\n error instanceof Error ? error.message : String(error),\n );\n try {\n await execa(\"pnpm\", viteArgs, { cwd, stdio: \"inherit\" });\n } catch (fallbackErr) {\n // execa v8 sets `signal` (not `code`) when killed by a signal. Treat\n // both SIGINT and SIGTERM (container/Mist orchestration) as clean\n // shutdowns, matching the programmatic path above.\n const execaError = fallbackErr as { signal?: string };\n if (execaError.signal === \"SIGINT\" || execaError.signal === \"SIGTERM\") {\n return;\n }\n console.error(chalk.red(\"Development server exited with an error\"));\n process.exit(1);\n }\n }\n });\n\nexport function registerDevCommand(ctx: PluginContext): void {\n ctx.program.addCommand(devCommand);\n}\n","import { Command } from \"commander\";\nimport type { PluginContext } from \"@fluid-app/fluid-cli\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport { execa } from \"execa\";\nimport { existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { BuildOptions } from \"../types.js\";\n\nexport const buildCommand: Command = new Command(\"build\")\n .description(\"Build the application for production\")\n .option(\"-o, --out-dir <dir>\", \"Output directory\", \"dist\")\n .action(async (options: BuildOptions) => {\n const cwd = process.cwd();\n\n // Check if we're in a Fluid project\n const packageJsonPath = join(cwd, \"package.json\");\n if (!existsSync(packageJsonPath)) {\n console.error(\n chalk.red(\"Error: No package.json found in current directory\"),\n );\n console.error(\n chalk.yellow(\"Make sure you're in a Fluid project directory\"),\n );\n process.exit(1);\n }\n\n // Check for vite.config\n const viteConfigPath = join(cwd, \"vite.config.ts\");\n if (!existsSync(viteConfigPath)) {\n console.error(chalk.red(\"Error: No vite.config.ts found\"));\n console.error(\n chalk.yellow(\"This command must be run from a Fluid project directory\"),\n );\n process.exit(1);\n }\n\n console.log();\n console.log(chalk.bold(\"Building for production...\"));\n console.log();\n\n const spinner = ora(\"Building...\").start();\n\n try {\n // Run the project's build script\n await execa(\"pnpm\", [\"run\", \"build\"], {\n cwd,\n stdio: \"pipe\",\n });\n\n spinner.succeed(\"Build completed\");\n\n const outDir = options.outDir ?? \"dist\";\n console.log();\n console.log(`Output written to ${chalk.cyan(outDir)}/`);\n console.log();\n console.log(\"To preview the build locally:\");\n console.log(chalk.cyan(\" pnpm vite preview\"));\n console.log();\n } catch (error) {\n spinner.fail(\"Build failed\");\n const execaError = error as { stderr?: string };\n if (execaError.stderr) {\n console.error(execaError.stderr);\n }\n process.exit(1);\n }\n });\n\nexport function registerBuildCommand(ctx: PluginContext): void {\n ctx.program.addCommand(buildCommand);\n}\n","/**\n * Cross-reference validation and change categorization utilities for the push command.\n *\n * Extracted into a standalone utility so that pure logic can be tested\n * without pulling in CLI dependencies (ora, chalk, prompts, etc.).\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport { join, basename } from \"node:path\";\nimport { existsSync, readdirSync } from \"node:fs\";\n\nimport type { PortalMappings } from \"./mappings.js\";\nimport type { SnapshotDiff } from \"./snapshot.js\";\nimport type {\n LocalNavigation,\n LocalNavigationItem,\n LocalProfile,\n} from \"./transform.js\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Categorize changed files by resource type. */\nexport interface CategorizedChanges {\n readonly screens: { new: string[]; changed: string[]; deleted: string[] };\n readonly themes: { new: string[]; changed: string[]; deleted: string[] };\n readonly navigations: { new: string[]; changed: string[]; deleted: string[] };\n readonly profiles: { new: string[]; changed: string[]; deleted: string[] };\n}\n\n/** A validation error found during cross-reference checking. */\nexport interface ValidationError {\n readonly file: string;\n readonly message: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Extract the slug from a file path (e.g., \"screens/home.json\" -> \"home\").\n */\nexport function slugFromPath(filePath: string): string {\n return basename(filePath, \".json\");\n}\n\n/**\n * Extract the resource type directory from a file path (e.g., \"screens/home.json\" -> \"screens\").\n */\nfunction resourceTypeFromPath(\n filePath: string,\n): \"screens\" | \"themes\" | \"navigations\" | \"profiles\" | null {\n const dir = filePath.split(\"/\")[0];\n if (\n dir === \"screens\" ||\n dir === \"themes\" ||\n dir === \"navigations\" ||\n dir === \"profiles\"\n ) {\n return dir;\n }\n return null;\n}\n\n/**\n * Read and parse a JSON file from the portal directory.\n */\nasync function readPortalFile<T>(\n portalDir: string,\n relativePath: string,\n): Promise<T> {\n const filePath = join(portalDir, relativePath);\n const content = await readFile(filePath, \"utf-8\");\n return JSON.parse(content) as T;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Change categorization\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Categorize a snapshot diff into resource-type-specific change lists.\n */\nexport function categorizeChanges(diff: SnapshotDiff): CategorizedChanges {\n const result: CategorizedChanges = {\n screens: { new: [], changed: [], deleted: [] },\n themes: { new: [], changed: [], deleted: [] },\n navigations: { new: [], changed: [], deleted: [] },\n profiles: { new: [], changed: [], deleted: [] },\n };\n\n for (const file of diff.new) {\n const type = resourceTypeFromPath(file);\n if (type) result[type].new.push(file);\n }\n for (const file of diff.changed) {\n const type = resourceTypeFromPath(file);\n if (type) result[type].changed.push(file);\n }\n for (const file of diff.deleted) {\n const type = resourceTypeFromPath(file);\n if (type) result[type].deleted.push(file);\n }\n\n return result;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Cross-reference validation\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Validate that all cross-references between local portal files are valid.\n *\n * Checks:\n * - Navigation items' \"screen\" slugs reference existing screen files or mappings\n * - Profile \"navigation\" and \"mobile_navigation\" slugs reference existing nav files or mappings\n * - Profile \"themes\" slugs reference existing theme files or mappings\n */\nexport async function validateCrossReferences(\n portalDir: string,\n mappings: PortalMappings,\n changes: CategorizedChanges,\n): Promise<ValidationError[]> {\n const errors: ValidationError[] = [];\n\n // Build sets of valid slugs (existing mappings + local files on disk)\n const validScreenSlugs = buildValidSlugsSet(portalDir, \"screens\", mappings);\n const validNavSlugs = buildValidSlugsSet(portalDir, \"navigations\", mappings);\n const validThemeSlugs = buildValidSlugsSet(portalDir, \"themes\", mappings);\n\n // Remove deleted resource slugs from valid sets\n for (const file of changes.screens.deleted) {\n validScreenSlugs.delete(slugFromPath(file));\n }\n for (const file of changes.navigations.deleted) {\n validNavSlugs.delete(slugFromPath(file));\n }\n for (const file of changes.themes.deleted) {\n validThemeSlugs.delete(slugFromPath(file));\n }\n\n // Validate navigation files (new + changed)\n const navFilesToCheck = [\n ...changes.navigations.new,\n ...changes.navigations.changed,\n ];\n for (const file of navFilesToCheck) {\n try {\n const nav = await readPortalFile<LocalNavigation>(portalDir, file);\n validateNavigationItems(\n nav.navigation_items,\n file,\n validScreenSlugs,\n errors,\n );\n } catch {\n errors.push({ file, message: \"Failed to read navigation file\" });\n }\n }\n\n // Validate profile files (new + changed)\n const profileFilesToCheck = [\n ...changes.profiles.new,\n ...changes.profiles.changed,\n ];\n for (const file of profileFilesToCheck) {\n try {\n const profile = await readPortalFile<LocalProfile>(portalDir, file);\n\n if (profile.navigation && !validNavSlugs.has(profile.navigation)) {\n errors.push({\n file,\n message: `References navigation \"${profile.navigation}\" which does not exist`,\n });\n }\n\n if (\n profile.mobile_navigation &&\n !validNavSlugs.has(profile.mobile_navigation)\n ) {\n errors.push({\n file,\n message: `References mobile_navigation \"${profile.mobile_navigation}\" which does not exist`,\n });\n }\n\n for (const themeSlug of profile.themes) {\n if (!validThemeSlugs.has(themeSlug)) {\n errors.push({\n file,\n message: `References theme \"${themeSlug}\" which does not exist`,\n });\n }\n }\n } catch {\n errors.push({ file, message: \"Failed to read profile file\" });\n }\n }\n\n return errors;\n}\n\n/**\n * Build a set of valid slugs for a resource type by combining\n * existing mapping slugs with local file slugs on disk.\n */\nfunction buildValidSlugsSet(\n portalDir: string,\n resourceType: \"screens\" | \"themes\" | \"navigations\" | \"profiles\",\n mappings: PortalMappings,\n): Set<string> {\n const slugs = new Set<string>();\n\n // Add all slugs from mappings\n for (const slug of Object.keys(mappings[resourceType])) {\n slugs.add(slug);\n }\n\n // Add slugs from local files on disk\n const dir = join(portalDir, resourceType);\n if (existsSync(dir)) {\n try {\n const entries = readdirSync(dir);\n for (const entry of entries) {\n if (entry.endsWith(\".json\")) {\n slugs.add(basename(entry, \".json\"));\n }\n }\n } catch {\n // Directory doesn't exist or can't be read — skip\n }\n }\n\n return slugs;\n}\n\n/**\n * Recursively validate navigation item screen references.\n */\nfunction validateNavigationItems(\n items: LocalNavigationItem[],\n file: string,\n validScreenSlugs: Set<string>,\n errors: ValidationError[],\n): void {\n for (const item of items) {\n if (item.screen && !validScreenSlugs.has(item.screen)) {\n errors.push({\n file,\n message: `Navigation item \"${item.label ?? \"(unlabeled)\"}\" references screen \"${item.screen}\" which does not exist`,\n });\n }\n if (item.children && item.children.length > 0) {\n validateNavigationItems(item.children, file, validScreenSlugs, errors);\n }\n }\n}\n","/**\n * Portal git-credential adapter (the \"invisible git\" seam).\n *\n * Mints a short-lived (~1h) remote URL with an embedded installation\n * token for the portal's provisioned GitHub repo. The URL is used\n * immediately for one git op and discarded — NEVER persisted or logged.\n *\n * The backend endpoint (GitSync phase 006, portal adapter) mirrors the\n * widget contract exactly:\n *\n * POST <endpoint> -> 201 { git_credential: { remote_url, expires_at }, status, meta }\n * errors: 409 not_provisioned/not_connected, 502 vendor_error, 422 else\n * { error: { message, details: { type } }, status, meta }\n *\n * The exact path is still being finalized as 006 lands, so it lives in\n * one constant here. Until the endpoint ships to prod a request 404s —\n * which we surface as `unavailable` so callers degrade gracefully\n * (warn + skip the git sync, proceed with the normal content push).\n */\n\n/** Minted credential — token embedded in `remote_url`. Treat as secret. */\nexport interface PortalGitCredential {\n readonly remote_url: string;\n readonly expires_at: string;\n}\n\n/**\n * Outcome of a credential request. `unavailable` means \"no repo to sync\"\n * (endpoint missing / not provisioned / not connected) — the caller\n * should warn and skip, never abort. `error` is an unexpected transport\n * failure the caller may log but still shouldn't hard-fail push on.\n */\nexport type GitCredentialOutcome =\n | { readonly status: \"ok\"; readonly credential: PortalGitCredential }\n | { readonly status: \"unavailable\"; readonly reason: string }\n | { readonly status: \"error\"; readonly reason: string };\n\n/**\n * Build the portal git-credential path for a definition id.\n *\n * Matches GitSync Phase 006 (fluid-commerce/fluid #19596): a member\n * route on the company-scoped definitions resource —\n * `POST /api/company/fluid_os/definitions/:id/git_credential`.\n */\nexport function portalGitCredentialPath(definitionId: number): string {\n return `/api/company/fluid_os/definitions/${definitionId}/git_credential`;\n}\n\nexport interface CreatePortalGitCredentialOptions {\n readonly baseUrl: string;\n readonly token: string;\n readonly definitionId: number;\n /** Injectable for tests; defaults to global fetch. */\n readonly fetchImpl?: typeof fetch;\n}\n\nfunction extractErrorReason(body: unknown, fallback: string): string {\n if (body && typeof body === \"object\" && \"error\" in body) {\n const error = (body as { error?: unknown }).error;\n if (error && typeof error === \"object\" && \"message\" in error) {\n const message = (error as { message?: unknown }).message;\n if (typeof message === \"string\" && message.length > 0) return message;\n }\n }\n return fallback;\n}\n\n/**\n * Request a git credential for the portal definition's provisioned repo.\n *\n * Never throws — every failure maps to an `unavailable`/`error` outcome\n * so the invisible-git sync is strictly best-effort.\n */\nexport async function createPortalGitCredential(\n options: CreatePortalGitCredentialOptions,\n): Promise<GitCredentialOutcome> {\n const { baseUrl, token, definitionId } = options;\n const fetchImpl = options.fetchImpl ?? fetch;\n const url = `${baseUrl.replace(/\\/$/, \"\")}${portalGitCredentialPath(definitionId)}`;\n\n let resp: Response;\n try {\n resp = await fetchImpl(url, {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${token}`,\n Accept: \"application/json\",\n },\n });\n } catch (err) {\n return {\n status: \"error\",\n reason: err instanceof Error ? err.message : String(err),\n };\n }\n\n // 404 → endpoint not deployed yet (prod today). Graceful skip.\n if (resp.status === 404) {\n return {\n status: \"unavailable\",\n reason: \"git sync isn't available for portals on this Fluid API yet\",\n };\n }\n\n const body: unknown = await resp.json().catch(() => null);\n\n if (!resp.ok) {\n // 409 not_provisioned/not_connected also means \"nothing to sync yet\".\n const reason = extractErrorReason(\n body,\n `git credential request failed (HTTP ${resp.status})`,\n );\n if (resp.status === 409) return { status: \"unavailable\", reason };\n return { status: \"error\", reason };\n }\n\n const credential = (body as { git_credential?: PortalGitCredential } | null)\n ?.git_credential;\n if (!credential?.remote_url) {\n return {\n status: \"error\",\n reason: \"git credential response missing remote_url\",\n };\n }\n\n return { status: \"ok\", credential };\n}\n","/**\n * Invisible-git sync for `fluid portal push`.\n *\n * Before the normal content push, we best-effort mirror the local portal\n * project to its provisioned GitHub repo: fetch → fast-forward-pull if a\n * teammate pushed → auto-commit working-tree changes → push HEAD:main.\n * This mirrors `fluid mist push`, but is STRICTLY non-fatal: any problem\n * (no git, no repo, credential endpoint missing, diverged history,\n * network blip) degrades to a skip so the content push always proceeds.\n */\n\nimport {\n createPortalGitCredential,\n type CreatePortalGitCredentialOptions,\n} from \"../api/git-credential.js\";\nimport {\n getOriginUrl,\n gitAdoptFetchHead,\n gitAheadBehindVsFetchHead,\n gitAutoCommit,\n gitFetch,\n gitInit,\n gitMergeFastForward,\n gitPush,\n gitRemoteBranchExists,\n hasCommits,\n hasCommonAncestorWithFetchHead,\n hasUncommittedChanges,\n isGitRepo,\n normalizedRepoIdentity,\n} from \"./git.js\";\n\nexport type PortalGitSyncResult =\n | { readonly status: \"skipped\"; readonly reason: string }\n | { readonly status: \"synced\"; readonly pushed: boolean }\n /** Local git state was already mutated (commit / fast-forward) and the\n * push then failed — the caller must NOT proceed with the content\n * push, or published artifacts drift from unsynced source. */\n | { readonly status: \"failed\"; readonly reason: string };\n\nexport interface PortalGitSyncOptions {\n readonly cwd: string;\n readonly baseUrl: string;\n readonly token: string;\n readonly definitionId: number;\n readonly commitMessage: string;\n readonly branch?: string;\n /** Injectable for tests. */\n readonly fetchImpl?: CreatePortalGitCredentialOptions[\"fetchImpl\"];\n}\n\n/**\n * Run the best-effort git sync. Returns a result describing what\n * happened; never throws. Callers surface `skipped` reasons as a dim\n * note and continue.\n */\nexport async function runPortalGitSync(\n options: PortalGitSyncOptions,\n): Promise<PortalGitSyncResult> {\n const { cwd, baseUrl, token, definitionId, commitMessage } = options;\n const branch = options.branch ?? \"main\";\n\n // Ensure a local repo exists — `fluid portal create` strips the\n // template's git history, so the first push initializes one and adopts\n // the provisioned remote's history below (the widget CLI's proven\n // first-push flow). A failed init falls back to the old skip.\n if (!(await isGitRepo(cwd)) && !(await gitInit(cwd))) {\n return { status: \"skipped\", reason: \"not a git repository\" };\n }\n\n // One credential for the whole sync — tokens live ~1h, far beyond a\n // push. Minting again mid-flow would open a degrade window AFTER the\n // auto-commit: a failure there reads as \"sync unavailable\" while the\n // fresh commit stays local and the content push carries on with\n // unsynced source. With a single mint, unavailability can only surface\n // before any local or remote mutation.\n const outcome = await createPortalGitCredential({\n baseUrl,\n token,\n definitionId,\n fetchImpl: options.fetchImpl,\n });\n if (outcome.status !== \"ok\") {\n return { status: \"skipped\", reason: outcome.reason };\n }\n const remoteUrl = outcome.credential.remote_url;\n\n // Identity guard: if this checkout already has an origin, it must be\n // the repo the credential was minted for. Without this, running\n // `fluid portal push` from an UNRELATED git repo would whole-tree\n // auto-commit that repo and push it to the portal's remote.\n const origin = await getOriginUrl(cwd);\n if (origin) {\n const originId = normalizedRepoIdentity(origin);\n const mintedId = normalizedRepoIdentity(remoteUrl);\n // Fail closed: an origin we can't normalize can't be verified, and\n // the downstream merge-base guard doesn't help when repos share\n // starter-template ancestry — skip rather than risk pushing another\n // project to the portal's remote.\n if (mintedId && originId !== mintedId) {\n return {\n status: \"skipped\",\n reason:\n `this directory's git origin (${originId ?? origin}) is not the ` +\n `portal's provisioned repository (${mintedId}) — are you in the ` +\n \"right project directory?\",\n };\n }\n }\n\n try {\n // Detect drift. When strictly behind with a CLEAN tree we can\n // fast-forward a teammate's commits in safely. We deliberately do NOT\n // stash-then-merge a dirty tree: a stash-pop conflict would leave the\n // checkout advanced with conflict markers while the content push\n // carried on. Instead, behind + dirty (and diverged) simply skip and\n // ask the user to reconcile in a terminal — the working tree is left\n // exactly as it was.\n // A freshly-initialized local repo (unborn HEAD) can't reconcile via\n // ahead/behind — and the backend provisions repos WITH an auto-init\n // or starter-template commit, so a fresh root commit would be\n // rejected as unrelated history. Adopt the remote history as the\n // local base instead; the auto-commit below then records the local\n // tree on top of it and the push fast-forwards.\n if (!(await hasCommits(cwd))) {\n if (await gitRemoteBranchExists(cwd, remoteUrl, branch)) {\n await gitFetch(cwd, remoteUrl, branch);\n await gitAdoptFetchHead(cwd);\n }\n if (await hasUncommittedChanges(cwd)) {\n // ADOPTION-ONLY semantics: stage new + modified files, never\n // removals. This directory may hold only a content subset (e.g.\n // populated by `fluid portal pull`, which syncs content but not\n // the repo), so a file absent locally is not a deletion — with\n // plain `add -A`, the first push would delete every remote-only\n // file from the provisioned repo. Local additions/edits win;\n // remote-only files are preserved on adoption. Deletions only\n // ever ship from full checkouts (the `add -A` path below).\n await gitAutoCommit(cwd, commitMessage, { ignoreRemovals: true });\n }\n return await pushCommitted(cwd, remoteUrl, branch);\n }\n\n await gitFetch(cwd, remoteUrl, branch);\n // Second identity guard, for checkouts without an origin: a repo\n // whose history shares no ancestor with the provisioned repo is some\n // OTHER project — never auto-commit and push it wholesale.\n if (!(await hasCommonAncestorWithFetchHead(cwd))) {\n return {\n status: \"skipped\",\n reason:\n \"this repository's history is unrelated to the portal's \" +\n \"provisioned repository — are you in the right project directory?\",\n };\n }\n const { ahead, behind } = await gitAheadBehindVsFetchHead(cwd);\n if (behind > 0 && ahead > 0) {\n return {\n status: \"skipped\",\n reason:\n \"local branch and origin have diverged — run `git pull --rebase` and retry\",\n };\n }\n if (behind > 0) {\n if (await hasUncommittedChanges(cwd)) {\n return {\n status: \"skipped\",\n reason:\n \"origin has newer commits and you have uncommitted changes — \" +\n \"run `git pull` and retry\",\n };\n }\n await gitMergeFastForward(cwd);\n }\n\n // Bundle working-tree edits into one commit so push has something to\n // send. No-op when the tree is already clean.\n if (await hasUncommittedChanges(cwd)) {\n await gitAutoCommit(cwd, commitMessage);\n }\n\n return await pushCommitted(cwd, remoteUrl, branch);\n } catch (err) {\n // Reconcile-phase failure (fetch / ls-remote / adopt / ff-merge /\n // commit). An ff-merge may already have advanced the checkout to\n // include a teammate's commits — that state is valid and self-heals,\n // but say so instead of pretending nothing happened.\n return {\n status: \"skipped\",\n reason: err instanceof Error ? err.message : String(err),\n };\n }\n}\n\n/**\n * The push gets its own failure boundary: by this point the auto-commit\n * (and any fast-forward) has already mutated local git state. A push\n * failure here is FATAL to the content push (matching `fluid widget\n * push`): publishing artifacts whose source never reached the repo is\n * exactly the drift the invisible-git model exists to prevent. The\n * local commit is durable — rerunning push finds the branch ahead and\n * retries the sync.\n */\nasync function pushCommitted(\n cwd: string,\n remoteUrl: string,\n branch: string,\n): Promise<PortalGitSyncResult> {\n try {\n await gitPush(cwd, remoteUrl, branch);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return {\n status: \"failed\",\n reason:\n \"your changes are committed locally but couldn't be pushed \" +\n `(${message}) — nothing was published; rerun \\`fluid portal push\\` ` +\n \"to retry\",\n };\n }\n return { status: \"synced\", pushed: true };\n}\n","/**\n * `fluid portal push` command\n *\n * Pushes local portal content changes to the Fluid OS API.\n * Detects changes since the last pull/push via snapshot diffing,\n * validates cross-references, and pushes resources in dependency order.\n */\n\nimport { Command } from \"commander\";\nimport type { PluginContext } from \"@fluid-app/fluid-cli\";\nimport { getAuthToken, getActiveProfile } from \"@fluid-app/fluid-cli\";\nimport { createFetchClient } from \"@fluid-app/fluidos-api-client\";\nimport type { FetchClient, components } from \"@fluid-app/fluidos-api-client\";\nimport { fluidOs } from \"@fluid-app/fluidos-api-client\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport { join } from \"node:path\";\nimport { readFile } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport prompts from \"prompts\";\n\nimport {\n readMappings,\n writeMappings,\n updateMapping,\n removeMapping,\n resolveSlugToId,\n} from \"../utils/mappings.js\";\nimport type { PortalMappings } from \"../utils/mappings.js\";\nimport {\n readSnapshot,\n diffAgainstSnapshot,\n writeSnapshot,\n computeFileHash,\n} from \"../utils/snapshot.js\";\nimport type { SnapshotDiff } from \"../utils/snapshot.js\";\nimport type {\n LocalScreen,\n LocalTheme,\n LocalNavigation,\n LocalNavigationItem,\n LocalProfile,\n} from \"../utils/transform.js\";\nimport {\n categorizeChanges,\n validateCrossReferences,\n slugFromPath,\n} from \"../utils/push-validation.js\";\nimport type { CategorizedChanges } from \"../utils/push-validation.js\";\nimport { runPortalGitSync } from \"../lib/git-sync.js\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Derived from the generated FluidOSNavigationItem but with `id` optional\n * (new items don't have one yet) and `label`/`position` required (needed\n * for create/update payloads).\n */\ntype NavigationSyncItem = Omit<\n components[\"schemas\"][\"FluidOSNavigationItem\"],\n \"id\" | \"label\" | \"position\" | \"children\"\n> & {\n id?: number;\n label: string;\n position: number;\n children?: NavigationSyncItem[];\n parent_id?: number | null;\n};\n\ninterface PushOptions {\n yes?: boolean;\n}\n\n/** Result of a single push operation. */\ninterface PushResult {\n readonly file: string;\n readonly action: \"created\" | \"updated\" | \"deleted\";\n readonly success: boolean;\n readonly error?: string;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Constants\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst PORTAL_DIR = \"portal\";\nconst PORTAL_SYNC_DIR = \".portal-sync\";\n\n/**\n * Convert the local array-form component_tree back to the object\n * the API expects. The pull command normalizes the API object into\n * an array for local convenience; this reverses that transformation.\n */\nfunction toApiComponentTree(\n tree: Record<string, unknown>[],\n): Record<string, unknown> | null {\n if (tree.length === 0) return null;\n if (tree.length === 1) return tree[0]!;\n // Fallback: wrap multiple roots in a container (shouldn't happen in practice)\n return { children: tree };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Create an authenticated FetchClient using the stored CLI profile.\n */\nfunction createClient(): FetchClient {\n const token = getAuthToken();\n if (!token) {\n const profile = getActiveProfile();\n if (!profile) {\n throw new Error(\n \"Not logged in. Run \" + chalk.cyan(\"fluid login\") + \" first.\",\n );\n }\n throw new Error(\n \"No auth token found for profile \" +\n chalk.cyan(profile.name) +\n \". Run \" +\n chalk.cyan(\"fluid login\") +\n \" to re-authenticate.\",\n );\n }\n\n const baseUrl = process.env[\"FLUID_API_BASE\"] ?? \"https://api.fluid.app\";\n\n return createFetchClient({\n baseUrl,\n getAuthToken: () => token,\n });\n}\n\n/**\n * Extract an enriched error message from a caught value.\n * Includes structured API error data when available.\n */\nfunction enrichedErrorMessage(err: unknown): string {\n let msg = err instanceof Error ? err.message : String(err);\n if (err && typeof err === \"object\" && \"data\" in err) {\n msg += ` — ${JSON.stringify((err as { data: unknown }).data)}`;\n }\n return msg;\n}\n\n/**\n * Best-effort \"invisible git\" sync: commit the local project and push it\n * to the portal's provisioned GitHub repo before the content push.\n * Degradable failures (no git, endpoint missing, diverged) are reported\n * as a dim note and the content push proceeds. Returns false — with a\n * clean error, never a thrown stack — when local git state was mutated\n * but the push failed, so the caller aborts the content push.\n */\nasync function runInvisibleGitSync(\n cwd: string,\n definitionId: number,\n definitionName: string,\n): Promise<boolean> {\n const token = getAuthToken();\n if (!token) return true; // content push reports the auth problem clearly.\n const baseUrl = process.env[\"FLUID_API_BASE\"] ?? \"https://api.fluid.app\";\n const timestamp = new Date().toISOString().slice(0, 19).replace(\"T\", \" \");\n const commitMessage = `Portal push: ${definitionName} · ${timestamp}`;\n\n const spinner = ora(\"Syncing project to git…\").start();\n const result = await runPortalGitSync({\n cwd,\n baseUrl,\n token,\n definitionId,\n commitMessage,\n });\n if (result.status === \"synced\") {\n spinner.succeed(\"Synced project to git\");\n } else if (result.status === \"failed\") {\n // Local git state was mutated but the push didn't land — abort\n // before the content push so published artifacts can't drift from\n // unsynced source (same contract as `fluid widget push`).\n spinner.fail(\"Git sync failed\");\n // Clean message, no stack trace: this is a user-facing outcome, not\n // a crash.\n console.error(chalk.red(result.reason));\n console.log();\n return false;\n } else {\n spinner.info(chalk.gray(`Skipped git sync — ${result.reason}`));\n }\n console.log();\n return true;\n}\n\n/**\n * Read and parse a JSON file from the portal directory.\n */\nasync function readPortalFile<T>(\n portalDir: string,\n relativePath: string,\n): Promise<T> {\n const filePath = join(portalDir, relativePath);\n const content = await readFile(filePath, \"utf-8\");\n return JSON.parse(content) as T;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Resource push functions\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Push screen changes to the API.\n */\nasync function pushScreens(\n client: FetchClient,\n defId: number,\n portalDir: string,\n changes: CategorizedChanges[\"screens\"],\n mappings: PortalMappings,\n): Promise<{ results: PushResult[]; mappings: PortalMappings }> {\n const results: PushResult[] = [];\n let currentMappings = mappings;\n\n // Create new screens\n for (const file of changes.new) {\n const slug = slugFromPath(file);\n try {\n const local = await readPortalFile<LocalScreen>(portalDir, file);\n const response = await fluidOs.fluid_os_v0_create_fluid_osscreen(\n client,\n defId,\n {\n screen: {\n name: local.name,\n slug,\n component_tree: toApiComponentTree(\n local.component_tree,\n ) as unknown as Record<string, unknown>,\n },\n },\n );\n const newId = response.screen?.id;\n if (newId != null) {\n currentMappings = updateMapping(\n currentMappings,\n \"screens\",\n slug,\n newId,\n );\n }\n results.push({ file, action: \"created\", success: true });\n } catch (err) {\n results.push({\n file,\n action: \"created\",\n success: false,\n error: enrichedErrorMessage(err),\n });\n return { results, mappings: currentMappings };\n }\n }\n\n // Update changed screens\n for (const file of changes.changed) {\n const slug = slugFromPath(file);\n const screenId = resolveSlugToId(currentMappings, \"screens\", slug);\n if (screenId == null) {\n results.push({\n file,\n action: \"updated\",\n success: false,\n error: `No mapping found for screen slug \"${slug}\"`,\n });\n return { results, mappings: currentMappings };\n }\n try {\n const local = await readPortalFile<LocalScreen>(portalDir, file);\n await fluidOs.fluid_os_v0_update_fluid_osscreen(client, defId, screenId, {\n screen: {\n name: local.name,\n slug,\n component_tree: toApiComponentTree(\n local.component_tree,\n ) as unknown as Record<string, unknown>,\n },\n });\n results.push({ file, action: \"updated\", success: true });\n } catch (err) {\n results.push({\n file,\n action: \"updated\",\n success: false,\n error: enrichedErrorMessage(err),\n });\n return { results, mappings: currentMappings };\n }\n }\n\n // Delete screens\n for (const file of changes.deleted) {\n const slug = slugFromPath(file);\n const screenId = resolveSlugToId(currentMappings, \"screens\", slug);\n if (screenId == null) {\n results.push({\n file,\n action: \"deleted\",\n success: false,\n error: `No mapping found for screen slug \"${slug}\"`,\n });\n return { results, mappings: currentMappings };\n }\n try {\n await fluidOs.fluid_os_v0_delete_fluid_osscreen(client, defId, screenId);\n currentMappings = removeMapping(currentMappings, \"screens\", slug);\n results.push({ file, action: \"deleted\", success: true });\n } catch (err) {\n results.push({\n file,\n action: \"deleted\",\n success: false,\n error: enrichedErrorMessage(err),\n });\n return { results, mappings: currentMappings };\n }\n }\n\n return { results, mappings: currentMappings };\n}\n\n/**\n * Push theme changes to the API.\n */\nasync function pushThemes(\n client: FetchClient,\n defId: number,\n portalDir: string,\n changes: CategorizedChanges[\"themes\"],\n mappings: PortalMappings,\n): Promise<{ results: PushResult[]; mappings: PortalMappings }> {\n const results: PushResult[] = [];\n let currentMappings = mappings;\n\n // Create new themes\n for (const file of changes.new) {\n const slug = slugFromPath(file);\n try {\n const local = await readPortalFile<LocalTheme>(portalDir, file);\n const response = await fluidOs.fluid_os_v0_create_fluid_ostheme(\n client,\n defId,\n {\n theme: {\n name: local.name,\n active: local.active,\n config: local.config,\n },\n },\n );\n const newId = response.theme?.id;\n if (newId != null) {\n currentMappings = updateMapping(currentMappings, \"themes\", slug, newId);\n }\n results.push({ file, action: \"created\", success: true });\n } catch (err) {\n results.push({\n file,\n action: \"created\",\n success: false,\n error: enrichedErrorMessage(err),\n });\n return { results, mappings: currentMappings };\n }\n }\n\n // Update changed themes\n for (const file of changes.changed) {\n const slug = slugFromPath(file);\n const themeId = resolveSlugToId(currentMappings, \"themes\", slug);\n if (themeId == null) {\n results.push({\n file,\n action: \"updated\",\n success: false,\n error: `No mapping found for theme slug \"${slug}\"`,\n });\n return { results, mappings: currentMappings };\n }\n try {\n const local = await readPortalFile<LocalTheme>(portalDir, file);\n await fluidOs.fluid_os_v0_update_fluid_ostheme(client, defId, themeId, {\n theme: {\n name: local.name,\n active: local.active,\n config: local.config,\n },\n });\n results.push({ file, action: \"updated\", success: true });\n } catch (err) {\n results.push({\n file,\n action: \"updated\",\n success: false,\n error: enrichedErrorMessage(err),\n });\n return { results, mappings: currentMappings };\n }\n }\n\n // Delete themes\n for (const file of changes.deleted) {\n const slug = slugFromPath(file);\n const themeId = resolveSlugToId(currentMappings, \"themes\", slug);\n if (themeId == null) {\n results.push({\n file,\n action: \"deleted\",\n success: false,\n error: `No mapping found for theme slug \"${slug}\"`,\n });\n return { results, mappings: currentMappings };\n }\n try {\n await fluidOs.fluid_os_v0_delete_fluid_ostheme(client, defId, themeId);\n currentMappings = removeMapping(currentMappings, \"themes\", slug);\n results.push({ file, action: \"deleted\", success: true });\n } catch (err) {\n results.push({\n file,\n action: \"deleted\",\n success: false,\n error: enrichedErrorMessage(err),\n });\n return { results, mappings: currentMappings };\n }\n }\n\n return { results, mappings: currentMappings };\n}\n\n/**\n * Resolve screen slug references to screen IDs in navigation items.\n * Returns a new tree with `screen_id` instead of `screen` slug,\n * shaped to match the FluidOSNavigationItemSyncItem schema.\n */\nfunction resolveNavigationItemScreenIds(\n items: LocalNavigationItem[],\n mappings: PortalMappings,\n): NavigationSyncItem[] {\n return items.map((item) => {\n const screenId = item.screen\n ? (resolveSlugToId(mappings, \"screens\", item.screen) ?? undefined)\n : undefined;\n const result: NavigationSyncItem = {\n ...(item.id ? { id: item.id } : {}),\n label: item.label ?? \"\",\n position: item.position ?? 0,\n icon: item.icon,\n screen_id: screenId ?? null,\n slug: item.slug,\n source: (item.source as \"user\" | \"system\" | \"code\") ?? \"user\",\n parent_id: item.parent_id,\n children: resolveNavigationItemScreenIds(item.children ?? [], mappings),\n };\n return result;\n });\n}\n\n/**\n * Flatten a tree of navigation sync items into a flat list.\n * The API reconciliation logic requires a flat list to correctly\n * compare against the flat server response.\n */\nfunction flattenNavigationItems(\n items: NavigationSyncItem[],\n): NavigationSyncItem[] {\n const flat: NavigationSyncItem[] = [];\n for (const item of items) {\n const { children, ...rest } = item;\n flat.push(rest as NavigationSyncItem);\n if (children && children.length > 0) {\n flat.push(...flattenNavigationItems(children as NavigationSyncItem[]));\n }\n }\n return flat;\n}\n\n/**\n * Push navigation changes to the API.\n */\nasync function pushNavigations(\n client: FetchClient,\n defId: number,\n portalDir: string,\n changes: CategorizedChanges[\"navigations\"],\n mappings: PortalMappings,\n): Promise<{ results: PushResult[]; mappings: PortalMappings }> {\n const results: PushResult[] = [];\n let currentMappings = mappings;\n\n // Create new navigations\n for (const file of changes.new) {\n const slug = slugFromPath(file);\n try {\n const local = await readPortalFile<LocalNavigation>(portalDir, file);\n const response = await fluidOs.fluid_os_v0_create_fluid_osnavigation(\n client,\n defId,\n {\n navigation: {\n name: local.name,\n platform: local.platform as \"web\" | \"mobile\",\n },\n },\n );\n const newId = response.navigation?.id;\n if (newId != null) {\n currentMappings = updateMapping(\n currentMappings,\n \"navigations\",\n slug,\n newId,\n );\n // Create navigation items individually (flatten tree for API).\n // Track local→server ID mapping so child items reference the\n // correct server-assigned parent IDs.\n const resolvedItems = flattenNavigationItems(\n resolveNavigationItemScreenIds(\n local.navigation_items,\n currentMappings,\n ),\n );\n const localToServerId = new Map<number, number>();\n\n for (const item of resolvedItems) {\n const resolvedParentId =\n item.parent_id != null\n ? (localToServerId.get(item.parent_id) ?? item.parent_id)\n : undefined;\n\n const created =\n await fluidOs.fluid_os_v0_create_fluid_osnavigation_item(\n client,\n defId,\n newId,\n {\n navigation_item: {\n label: item.label ?? \"\",\n position: item.position ?? 0,\n icon: item.icon ?? undefined,\n screen_id: item.screen_id ?? undefined,\n slug: item.slug ?? undefined,\n source: item.source ?? undefined,\n parent_id: resolvedParentId,\n },\n },\n );\n\n if (item.id != null && created.navigation_item?.id != null) {\n localToServerId.set(item.id, created.navigation_item.id);\n }\n }\n }\n results.push({ file, action: \"created\", success: true });\n } catch (err) {\n results.push({\n file,\n action: \"created\",\n success: false,\n error: enrichedErrorMessage(err),\n });\n return { results, mappings: currentMappings };\n }\n }\n\n // Update changed navigations\n for (const file of changes.changed) {\n const slug = slugFromPath(file);\n const navId = resolveSlugToId(currentMappings, \"navigations\", slug);\n if (navId == null) {\n results.push({\n file,\n action: \"updated\",\n success: false,\n error: `No mapping found for navigation slug \"${slug}\"`,\n });\n return { results, mappings: currentMappings };\n }\n try {\n const local = await readPortalFile<LocalNavigation>(portalDir, file);\n\n // Update navigation metadata\n await fluidOs.fluid_os_v0_update_fluid_osnavigation(\n client,\n defId,\n navId,\n {\n navigation: {\n name: local.name,\n platform: local.platform as \"web\" | \"mobile\",\n },\n },\n );\n\n // Reconcile navigation items via individual CRUD operations.\n // The bulk sync endpoint is unreliable, so we diff local vs server\n // and issue create/update/delete calls like the admin builder does.\n // Flatten the tree since the API returns/expects a flat list.\n const resolvedItems = flattenNavigationItems(\n resolveNavigationItemScreenIds(local.navigation_items, currentMappings),\n );\n\n const serverResponse =\n await fluidOs.fluid_os_v0_list_fluid_osnavigation_items(\n client,\n defId,\n navId,\n );\n const serverItems = serverResponse.navigation_items ?? [];\n const serverById = new Map(serverItems.map((s) => [s.id, s]));\n const localIds = new Set(\n resolvedItems.filter((i) => i.id).map((i) => i.id),\n );\n\n // Delete server items not in local\n for (const serverItem of serverItems) {\n if (!localIds.has(serverItem.id)) {\n await fluidOs.fluid_os_v0_delete_fluid_osnavigation_item(\n client,\n defId,\n navId,\n serverItem.id,\n );\n }\n }\n\n // Create or update local items.\n // Track local→server ID mapping so newly created child items\n // reference the correct server-assigned parent IDs.\n const localToServerId = new Map<number, number>();\n\n for (const item of resolvedItems) {\n const resolvedParentId =\n item.parent_id != null\n ? (localToServerId.get(item.parent_id) ?? item.parent_id)\n : undefined;\n\n const body = {\n label: item.label,\n position: item.position,\n icon: item.icon ?? undefined,\n screen_id: item.screen_id ?? undefined,\n slug: item.slug ?? undefined,\n source: item.source ?? undefined,\n parent_id: resolvedParentId,\n };\n\n if (item.id && serverById.has(item.id)) {\n // Update existing\n await fluidOs.fluid_os_v0_update_fluid_osnavigation_item(\n client,\n defId,\n navId,\n item.id,\n { navigation_item: body },\n );\n } else {\n // Create new\n const created =\n await fluidOs.fluid_os_v0_create_fluid_osnavigation_item(\n client,\n defId,\n navId,\n {\n navigation_item: {\n ...body,\n label: body.label ?? \"\",\n position: body.position ?? 0,\n },\n },\n );\n\n if (item.id != null && created.navigation_item?.id != null) {\n localToServerId.set(item.id, created.navigation_item.id);\n }\n }\n }\n\n results.push({ file, action: \"updated\", success: true });\n } catch (err) {\n results.push({\n file,\n action: \"updated\",\n success: false,\n error: enrichedErrorMessage(err),\n });\n return { results, mappings: currentMappings };\n }\n }\n\n // Delete navigations\n for (const file of changes.deleted) {\n const slug = slugFromPath(file);\n const navId = resolveSlugToId(currentMappings, \"navigations\", slug);\n if (navId == null) {\n results.push({\n file,\n action: \"deleted\",\n success: false,\n error: `No mapping found for navigation slug \"${slug}\"`,\n });\n return { results, mappings: currentMappings };\n }\n try {\n await fluidOs.fluid_os_v0_delete_fluid_osnavigation(client, defId, navId);\n currentMappings = removeMapping(currentMappings, \"navigations\", slug);\n results.push({ file, action: \"deleted\", success: true });\n } catch (err) {\n results.push({\n file,\n action: \"deleted\",\n success: false,\n error: enrichedErrorMessage(err),\n });\n return { results, mappings: currentMappings };\n }\n }\n\n return { results, mappings: currentMappings };\n}\n\n/**\n * Push profile changes to the API.\n */\nasync function pushProfiles(\n client: FetchClient,\n defId: number,\n portalDir: string,\n changes: CategorizedChanges[\"profiles\"],\n mappings: PortalMappings,\n): Promise<{ results: PushResult[]; mappings: PortalMappings }> {\n const results: PushResult[] = [];\n let currentMappings = mappings;\n\n // Create new profiles\n for (const file of changes.new) {\n const slug = slugFromPath(file);\n try {\n const local = await readPortalFile<LocalProfile>(portalDir, file);\n const body = resolveProfileBody(local, currentMappings);\n const response = await fluidOs.fluid_os_v0_create_fluid_osprofile(\n client,\n defId,\n {\n profile: body,\n },\n );\n const newId = response.profile?.id;\n if (newId != null) {\n currentMappings = updateMapping(\n currentMappings,\n \"profiles\",\n slug,\n newId,\n );\n }\n results.push({ file, action: \"created\", success: true });\n } catch (err) {\n results.push({\n file,\n action: \"created\",\n success: false,\n error: enrichedErrorMessage(err),\n });\n return { results, mappings: currentMappings };\n }\n }\n\n // Update changed profiles\n for (const file of changes.changed) {\n const slug = slugFromPath(file);\n const profileId = resolveSlugToId(currentMappings, \"profiles\", slug);\n if (profileId == null) {\n results.push({\n file,\n action: \"updated\",\n success: false,\n error: `No mapping found for profile slug \"${slug}\"`,\n });\n return { results, mappings: currentMappings };\n }\n try {\n const local = await readPortalFile<LocalProfile>(portalDir, file);\n const body = resolveProfileBody(local, currentMappings);\n await fluidOs.fluid_os_v0_update_fluid_osprofile(\n client,\n defId,\n profileId,\n {\n profile: body,\n },\n );\n results.push({ file, action: \"updated\", success: true });\n } catch (err) {\n results.push({\n file,\n action: \"updated\",\n success: false,\n error: enrichedErrorMessage(err),\n });\n return { results, mappings: currentMappings };\n }\n }\n\n // Delete profiles\n for (const file of changes.deleted) {\n const slug = slugFromPath(file);\n const profileId = resolveSlugToId(currentMappings, \"profiles\", slug);\n if (profileId == null) {\n results.push({\n file,\n action: \"deleted\",\n success: false,\n error: `No mapping found for profile slug \"${slug}\"`,\n });\n return { results, mappings: currentMappings };\n }\n try {\n await fluidOs.fluid_os_v0_delete_fluid_osprofile(\n client,\n defId,\n profileId,\n );\n currentMappings = removeMapping(currentMappings, \"profiles\", slug);\n results.push({ file, action: \"deleted\", success: true });\n } catch (err) {\n results.push({\n file,\n action: \"deleted\",\n success: false,\n error: enrichedErrorMessage(err),\n });\n return { results, mappings: currentMappings };\n }\n }\n\n return { results, mappings: currentMappings };\n}\n\n/** Typed profile body for API requests (create and update share the same shape). */\ninterface ProfileBody {\n name: string;\n default?: boolean;\n navigation_id?: number;\n mobile_navigation_id?: number;\n theme_ids?: number[];\n permissions?: {\n countries?: number[];\n ranks?: number[];\n roles?: string[];\n platform?: string[];\n };\n}\n\n/**\n * Resolve profile slug references to API IDs for create/update request body.\n */\nfunction resolveProfileBody(\n local: LocalProfile,\n mappings: PortalMappings,\n): ProfileBody {\n const body: ProfileBody = {\n name: local.name,\n default: local.default,\n permissions: local.permissions,\n };\n\n if (local.navigation) {\n const navId = resolveSlugToId(mappings, \"navigations\", local.navigation);\n if (navId != null) {\n body.navigation_id = navId;\n }\n }\n\n if (local.mobile_navigation) {\n const mobileNavId = resolveSlugToId(\n mappings,\n \"navigations\",\n local.mobile_navigation,\n );\n if (mobileNavId != null) {\n body.mobile_navigation_id = mobileNavId;\n }\n }\n\n const themeIds = local.themes\n .map((slug) => resolveSlugToId(mappings, \"themes\", slug))\n .filter((id): id is number => id != null);\n body.theme_ids = themeIds;\n\n return body;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Print helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\nfunction printChangesSummary(diff: SnapshotDiff, definitionName: string): void {\n console.log(\n chalk.blue(\"Changes to push for \") +\n chalk.white.bold(`\"${definitionName}\"`) +\n chalk.blue(\":\"),\n );\n console.log();\n\n if (diff.new.length > 0) {\n console.log(chalk.green(\" New: \") + diff.new.join(\", \"));\n }\n if (diff.changed.length > 0) {\n console.log(chalk.yellow(\" Changed: \") + diff.changed.join(\", \"));\n }\n if (diff.deleted.length > 0) {\n console.log(chalk.red(\" Deleted: \") + diff.deleted.join(\", \"));\n }\n console.log();\n}\n\nfunction printPushReport(results: PushResult[], skippedPhases: string[]): void {\n const succeeded = results.filter((r) => r.success);\n const failed = results.filter((r) => !r.success);\n\n if (succeeded.length > 0) {\n console.log(chalk.green.bold(\"Succeeded:\"));\n for (const r of succeeded) {\n console.log(chalk.green(\" \" + r.action + \": \") + r.file);\n }\n }\n\n if (failed.length > 0) {\n console.log();\n console.log(chalk.red.bold(\"Failed:\"));\n for (const r of failed) {\n console.log(\n chalk.red(\" \" + r.action + \": \") +\n r.file +\n chalk.gray(\" — \" + (r.error ?? \"Unknown error\")),\n );\n }\n }\n\n if (skippedPhases.length > 0) {\n console.log();\n console.log(chalk.yellow.bold(\"Skipped phases:\"));\n for (const phase of skippedPhases) {\n console.log(chalk.yellow(\" \" + phase));\n }\n }\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Command\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport const pushCommand: Command = new Command(\"push\")\n .description(\"Push local portal content changes to the Fluid OS API\")\n .option(\"--yes\", \"Skip confirmation prompt\")\n .action(async (options: PushOptions) => {\n const cwd = process.cwd();\n const portalDir = join(cwd, PORTAL_DIR);\n const portalSyncDir = join(cwd, PORTAL_SYNC_DIR);\n\n console.log();\n console.log(chalk.blue.bold(\"Fluid Portal Push\"));\n console.log();\n\n // ── Check for portal directory ─────────────────────────────────────\n if (!existsSync(portalDir)) {\n console.log(\n chalk.red(\"Error:\") +\n \" No portal/ directory found. Run \" +\n chalk.cyan(\"fluid portal pull\") +\n \" first.\",\n );\n console.log();\n process.exit(1);\n }\n\n // ── Read snapshot ──────────────────────────────────────────────────\n const snapshot = await readSnapshot(portalSyncDir);\n if (!snapshot) {\n console.log(\n chalk.red(\"Error:\") +\n \" No snapshot found in .portal-sync/. Run \" +\n chalk.cyan(\"fluid portal pull\") +\n \" first.\",\n );\n console.log();\n process.exit(1);\n }\n\n // ── Read mappings ──────────────────────────────────────────────────\n const mappings = await readMappings(portalSyncDir);\n if (!mappings) {\n console.log(\n chalk.red(\"Error:\") +\n \" No mappings found in .portal-sync/. Run \" +\n chalk.cyan(\"fluid portal pull\") +\n \" first.\",\n );\n console.log();\n process.exit(1);\n }\n\n const definitionId = snapshot.definition_id;\n const definitionName = snapshot.definition;\n\n console.log(\n chalk.gray(\"Definition: \") +\n chalk.white(definitionName) +\n chalk.gray(` (ID: ${definitionId})`),\n );\n console.log();\n\n // ── Invisible git sync (before the content push) ────────────────────\n // Mirror the local project to its provisioned GitHub repo. Degradable\n // problems — no git / no repo / endpoint missing (404) / diverged\n // history — degrade to a dim note and the content push proceeds. The\n // one fatal case is a rejected push AFTER local git state was mutated:\n // publishing then would ship content whose source never reached the\n // repo, so we stop here (clean error already printed).\n if (!(await runInvisibleGitSync(cwd, definitionId, definitionName))) {\n process.exitCode = 1;\n return;\n }\n\n // ── Detect changes ─────────────────────────────────────────────────\n const spinner = ora();\n spinner.start(\"Detecting changes...\");\n\n const diff = await diffAgainstSnapshot(portalDir, snapshot);\n const totalChanges =\n diff.new.length + diff.changed.length + diff.deleted.length;\n\n if (totalChanges === 0) {\n spinner.succeed(\"Nothing to push.\");\n console.log();\n return;\n }\n\n spinner.succeed(`Found ${totalChanges} change(s)`);\n console.log();\n\n // ── Show changes and confirm ───────────────────────────────────────\n printChangesSummary(diff, definitionName);\n\n if (!options.yes) {\n const { confirmed } = await prompts({\n type: \"confirm\",\n name: \"confirmed\",\n message: `Push ${totalChanges} change(s) to Fluid OS?`,\n initial: false,\n });\n\n if (!confirmed) {\n console.log();\n console.log(chalk.gray(\"Push cancelled.\"));\n console.log();\n return;\n }\n console.log();\n }\n\n // ── Categorize changes ─────────────────────────────────────────────\n const changes = categorizeChanges(diff);\n\n // ── Cross-reference validation ─────────────────────────────────────\n spinner.start(\"Validating cross-references...\");\n const validationErrors = await validateCrossReferences(\n portalDir,\n mappings,\n changes,\n );\n\n if (validationErrors.length > 0) {\n spinner.fail(\"Cross-reference validation failed\");\n console.log();\n for (const err of validationErrors) {\n console.log(chalk.red(\" \" + err.file + \": \") + err.message);\n }\n console.log();\n process.exit(1);\n }\n spinner.succeed(\"Cross-references valid\");\n\n // ── Authenticate ───────────────────────────────────────────────────\n spinner.start(\"Authenticating...\");\n\n let client: FetchClient;\n try {\n client = createClient();\n spinner.succeed(\"Authenticated\");\n } catch (err) {\n spinner.fail(\"Authentication failed\");\n console.log();\n console.log(\n chalk.red(\"Error:\") +\n \" \" +\n (err instanceof Error ? err.message : String(err)),\n );\n console.log();\n process.exit(1);\n }\n\n // ── Phase 1: Screens + Themes (parallel) ───────────────────────────\n const allResults: PushResult[] = [];\n const skippedPhases: string[] = [];\n let currentMappings = mappings;\n let aborted = false;\n\n const hasScreenChanges =\n changes.screens.new.length > 0 ||\n changes.screens.changed.length > 0 ||\n changes.screens.deleted.length > 0;\n const hasThemeChanges =\n changes.themes.new.length > 0 ||\n changes.themes.changed.length > 0 ||\n changes.themes.deleted.length > 0;\n\n if (hasScreenChanges || hasThemeChanges) {\n spinner.start(\"Phase 1: Pushing screens and themes...\");\n\n const phase1Tasks: Promise<{\n results: PushResult[];\n mappings: PortalMappings;\n }>[] = [];\n\n if (hasScreenChanges) {\n phase1Tasks.push(\n pushScreens(\n client,\n definitionId,\n portalDir,\n changes.screens,\n currentMappings,\n ),\n );\n }\n if (hasThemeChanges) {\n phase1Tasks.push(\n pushThemes(\n client,\n definitionId,\n portalDir,\n changes.themes,\n currentMappings,\n ),\n );\n }\n\n const phase1Results = await Promise.all(phase1Tasks);\n\n // Merge mappings from both parallel operations\n // Only apply the resource type each operation actually owns\n for (const result of phase1Results) {\n allResults.push(...result.results);\n }\n if (hasScreenChanges) {\n currentMappings = {\n ...currentMappings,\n screens: phase1Results[0]!.mappings.screens,\n };\n }\n if (hasThemeChanges) {\n const idx = hasScreenChanges ? 1 : 0;\n currentMappings = {\n ...currentMappings,\n themes: phase1Results[idx]!.mappings.themes,\n };\n }\n\n const phase1Failed = phase1Results.some((r) =>\n r.results.some((res) => !res.success),\n );\n\n if (phase1Failed) {\n spinner.fail(\"Phase 1 failed\");\n aborted = true;\n skippedPhases.push(\"Phase 2: Navigations\", \"Phase 3: Profiles\");\n } else {\n spinner.succeed(\"Phase 1 complete\");\n }\n }\n\n // ── Phase 2: Navigations ───────────────────────────────────────────\n const hasNavChanges =\n changes.navigations.new.length > 0 ||\n changes.navigations.changed.length > 0 ||\n changes.navigations.deleted.length > 0;\n\n if (!aborted && hasNavChanges) {\n spinner.start(\"Phase 2: Pushing navigations...\");\n\n const navResult = await pushNavigations(\n client,\n definitionId,\n portalDir,\n changes.navigations,\n currentMappings,\n );\n\n allResults.push(...navResult.results);\n currentMappings = {\n ...currentMappings,\n navigations: navResult.mappings.navigations,\n };\n\n const phase2Failed = navResult.results.some((r) => !r.success);\n if (phase2Failed) {\n spinner.fail(\"Phase 2 failed\");\n aborted = true;\n skippedPhases.push(\"Phase 3: Profiles\");\n } else {\n spinner.succeed(\"Phase 2 complete\");\n }\n } else if (aborted && hasNavChanges) {\n // Already marked as skipped above\n }\n\n // ── Phase 3: Profiles ──────────────────────────────────────────────\n const hasProfileChanges =\n changes.profiles.new.length > 0 ||\n changes.profiles.changed.length > 0 ||\n changes.profiles.deleted.length > 0;\n\n if (!aborted && hasProfileChanges) {\n spinner.start(\"Phase 3: Pushing profiles...\");\n\n const profileResult = await pushProfiles(\n client,\n definitionId,\n portalDir,\n changes.profiles,\n currentMappings,\n );\n\n allResults.push(...profileResult.results);\n currentMappings = {\n ...currentMappings,\n profiles: profileResult.mappings.profiles,\n };\n\n const phase3Failed = profileResult.results.some((r) => !r.success);\n if (phase3Failed) {\n spinner.fail(\"Phase 3 failed\");\n } else {\n spinner.succeed(\"Phase 3 complete\");\n }\n }\n\n // ── Update mappings ────────────────────────────────────────────────\n await writeMappings(portalSyncDir, currentMappings);\n\n // ── Update snapshot for successfully pushed files ───────────────────\n const successfulFiles = new Set(\n allResults.filter((r) => r.success).map((r) => r.file),\n );\n\n if (successfulFiles.size > 0) {\n // Only advance snapshot entries for successfully pushed files;\n // leave skipped/failed entries at their original hashes so they\n // show up as changed on the next run.\n const updatedHashes = { ...snapshot.files };\n for (const file of successfulFiles) {\n const fullPath = join(portalDir, file);\n if (existsSync(fullPath)) {\n updatedHashes[file] = await computeFileHash(fullPath);\n } else {\n // deleted file — remove from snapshot\n delete updatedHashes[file];\n }\n }\n await writeSnapshot(portalSyncDir, {\n ...snapshot,\n files: updatedHashes,\n });\n }\n\n // ── Report ─────────────────────────────────────────────────────────\n console.log();\n const allSucceeded = allResults.every((r) => r.success);\n\n if (allSucceeded && !aborted) {\n console.log(chalk.green.bold(\"Push complete!\"));\n } else {\n console.log(chalk.yellow.bold(\"Push completed with issues:\"));\n }\n console.log();\n printPushReport(allResults, skippedPhases);\n console.log();\n });\n\nexport function registerPushCommand(ctx: PluginContext): void {\n ctx.program.addCommand(pushCommand);\n}\n","/**\n * `fluid portal clone [app]` — mints a fresh git credential for a Fluid\n * OS portal definition and clones its provisioned GitHub repo into\n * `./<slug>/`. Mirrors `fluid mist clone` (credential → clone), but\n * against the portal git-credential endpoint.\n *\n * The credential endpoint is part of GitSync phase 006; until it ships\n * the request 404s and we print a friendly \"not available yet\" message\n * instead of a raw git error.\n */\n\nimport { Command } from \"commander\";\nimport type { PluginContext } from \"@fluid-app/fluid-cli\";\nimport { getAuthToken, getActiveProfile } from \"@fluid-app/fluid-cli\";\nimport { createFetchClient } from \"@fluid-app/fluidos-api-client\";\nimport type { FetchClient } from \"@fluid-app/fluidos-api-client\";\nimport { fluidOs } from \"@fluid-app/fluidos-api-client\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport prompts from \"prompts\";\nimport { existsSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { deriveSlug } from \"../utils/mappings.js\";\nimport { createPortalGitCredential } from \"../api/git-credential.js\";\nimport { ensureGitAvailable, gitClone } from \"../lib/git.js\";\n\ninterface CloneOptions {\n app?: string;\n dir?: string;\n}\n\nfunction getBaseUrl(): string {\n return process.env[\"FLUID_API_BASE\"] ?? \"https://api.fluid.app\";\n}\n\nfunction createClient(token: string): FetchClient {\n return createFetchClient({\n baseUrl: getBaseUrl(),\n getAuthToken: () => token,\n });\n}\n\ninterface ResolvedDefinition {\n id: number;\n name: string;\n}\n\n/** Select a definition by --app name, else interactively. */\nasync function selectDefinition(\n client: FetchClient,\n appName?: string,\n): Promise<ResolvedDefinition> {\n const definitions: Array<{ id?: number; name?: string | null }> = [];\n let page = 1;\n while (true) {\n const response = await fluidOs.fluid_os_v0_list_fluid_osdefinitions(\n client,\n { page, per_page: 500 },\n );\n const batch = response.definitions ?? [];\n definitions.push(...batch);\n const totalPages = response.meta?.total_pages ?? 1;\n if (page >= totalPages || batch.length === 0) break;\n page++;\n }\n\n if (definitions.length === 0) {\n throw new Error(\n \"No Fluid OS definitions found. Create one in the admin dashboard first.\",\n );\n }\n\n if (appName) {\n const match = definitions.find(\n (d) => d.name?.toLowerCase() === appName.toLowerCase(),\n );\n if (!match || match.id == null) {\n const available = definitions\n .map((d) => chalk.cyan(d.name ?? \"(unnamed)\"))\n .join(\", \");\n throw new Error(\n `No definition found matching \"${appName}\". Available: ${available}`,\n );\n }\n return { id: match.id, name: match.name ?? `portal-${match.id}` };\n }\n\n const { definitionId } = await prompts({\n type: \"select\",\n name: \"definitionId\",\n message: \"Select a Fluid OS definition to clone\",\n choices: definitions\n .filter((d) => d.id != null)\n .map((d) => ({ title: d.name ?? \"(unnamed)\", value: d.id })),\n });\n if (definitionId == null) throw new Error(\"No definition selected.\");\n const selected = definitions.find((d) => d.id === definitionId);\n if (!selected || selected.id == null) {\n throw new Error(\"Selected definition not found.\");\n }\n return { id: selected.id, name: selected.name ?? `portal-${selected.id}` };\n}\n\nexport const cloneCommand: Command = new Command(\"clone\")\n .description(\n \"Clone a portal definition's provisioned GitHub repo (mints a fresh token per call)\",\n )\n .argument(\"[app]\", \"Definition name (prompts to select when omitted)\")\n .option(\"--app <name>\", \"Definition name (same as the positional argument)\")\n .option(\"--dir <dir>\", \"Target directory (defaults to ./<slug>/)\")\n .action(async (appArg: string | undefined, options: CloneOptions) => {\n try {\n await ensureGitAvailable();\n\n const token = getAuthToken();\n if (!token) {\n const profile = getActiveProfile();\n throw new Error(\n profile\n ? `No auth token for profile ${chalk.cyan(profile.name)}. Run ${chalk.cyan(\"fluid login\")}.`\n : `Not logged in. Run ${chalk.cyan(\"fluid login\")} first.`,\n );\n }\n const client = createClient(token);\n\n const spinner = ora(\"Resolving definition…\").start();\n const definition = await selectDefinition(client, options.app ?? appArg);\n\n spinner.text = \"Minting git credential…\";\n const outcome = await createPortalGitCredential({\n baseUrl: getBaseUrl(),\n token,\n definitionId: definition.id,\n });\n if (outcome.status !== \"ok\") {\n spinner.warn(`Can't clone — ${outcome.reason}`);\n process.exit(1);\n }\n\n const slug = deriveSlug(definition.name);\n const targetDir = resolve(options.dir ?? slug);\n if (existsSync(targetDir)) {\n spinner.fail(\n `Directory ${targetDir} already exists. Pass --dir or remove it first.`,\n );\n process.exit(1);\n }\n\n spinner.text = `Cloning into ${slug}/…`;\n try {\n // Token-bearing URL is passed inline — never written to .git/config.\n await gitClone(outcome.credential.remote_url, targetDir);\n } catch (err) {\n spinner.fail(\"git clone failed\");\n throw err;\n }\n spinner.succeed(`Cloned into ${chalk.cyan(targetDir)}`);\n console.log(chalk.dim(` cd ${slug}`));\n console.log(\n chalk.dim(` ${chalk.cyan(\"fluid portal pull\")} to sync content`),\n );\n } catch (error) {\n console.log();\n console.log(\n chalk.red(\"Error:\") +\n \" \" +\n (error instanceof Error ? error.message : String(error)),\n );\n process.exit(1);\n }\n });\n\nexport function registerCloneCommand(ctx: PluginContext): void {\n ctx.program.addCommand(cloneCommand);\n}\n","/**\n * Pure helper functions for widget scaffolding.\n * Extracted from widget-create command for testability.\n */\n\n/**\n * Convert kebab-case name to PascalCase widget type.\n * e.g., \"stock-ticker\" → \"StockTickerWidget\"\n */\nexport function toWidgetType(name: string): string {\n const pascal = name\n .split(\"-\")\n .map((s) => s.charAt(0).toUpperCase() + s.slice(1))\n .join(\"\");\n return pascal.endsWith(\"Widget\") ? pascal : `${pascal}Widget`;\n}\n\n/**\n * Derive the component name from a widget type.\n * Strips the trailing \"Widget\" suffix, but only if the result is non-empty\n * and starts with a letter (not a digit).\n * e.g., \"StockTickerWidget\" → \"StockTicker\"\n */\nexport function toComponentName(widgetType: string): string {\n if (widgetType === \"Widget\") return \"Widget\";\n const stripped = widgetType.replace(/Widget$/, \"\");\n return stripped || widgetType;\n}\n\n/**\n * Convert kebab-case name to a display name.\n * e.g., \"stock-ticker\" → \"Stock Ticker\"\n */\nexport function toDisplayName(name: string): string {\n return name\n .split(\"-\")\n .map((s) => s.charAt(0).toUpperCase() + s.slice(1))\n .join(\" \");\n}\n\n/**\n * Convert kebab-case to camelCase.\n * e.g., \"stock-ticker\" → \"stockTicker\"\n */\nexport function toCamelCase(name: string): string {\n return name.replace(/-./g, (x) => x.charAt(1).toUpperCase());\n}\n\n/**\n * Validate a widget name for scaffold.\n * Must be kebab-case, no trailing/consecutive dashes, no bare \"widget\".\n */\nexport function validateWidgetName(name: string): string | null {\n if (!/^[a-z][a-z0-9]*(-[a-z0-9]+)*$/.test(name)) {\n return \"Widget name must be kebab-case with no trailing or consecutive dashes (e.g., stock-ticker).\";\n }\n if (name === \"widget\") {\n return 'Widget name \"widget\" is reserved. Use a more descriptive name (e.g., custom-widget).';\n }\n return null;\n}\n\n/**\n * Insert an import line after the last import in a source file.\n * Skips if the import already exists (prevents duplicates on re-scaffold).\n * Returns null if no imports exist in the source.\n */\nexport function insertImport(\n source: string,\n importLine: string,\n): string | null {\n // Skip if import already exists\n if (source.includes(importLine)) return source;\n\n // Find the last real import statement (starts at column 0, not inside comments)\n const importPattern = /^import\\s/gm;\n let lastImportIndex = -1;\n let match: RegExpExecArray | null;\n while ((match = importPattern.exec(source)) !== null) {\n lastImportIndex = match.index;\n }\n if (lastImportIndex === -1) return null;\n\n const lineEnd = source.indexOf(\"\\n\", lastImportIndex);\n if (lineEnd === -1) {\n // Last import is on the final line with no trailing newline\n return source + \"\\n\" + importLine + \"\\n\";\n }\n\n return (\n source.slice(0, lineEnd + 1) + importLine + \"\\n\" + source.slice(lineEnd + 1)\n );\n}\n\n/** Insert a widget definition into a defineWidgetPackage widgets array. */\nexport function insertIntoWidgetPackage(\n source: string,\n exportName: string,\n): string | null {\n const packageStart = source.lastIndexOf(\"defineWidgetPackage(\");\n if (packageStart === -1) return null;\n\n const packageSource = source.slice(packageStart);\n const pattern = /(widgets\\s*:\\s*)\\[([^\\]]*)\\]/g;\n let lastMatch: RegExpExecArray | null = null;\n let m: RegExpExecArray | null;\n while ((m = pattern.exec(packageSource)) !== null) {\n lastMatch = m;\n }\n\n if (!lastMatch) return null;\n\n const fullMatch = lastMatch[0];\n const declaration = lastMatch[1]!;\n const inner = lastMatch[2]!;\n const matchStart = packageStart + lastMatch.index;\n\n const lines = inner.split(\"\\n\");\n\n // Collect existing real entries (stripped of commas for comparison)\n const existingEntries: string[] = [];\n for (const line of lines) {\n const trimmed = line.trim();\n if (trimmed && !trimmed.startsWith(\"//\")) {\n existingEntries.push(trimmed.replace(/,$/, \"\"));\n }\n }\n\n // Skip if already registered\n if (existingEntries.includes(exportName)) {\n return source;\n }\n\n // Preserve comment lines\n const commentLines = lines\n .filter((line) => line.trim().startsWith(\"//\"))\n .map((line) => line.trimEnd());\n\n const commentBlock =\n commentLines.length > 0 ? \"\\n\" + commentLines.join(\"\\n\") : \"\";\n\n // Build entry lines with consistent 2-space indentation\n const allEntries = [...existingEntries, exportName];\n const entryBlock = allEntries.map((e) => ` ${e},`).join(\"\\n\");\n\n const replacement = `${declaration}[${commentBlock}\\n${entryBlock}\\n]`;\n\n return (\n source.slice(0, matchStart) +\n replacement +\n source.slice(matchStart + fullMatch.length)\n );\n}\n","import { Command } from \"commander\";\nimport type { PluginContext } from \"@fluid-app/fluid-cli\";\nimport chalk from \"chalk\";\nimport fs from \"fs-extra\";\nimport path from \"node:path\";\nimport type { WidgetCreateOptions } from \"../types.js\";\nimport {\n insertImport,\n insertIntoWidgetPackage,\n toCamelCase,\n toComponentName,\n toDisplayName,\n toWidgetType,\n validateWidgetName,\n} from \"../utils/widget-helpers.js\";\n\nconst WIDGET_CONFIG_PATH = path.join(\"src\", \"widgets.config.ts\");\n\nconst createSubcommand = new Command(\"create\")\n .description(\"Scaffold a new Remote DOM widget\")\n .argument(\"<name>\", \"Widget name in kebab-case (e.g., stock-ticker)\")\n .option(\n \"-c, --category <category>\",\n \"Widget category for palette grouping\",\n \"components\",\n )\n .action(async (name: string, options: WidgetCreateOptions) => {\n const cwd = process.cwd();\n const packageJsonPath = path.join(cwd, \"package.json\");\n const configPath = path.join(cwd, WIDGET_CONFIG_PATH);\n const widgetDir = path.join(cwd, \"src\", \"widgets\", name);\n\n const validationError = validateWidgetName(name);\n if (validationError) {\n console.error(chalk.red(`Error: ${validationError}`));\n process.exit(1);\n }\n\n if (!(await fs.pathExists(packageJsonPath))) {\n console.error(chalk.red(\"Error: No package.json found.\"));\n console.error(\n chalk.yellow(\"Run this command from a Fluid portal project root.\"),\n );\n process.exit(1);\n }\n\n if (await fs.pathExists(widgetDir)) {\n console.error(\n chalk.red(\n `Error: Widget directory already exists: src/widgets/${name}`,\n ),\n );\n process.exit(1);\n }\n\n const widgetType = toWidgetType(name);\n const componentName = toComponentName(widgetType);\n const camelName = toCamelCase(name);\n const exportName = camelName.endsWith(\"Widget\")\n ? camelName\n : `${camelName}Widget`;\n const displayName = toDisplayName(name);\n const category = options.category ?? \"components\";\n const importLine = `import { ${exportName} } from \"./widgets/${name}\";`;\n\n try {\n await fs.ensureDir(widgetDir);\n await fs.writeFile(\n path.join(widgetDir, \"index.tsx\"),\n createWidgetSource({\n name,\n widgetType,\n componentName,\n exportName,\n displayName,\n category,\n }),\n \"utf-8\",\n );\n\n if (await fs.pathExists(configPath)) {\n const configSource = await fs.readFile(configPath, \"utf-8\");\n if (isEmptyWidgetConfig(configSource)) {\n await fs.writeFile(\n configPath,\n createWidgetConfigSource(importLine, exportName),\n \"utf-8\",\n );\n } else {\n const withImport = insertImport(configSource, importLine);\n const updated = withImport\n ? insertIntoWidgetPackage(withImport, exportName)\n : null;\n if (!updated) {\n throw new Error(\n `${WIDGET_CONFIG_PATH} must export the starter widgetPackages placeholder or contain a defineWidgetPackage({ widgets: [...] }) declaration.`,\n );\n }\n await fs.writeFile(configPath, updated, \"utf-8\");\n }\n } else {\n await fs.writeFile(\n configPath,\n createWidgetConfigSource(importLine, exportName),\n \"utf-8\",\n );\n }\n } catch (error) {\n await fs.remove(widgetDir).catch(() => {});\n console.error(chalk.red(\"Error: Failed to scaffold Remote DOM widget.\"));\n console.error(error);\n process.exit(1);\n }\n\n console.log();\n console.log(\n chalk.green(\"Created widget:\") + ` src/widgets/${name}/index.tsx`,\n );\n console.log(\n chalk.green(\"Registered\") + ` in ${chalk.cyan(WIDGET_CONFIG_PATH)}`,\n );\n console.log();\n console.log(chalk.yellow(\"Next steps:\"));\n console.log(` 1. Edit ${chalk.cyan(`src/widgets/${name}/index.tsx`)}`);\n console.log(\n ` 2. Run ${chalk.cyan(\"fluid portal deploy --dry-run\")} to verify the package`,\n );\n });\n\nfunction createWidgetSource(options: {\n readonly name: string;\n readonly widgetType: string;\n readonly componentName: string;\n readonly exportName: string;\n readonly displayName: string;\n readonly category: string;\n}): string {\n return `import { defineWidget } from \"@fluid-app/portal-sdk/widgets/worker\";\n\ninterface ${options.widgetType}Props {\n readonly title?: string;\n}\n\nfunction ${options.componentName}({ title = ${JSON.stringify(options.displayName)} }: ${options.widgetType}Props) {\n return (\n <section>\n <h3>{title}</h3>\n <p>Edit this widget in src/widgets/${options.name}/index.tsx</p>\n </section>\n );\n}\n\nexport const ${options.exportName} = defineWidget({\n name: ${JSON.stringify(options.componentName)},\n component: ${options.componentName},\n displayName: ${JSON.stringify(options.displayName)},\n description: ${JSON.stringify(`A custom ${options.displayName.toLowerCase()} widget`)},\n icon: \"puzzle-piece\",\n category: ${JSON.stringify(options.category)},\n propertySchema: {\n fields: [{ key: \"title\", label: \"Title\", type: \"text\" }],\n },\n defaultProps: {\n title: ${JSON.stringify(options.displayName)},\n },\n});\n`;\n}\n\nfunction isEmptyWidgetConfig(source: string): boolean {\n return /export\\s+const\\s+widgetPackages\\s*=\\s*\\[\\]\\s+as\\s+const\\s*;/u.test(\n source,\n );\n}\n\nfunction createWidgetConfigSource(\n importLine: string,\n exportName: string,\n): string {\n return `import { defineWidgetPackage } from \"@fluid-app/portal-sdk/widgets/worker\";\n${importLine}\n\nexport const widgetPackage = defineWidgetPackage({\n scope: \"company\",\n packageStableId: \"portal-widgets\",\n version: \"0.0.0-dev\",\n widgets: [${exportName}],\n});\n`;\n}\n\nexport const widgetCommand: Command = new Command(\"widget\")\n .description(\"Manage Remote DOM portal widgets\")\n .addCommand(createSubcommand);\n\nexport function registerWidgetCommand(ctx: PluginContext): void {\n ctx.program.addCommand(widgetCommand);\n}\n","import { execa } from \"execa\";\nimport fs from \"fs-extra\";\nimport { createRequire } from \"node:module\";\nimport path from \"node:path\";\nimport { type Result, success, failure } from \"@fluid-app/fluid-cli\";\n\nconst WIDGET_CONFIG_PATH = \"src/widgets.config.ts\";\nconst SOURCE_PACKAGE_EXTRACT_FILENAME = \"extract-widget-packages.ts\";\nconst SOURCE_PACKAGE_OUTPUT_FILENAME = \"source-widget-packages.json\";\nconst SOURCE_PACKAGE_OUTPUT_SENTINEL = \"fluid-widget-source-packages:v1\";\nconst require = createRequire(import.meta.url);\nconst TSX_CLI_PATH = require.resolve(\"tsx/cli\");\n\nexport interface WidgetSourceConfig {\n readonly path: string;\n readonly relativePath: string;\n}\n\nexport interface WidgetSourceConfigLoadError {\n readonly code: \"CONFIG_LOAD_FAILED\" | \"INVALID_FORMAT\";\n readonly message: string;\n readonly details?: string;\n}\n\nexport async function resolvePortalWidgetSourceConfig(\n projectDir: string,\n): Promise<WidgetSourceConfig | undefined> {\n const candidate = path.join(projectDir, WIDGET_CONFIG_PATH);\n return (await fs.pathExists(candidate))\n ? { path: candidate, relativePath: WIDGET_CONFIG_PATH }\n : undefined;\n}\n\nexport async function loadSourceWidgetPackages(\n projectDir: string,\n): Promise<Result<unknown[], WidgetSourceConfigLoadError>> {\n const config = await resolvePortalWidgetSourceConfig(projectDir);\n if (!config) return success([]);\n\n let tempDir: string | undefined;\n\n try {\n tempDir = await createTempDirectory(projectDir);\n const extractFile = path.join(tempDir, SOURCE_PACKAGE_EXTRACT_FILENAME);\n const outputFile = path.join(tempDir, SOURCE_PACKAGE_OUTPUT_FILENAME);\n\n await fs.writeFile(\n extractFile,\n createSourcePackageExtractorScript({\n projectDir,\n configPath: config.path,\n }),\n { encoding: \"utf-8\", flag: \"wx\" },\n );\n\n await execa(process.execPath, [TSX_CLI_PATH, extractFile, outputFile], {\n cwd: projectDir,\n stdio: \"pipe\",\n env: {\n ...process.env,\n NODE_ENV: \"production\",\n },\n });\n\n const outputResult = await readSourcePackageExtractorOutput(outputFile);\n if (!outputResult.success) return outputResult;\n\n const parsed = outputResult.value;\n if (parsed.length === 0) return success([]);\n\n return success(parsed);\n } catch (err) {\n const error = err as { stderr?: string; message?: string };\n return failure({\n code: \"CONFIG_LOAD_FAILED\",\n message: `Failed to load widget packages from ${config.relativePath}`,\n details: error.stderr ?? error.message ?? String(err),\n });\n } finally {\n if (tempDir) await fs.remove(tempDir).catch(() => {});\n }\n}\n\nasync function createTempDirectory(projectDir: string): Promise<string> {\n const projectTmpDir = path.join(projectDir, \".fluid\", \"tmp\");\n try {\n await fs.ensureDir(projectTmpDir);\n return await fs.mkdtemp(path.join(projectTmpDir, \"widget-packages-\"));\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n throw new Error(\n `Unable to create project-local temporary directory at ${projectTmpDir}: ${message}`,\n );\n }\n}\n\nasync function readSourcePackageExtractorOutput(\n outputFile: string,\n): Promise<Result<unknown[], WidgetSourceConfigLoadError>> {\n let output: string;\n try {\n output = await fs.readFile(outputFile, \"utf-8\");\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return failure({\n code: \"CONFIG_LOAD_FAILED\",\n message: \"Widget package extractor did not write an output file\",\n details: message,\n });\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(output);\n } catch {\n return failure({\n code: \"INVALID_FORMAT\",\n message: \"Failed to parse widget package output file as JSON\",\n details: `Output was: ${output.slice(0, 200)}`,\n });\n }\n\n if (!isRecord(parsed) || parsed.sentinel !== SOURCE_PACKAGE_OUTPUT_SENTINEL) {\n return failure({\n code: \"INVALID_FORMAT\",\n message: \"Widget package extractor output file had an invalid sentinel\",\n });\n }\n\n if (!Array.isArray(parsed.data)) {\n return failure({\n code: \"INVALID_FORMAT\",\n message: \"Extracted widget package output is not an array\",\n details: `Expected an array, got: ${typeof parsed.data}`,\n });\n }\n\n return success(parsed.data);\n}\n\nfunction createSourcePackageExtractorScript(options: {\n readonly projectDir: string;\n readonly configPath: string;\n}): string {\n return `\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { createServer, normalizePath } from \"vite\";\n\nconst SOURCE_PACKAGE_MARKER = \"__fluidSourceWidgetPackage\";\nconst OUTPUT_SENTINEL = ${JSON.stringify(SOURCE_PACKAGE_OUTPUT_SENTINEL)};\nconst projectRoot = ${JSON.stringify(options.projectDir)};\nconst widgetConfigPath = ${JSON.stringify(options.configPath)};\nconst outputPath = process.argv[2];\nif (!outputPath) throw new Error(\"Missing widget package extractor output path.\");\n\nfunction toViteModuleId(modulePath) {\n const relativePath = path.relative(projectRoot, modulePath);\n if (!relativePath.startsWith(\"..\") && !path.isAbsolute(relativePath)) {\n return \"/\" + normalizePath(relativePath);\n }\n return normalizePath(modulePath);\n}\n\nconst server = await createServer({\n root: projectRoot,\n mode: \"production\",\n server: { middlewareMode: true },\n appType: \"custom\",\n logLevel: \"error\",\n clearScreen: false,\n resolve: {\n conditions: [\"module\", \"browser\", \"development|production\"],\n },\n ssr: {\n noExternal: [\"@fluid-app/portal-sdk\"],\n resolve: {\n conditions: [\"module\", \"node\", \"development|production\"],\n },\n },\n});\n\ntry {\n const widgetConfig = await server.ssrLoadModule(toViteModuleId(widgetConfigPath));\n\n function isSourceWidgetPackage(value) {\n return Boolean(value && typeof value === \"object\" && value[SOURCE_PACKAGE_MARKER] === true);\n }\n\n function collectSourceWidgetPackages(mod) {\n const candidates = [\n mod.widgetPackage,\n ...(Array.isArray(mod.widgetPackages) ? mod.widgetPackages : []),\n mod.default,\n ];\n const byPackageId = new Map();\n for (const candidate of candidates) {\n if (!isSourceWidgetPackage(candidate)) continue;\n if (!byPackageId.has(candidate.packageId)) byPackageId.set(candidate.packageId, candidate);\n }\n return Array.from(byPackageId.values());\n }\n\n function serializeWidget(widget) {\n if (!widget || typeof widget !== \"object\" || Array.isArray(widget)) {\n return widget;\n }\n const { component, ...metadata } = widget;\n return metadata;\n }\n\n function serializePackage(sourcePackage) {\n return {\n manifestVersion: sourcePackage.manifestVersion,\n scope: sourcePackage.scope,\n packageStableId: sourcePackage.packageStableId,\n packageId: sourcePackage.packageId,\n packageType: sourcePackage.packageType,\n version: sourcePackage.version,\n cssUrls: sourcePackage.cssUrls,\n widgets: Array.isArray(sourcePackage.widgets)\n ? sourcePackage.widgets.map(serializeWidget)\n : sourcePackage.widgets,\n };\n }\n\n await fs.writeFile(\n outputPath,\n JSON.stringify({\n sentinel: OUTPUT_SENTINEL,\n data: collectSourceWidgetPackages(widgetConfig).map(serializePackage),\n }),\n \"utf-8\",\n );\n} finally {\n await server.close();\n}\n`;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n","import fs from \"fs-extra\";\nimport path from \"node:path\";\nimport { createHash } from \"node:crypto\";\n\nexport const WIDGET_PACKAGE_BUILDER_VERSION = \"1\";\n\nexport interface ArtifactMetadata {\n readonly path: string;\n readonly sha256: string;\n readonly bytes: number;\n readonly contentType: string;\n}\n\nexport interface PublishManifestMetadata {\n readonly packageId: string;\n readonly version: string;\n readonly builderVersion: string;\n readonly cliVersion: string;\n readonly runtimeVersion: string;\n readonly manifestHash: string;\n readonly artifacts: readonly ArtifactMetadata[];\n}\n\nconst CONTENT_TYPES: Readonly<Record<string, string>> = {\n \".css\": \"text/css\",\n \".js\": \"application/javascript\",\n \".json\": \"application/json\",\n \".map\": \"application/json\",\n};\nconst CSS_ARTIFACT_PATH_PATTERN = /^[A-Za-z0-9._~-]+\\.css$/;\nconst JS_MAP_ARTIFACT_PATH_PATTERN = /^[A-Za-z0-9._~-]+\\.js\\.map$/;\n\nexport async function computeArtifactMetadata(\n filePath: string,\n baseDir: string,\n): Promise<ArtifactMetadata> {\n const relativePath = toPosixPath(path.relative(baseDir, filePath));\n assertAllowedArtifactPath(relativePath);\n await assertRegularArtifactFile(filePath, relativePath);\n const buffer = await fs.readFile(filePath);\n\n return {\n path: relativePath,\n sha256: sha256(buffer),\n bytes: buffer.byteLength,\n contentType: getArtifactContentType(filePath),\n };\n}\n\nexport async function collectWidgetPackageArtifacts(\n publishDir: string,\n): Promise<ArtifactMetadata[]> {\n const artifactPaths = await findArtifactPaths(publishDir);\n const artifacts = await Promise.all(\n artifactPaths.map((artifactPath) =>\n computeArtifactMetadata(artifactPath, publishDir),\n ),\n );\n\n assertRequiredUploadArtifacts(artifacts);\n\n return artifacts.sort((a, b) => a.path.localeCompare(b.path));\n}\n\nexport function createPublishManifestMetadata(options: {\n readonly packageId: string;\n readonly version: string;\n readonly cliVersion: string;\n readonly runtimeVersion: string;\n readonly manifestContent: string | Buffer;\n readonly artifacts: readonly ArtifactMetadata[];\n readonly builderVersion?: string;\n}): PublishManifestMetadata {\n return {\n packageId: options.packageId,\n version: options.version,\n builderVersion: options.builderVersion ?? WIDGET_PACKAGE_BUILDER_VERSION,\n cliVersion: options.cliVersion,\n runtimeVersion: options.runtimeVersion,\n manifestHash: sha256(options.manifestContent),\n artifacts: options.artifacts,\n };\n}\n\nexport function getArtifactContentType(filePath: string): string {\n const basename = path.basename(filePath);\n if (basename.endsWith(\".js.map\")) {\n return \"application/json\";\n }\n return CONTENT_TYPES[path.extname(filePath)] ?? \"application/octet-stream\";\n}\n\nexport function sha256(content: string | Buffer): string {\n return createHash(\"sha256\").update(content).digest(\"hex\");\n}\n\nexport function isWidgetPackageCssArtifactPath(relativePath: string): boolean {\n return CSS_ARTIFACT_PATH_PATTERN.test(relativePath);\n}\n\nexport function isWidgetPackageJsMapArtifactPath(\n relativePath: string,\n): boolean {\n return JS_MAP_ARTIFACT_PATH_PATTERN.test(relativePath);\n}\n\nasync function assertRegularArtifactFile(\n filePath: string,\n relativePath: string,\n): Promise<void> {\n const stat = await fs.lstat(filePath);\n if (stat.isSymbolicLink()) {\n throw new Error(\n `Widget publish artifact ${JSON.stringify(relativePath)} must be a regular file and must not be a symbolic link.`,\n );\n }\n if (!stat.isFile()) {\n throw new Error(\n `Widget publish artifact ${JSON.stringify(relativePath)} must be a regular file.`,\n );\n }\n}\n\nasync function findArtifactPaths(\n dir: string,\n baseDir: string = dir,\n): Promise<string[]> {\n if (!(await fs.pathExists(dir))) return [];\n\n const entries = await fs.readdir(dir, { withFileTypes: true });\n const paths = await Promise.all(\n entries.map(async (entry) => {\n const entryPath = path.join(dir, entry.name);\n const relativePath = toPosixPath(path.relative(baseDir, entryPath));\n\n if (entry.isSymbolicLink()) {\n throw new Error(\n `Widget publish artifact ${JSON.stringify(relativePath)} must be a regular file and must not be a symbolic link.`,\n );\n }\n\n if (isUploadArtifact(relativePath)) {\n if (!entry.isFile()) {\n throw new Error(\n `Widget publish artifact ${JSON.stringify(relativePath)} must be a regular file.`,\n );\n }\n return [entryPath];\n }\n\n if (entry.isDirectory()) {\n const nestedPaths = await findArtifactPaths(entryPath, baseDir);\n if (nestedPaths.length === 0) {\n throwUnsupportedArtifactPath(`${relativePath}/`);\n }\n return nestedPaths;\n }\n if (isAllowedGeneratedFile(relativePath)) return [];\n throwUnsupportedArtifactPath(relativePath);\n }),\n );\n\n return paths.flat();\n}\n\nfunction assertRequiredUploadArtifacts(\n artifacts: readonly ArtifactMetadata[],\n): void {\n const artifactPaths = new Set(artifacts.map((artifact) => artifact.path));\n for (const requiredPath of [\"widget.js\", \"manifest.json\"] as const) {\n if (artifactPaths.has(requiredPath)) continue;\n throw new Error(\n `Widget package publish output must include regular file ${JSON.stringify(requiredPath)}.`,\n );\n }\n}\n\nfunction assertAllowedArtifactPath(relativePath: string): void {\n if (isUploadArtifact(relativePath) || isAllowedGeneratedFile(relativePath))\n return;\n throwUnsupportedArtifactPath(relativePath);\n}\n\nfunction isUploadArtifact(relativePath: string): boolean {\n return (\n relativePath === \"widget.js\" ||\n relativePath === \"manifest.json\" ||\n isWidgetPackageCssArtifactPath(relativePath) ||\n isWidgetPackageJsMapArtifactPath(relativePath)\n );\n}\n\nfunction isAllowedGeneratedFile(relativePath: string): boolean {\n return (\n isFlatArtifactPath(relativePath) && relativePath === \"publish-manifest.json\"\n );\n}\n\nfunction isFlatArtifactPath(relativePath: string): boolean {\n return (\n relativePath.length > 0 &&\n !relativePath.includes(\"/\") &&\n !relativePath.includes(\"\\\\\")\n );\n}\n\nfunction throwUnsupportedArtifactPath(relativePath: string): never {\n throw new Error(\n `Unsupported widget publish artifact ${JSON.stringify(relativePath)}. ` +\n \"Allowed files are widget.js, manifest.json, top-level [A-Za-z0-9._~-]+.css, top-level [A-Za-z0-9._~-]+.js.map, and publish-manifest.json.\",\n );\n}\n\nfunction toPosixPath(value: string): string {\n return value.split(path.sep).join(\"/\");\n}\n","export const REMOTE_DOM_WIDGET_MANIFEST_VERSION = 1 as const;\nexport const REMOTE_DOM_WIDGET_RUNTIME = \"remote-dom\" as const;\n\nexport type JsonPrimitive = string | number | boolean | null;\n\nexport type JsonValue = JsonPrimitive | readonly JsonValue[] | JsonObject;\n\nexport interface JsonObject {\n readonly [key: string]: JsonValue;\n}\n\nexport interface RemoteDomWidgetCapabilityDeclaration {\n readonly name: string;\n readonly version: string;\n}\n\nexport interface RemoteDomWidgetDefinition {\n readonly type: string;\n readonly displayName: string;\n readonly description: string;\n readonly icon: string;\n readonly category: string;\n readonly propertySchema: JsonObject;\n readonly defaultProps: JsonObject;\n readonly container: \"inline\" | \"block\" | \"card\" | \"fullscreen\";\n readonly resizable: boolean | \"horizontal\" | \"vertical\" | \"both\";\n readonly minSdkVersion: string;\n readonly capabilities: readonly RemoteDomWidgetCapabilityDeclaration[];\n}\n\n/**\n * Canonical package contract shared by the CLI, catalog APIs, and portal hosts.\n * Catalog producers resolve every artifact to its public URL before returning\n * this descriptor; consumers do not translate legacy manifest shapes.\n */\nexport interface RemoteDomWidgetPackageDescriptor {\n readonly manifestVersion: typeof REMOTE_DOM_WIDGET_MANIFEST_VERSION;\n readonly runtime: typeof REMOTE_DOM_WIDGET_RUNTIME;\n readonly packageId: string;\n readonly source: \"company\" | \"droplet\";\n readonly version: string;\n readonly workerEntryUrl: string;\n readonly cssUrls: readonly string[];\n readonly assetUrls: readonly string[];\n readonly widgets: readonly RemoteDomWidgetDefinition[];\n}\n\nconst PACKAGE_FIELDS = new Set([\n \"manifestVersion\",\n \"runtime\",\n \"packageId\",\n \"source\",\n \"version\",\n \"workerEntryUrl\",\n \"cssUrls\",\n \"assetUrls\",\n \"widgets\",\n]);\n\nconst WIDGET_FIELDS = new Set([\n \"type\",\n \"displayName\",\n \"description\",\n \"icon\",\n \"category\",\n \"propertySchema\",\n \"defaultProps\",\n \"container\",\n \"resizable\",\n \"minSdkVersion\",\n \"capabilities\",\n]);\n\nconst CAPABILITY_FIELDS = new Set([\"name\", \"version\"]);\nconst UNSAFE_JSON_KEYS = new Set([\"__proto__\", \"constructor\", \"prototype\"]);\n\nexport function validateRemoteDomWidgetPackageDescriptor(\n value: unknown,\n): asserts value is RemoteDomWidgetPackageDescriptor {\n const invalidReason = getRemoteDomWidgetPackageInvalidReason(value);\n if (invalidReason) {\n throw new TypeError(`Invalid Remote DOM widget package: ${invalidReason}`);\n }\n}\n\nexport function isRemoteDomWidgetPackageDescriptor(\n value: unknown,\n): value is RemoteDomWidgetPackageDescriptor {\n return getRemoteDomWidgetPackageInvalidReason(value) === undefined;\n}\n\nfunction getRemoteDomWidgetPackageInvalidReason(\n value: unknown,\n): string | undefined {\n if (!isRecord(value)) return \"descriptor must be an object\";\n\n const unexpectedFieldReason = getUnexpectedFieldReason(\n value,\n PACKAGE_FIELDS,\n \"descriptor\",\n );\n if (unexpectedFieldReason) return unexpectedFieldReason;\n\n if (value.manifestVersion !== REMOTE_DOM_WIDGET_MANIFEST_VERSION) {\n return `manifestVersion must be ${REMOTE_DOM_WIDGET_MANIFEST_VERSION}`;\n }\n if (value.runtime !== REMOTE_DOM_WIDGET_RUNTIME) {\n return `runtime must be ${REMOTE_DOM_WIDGET_RUNTIME}`;\n }\n\n const packageIdReason = getNonEmptyStringReason(value.packageId, \"packageId\");\n if (packageIdReason) return packageIdReason;\n\n if (value.source !== \"company\" && value.source !== \"droplet\") {\n return \"source must be company or droplet\";\n }\n\n const versionReason = getNonEmptyStringReason(value.version, \"version\");\n if (versionReason) return versionReason;\n\n const workerEntryUrlReason = getHttpUrlReason(\n value.workerEntryUrl,\n \"workerEntryUrl\",\n );\n if (workerEntryUrlReason) return workerEntryUrlReason;\n\n const cssUrlsReason = getUrlArrayReason(value.cssUrls, \"cssUrls\");\n if (cssUrlsReason) return cssUrlsReason;\n\n const assetUrlsReason = getUrlArrayReason(value.assetUrls, \"assetUrls\");\n if (assetUrlsReason) return assetUrlsReason;\n\n if (!Array.isArray(value.widgets) || value.widgets.length === 0) {\n return \"widgets must be a non-empty array\";\n }\n\n const widgetTypes = new Set<string>();\n for (let index = 0; index < value.widgets.length; index += 1) {\n const widget = value.widgets[index];\n const widgetReason = getWidgetReason(widget, `widgets[${index}]`);\n if (widgetReason) return widgetReason;\n\n const widgetType = widget.type;\n if (widgetTypes.has(widgetType)) {\n return `widgets contains duplicate type ${widgetType}`;\n }\n widgetTypes.add(widgetType);\n }\n\n return undefined;\n}\n\nfunction getWidgetReason(value: unknown, path: string): string | undefined {\n if (!isRecord(value)) return `${path} must be an object`;\n\n const unexpectedFieldReason = getUnexpectedFieldReason(\n value,\n WIDGET_FIELDS,\n path,\n );\n if (unexpectedFieldReason) return unexpectedFieldReason;\n\n for (const field of [\n \"type\",\n \"displayName\",\n \"description\",\n \"icon\",\n \"category\",\n \"minSdkVersion\",\n ] as const) {\n const reason = getNonEmptyStringReason(value[field], `${path}.${field}`);\n if (reason) return reason;\n }\n\n if (!isRecord(value.propertySchema) || !isJsonValue(value.propertySchema)) {\n return `${path}.propertySchema must be a JSON object`;\n }\n if (!isRecord(value.defaultProps) || !isJsonValue(value.defaultProps)) {\n return `${path}.defaultProps must be a JSON object`;\n }\n\n if (\n value.container !== \"inline\" &&\n value.container !== \"block\" &&\n value.container !== \"card\" &&\n value.container !== \"fullscreen\"\n ) {\n return `${path}.container is unsupported`;\n }\n\n if (\n typeof value.resizable !== \"boolean\" &&\n value.resizable !== \"horizontal\" &&\n value.resizable !== \"vertical\" &&\n value.resizable !== \"both\"\n ) {\n return `${path}.resizable is unsupported`;\n }\n\n if (!Array.isArray(value.capabilities)) {\n return `${path}.capabilities must be an array`;\n }\n\n const capabilityNames = new Set<string>();\n for (let index = 0; index < value.capabilities.length; index += 1) {\n const capability = value.capabilities[index];\n const capabilityPath = `${path}.capabilities[${index}]`;\n if (!isRecord(capability)) return `${capabilityPath} must be an object`;\n\n const unexpectedFieldReason = getUnexpectedFieldReason(\n capability,\n CAPABILITY_FIELDS,\n capabilityPath,\n );\n if (unexpectedFieldReason) return unexpectedFieldReason;\n\n const capabilityName = capability.name;\n const nameReason = getNonEmptyStringReason(\n capabilityName,\n `${capabilityPath}.name`,\n );\n if (nameReason) return nameReason;\n if (typeof capabilityName !== \"string\") {\n return `${capabilityPath}.name must be a non-empty string`;\n }\n\n const versionReason = getNonEmptyStringReason(\n capability.version,\n `${capabilityPath}.version`,\n );\n if (versionReason) return versionReason;\n\n if (capabilityNames.has(capabilityName)) {\n return `${path}.capabilities contains duplicate name ${capabilityName}`;\n }\n capabilityNames.add(capabilityName);\n }\n\n return undefined;\n}\n\nfunction getUrlArrayReason(value: unknown, path: string): string | undefined {\n if (!Array.isArray(value)) return `${path} must be an array`;\n\n for (let index = 0; index < value.length; index += 1) {\n const reason = getHttpUrlReason(value[index], `${path}[${index}]`);\n if (reason) return reason;\n }\n return undefined;\n}\n\nfunction getHttpUrlReason(value: unknown, path: string): string | undefined {\n const stringReason = getNonEmptyStringReason(value, path);\n if (stringReason) return stringReason;\n if (typeof value !== \"string\") return `${path} must be a non-empty string`;\n\n try {\n const url = new URL(value);\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") {\n return `${path} must use http or https`;\n }\n if (url.username || url.password) {\n return `${path} must not contain credentials`;\n }\n } catch {\n return `${path} must be an absolute URL`;\n }\n\n return undefined;\n}\n\nfunction getNonEmptyStringReason(\n value: unknown,\n path: string,\n): string | undefined {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n return `${path} must be a non-empty string`;\n }\n if (value !== value.trim())\n return `${path} must not have surrounding whitespace`;\n if (/\\p{Cc}/u.test(value))\n return `${path} must not contain control characters`;\n return undefined;\n}\n\nexport function isJsonValue(value: unknown): value is JsonValue {\n return isJsonValueAtDepth(value, new WeakSet<object>(), 0);\n}\n\nfunction isJsonValueAtDepth(\n value: unknown,\n ancestors: WeakSet<object>,\n depth: number,\n): value is JsonValue {\n if (\n value === null ||\n typeof value === \"string\" ||\n typeof value === \"boolean\"\n ) {\n return true;\n }\n if (typeof value === \"number\") return Number.isFinite(value);\n if (typeof value !== \"object\" || depth > 50 || ancestors.has(value)) {\n return false;\n }\n\n ancestors.add(value);\n const prototype = Object.getPrototypeOf(value);\n const isJson = Array.isArray(value)\n ? value.every((item) => isJsonValueAtDepth(item, ancestors, depth + 1))\n : (prototype === Object.prototype || prototype === null) &&\n isRecord(value) &&\n Object.entries(value).every(\n ([key, item]) =>\n !UNSAFE_JSON_KEYS.has(key) &&\n isJsonValueAtDepth(item, ancestors, depth + 1),\n );\n ancestors.delete(value);\n return isJson;\n}\n\nfunction isRecord(value: unknown): value is Readonly<Record<string, unknown>> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction getUnexpectedFieldReason(\n value: Readonly<Record<string, unknown>>,\n allowedFields: ReadonlySet<string>,\n path: string,\n): string | undefined {\n const unexpectedField = Object.keys(value).find(\n (field) => !allowedFields.has(field),\n );\n return unexpectedField\n ? `${path} contains unsupported field ${unexpectedField}`\n : undefined;\n}\n","import { isWidgetPackageCssArtifactPath } from \"./widget-package-artifacts.js\";\nimport {\n isJsonValue,\n type JsonObject,\n type RemoteDomWidgetDefinition,\n} from \"@fluid-app/portal-core/remote-dom-widget-package\";\n\nconst URL_SAFE_SEGMENT_SOURCE = \"[A-Za-z0-9][A-Za-z0-9_~-]*\";\nconst URL_SAFE_WIDGET_NAME_PATTERN = new RegExp(`^${URL_SAFE_SEGMENT_SOURCE}$`);\nconst URL_SAFE_DOTTED_IDENTIFIER_PATTERN = new RegExp(\n `^${URL_SAFE_SEGMENT_SOURCE}(?:\\\\.${URL_SAFE_SEGMENT_SOURCE})*$`,\n);\nconst SEMVER_CORE_PATTERN = \"(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)\";\nconst SEMVER_PRERELEASE_IDENTIFIER_PATTERN =\n \"(?:0|[1-9]\\\\d*|[0-9A-Za-z-]*[A-Za-z-][0-9A-Za-z-]*)\";\nconst SEMVER_URL_SAFE_PATTERN = new RegExp(\n `^${SEMVER_CORE_PATTERN}(?:-${SEMVER_PRERELEASE_IDENTIFIER_PATTERN}(?:\\\\.${SEMVER_PRERELEASE_IDENTIFIER_PATTERN})*)?$`,\n);\nconst WIDGET_PACKAGE_MANIFEST_VERSION = 1;\nconst DEFAULT_WIDGET_ICON = \"box\";\nconst DEFAULT_WIDGET_CATEGORY = \"components\";\nconst DEFAULT_WIDGET_CONTAINER = \"block\";\nconst DEFAULT_MIN_SDK_VERSION = \"0.0.0\";\n\nexport type WidgetPackageOwnerKind = \"company\" | \"droplet\";\n\nexport type WidgetRuntimeContainer = RemoteDomWidgetDefinition[\"container\"];\nexport type WidgetRuntimeResizable = RemoteDomWidgetDefinition[\"resizable\"];\n\nexport interface WidgetCapabilityMetadata {\n readonly name: string;\n readonly version?: string;\n}\n\nexport interface SourceWidgetMetadata {\n readonly name: string;\n readonly displayName?: string;\n readonly description?: string;\n readonly icon?: string;\n readonly category?: string;\n readonly propertySchema?: JsonObject;\n readonly defaultProps?: JsonObject;\n readonly container?: unknown;\n readonly minSdkVersion?: string;\n readonly resizable?: unknown;\n readonly capabilities?: readonly WidgetCapabilityMetadata[];\n}\n\nexport interface SourceWidgetPackageMetadata {\n readonly manifestVersion?: number;\n readonly scope?: string;\n readonly packageStableId?: string;\n readonly packageId: string;\n readonly packageType?: string;\n readonly version: string;\n readonly widgets: readonly SourceWidgetMetadata[];\n readonly cssUrls?: readonly string[];\n}\n\nexport interface WidgetDescriptorMetadata extends RemoteDomWidgetDefinition {\n readonly name: string;\n}\n\ndeclare const WIDGET_PACKAGE_ARTIFACT_PATH: unique symbol;\n\n/** A package-local artifact path written into the uploaded manifest. */\nexport type WidgetPackageArtifactPath = string & {\n readonly [WIDGET_PACKAGE_ARTIFACT_PATH]: true;\n};\n\n/**\n * Upload-time manifest whose artifact references are package-local paths.\n *\n * This is deliberately distinct from RemoteDomWidgetPackageDescriptor, whose\n * catalog-facing artifact fields must already be resolved to absolute URLs.\n */\nexport interface WidgetPackagePublishManifest {\n readonly manifestVersion: 1;\n readonly runtime: \"remote-dom\";\n readonly packageId: string;\n readonly source: WidgetPackageOwnerKind;\n readonly version: string;\n readonly workerEntryUrl: WidgetPackageArtifactPath;\n readonly cssUrls: readonly WidgetPackageArtifactPath[];\n readonly assetUrls: readonly WidgetPackageArtifactPath[];\n readonly widgets: readonly RemoteDomWidgetDefinition[];\n}\n\nexport interface ValidatedWidgetPackageBuild {\n readonly sourcePackage: SourceWidgetPackageMetadata;\n readonly packageKey: string;\n readonly packageId: string;\n readonly owner: WidgetPackageOwnerKind;\n readonly version: string;\n readonly widgets: readonly WidgetDescriptorMetadata[];\n}\n\nexport type WidgetPackageValidationErrorCode =\n | \"NO_SOURCE_PACKAGE\"\n | \"MULTIPLE_SOURCE_PACKAGES\"\n | \"INVALID_SOURCE_PACKAGE\"\n | \"INVALID_WIDGET_METADATA\"\n | \"UNSUPPORTED_OWNER\"\n | \"INVALID_PACKAGE_KEY\"\n | \"INVALID_VERSION\"\n | \"NO_WIDGETS\"\n | \"INVALID_WIDGET_NAME\"\n | \"INVALID_WIDGET_TYPE\"\n | \"DUPLICATE_WIDGET\";\n\nexport interface WidgetPackageValidationError {\n readonly code: WidgetPackageValidationErrorCode;\n readonly message: string;\n readonly path?: string;\n}\n\nexport interface WidgetPackageValidationResult {\n readonly success: boolean;\n readonly value?: ValidatedWidgetPackageBuild;\n readonly errors: readonly WidgetPackageValidationError[];\n}\n\nexport interface ValidateSourceWidgetPackageOptions {\n readonly owner?: WidgetPackageOwnerKind;\n}\n\nexport function validateSingleSourceWidgetPackage(\n sourcePackages: readonly unknown[],\n options: ValidateSourceWidgetPackageOptions = {},\n): WidgetPackageValidationResult {\n if (sourcePackages.length === 0) {\n return validationFailure({\n code: \"NO_SOURCE_PACKAGE\",\n message:\n \"No defineWidgetPackage source package was found. Export widgetPackage, widgetPackages, or a default widget package from src/widgets.config.ts.\",\n });\n }\n\n if (sourcePackages.length > 1) {\n return validationFailure({\n code: \"MULTIPLE_SOURCE_PACKAGES\",\n message:\n \"Publish/deploy supports exactly one defineWidgetPackage source package. Export one package or split packages into separate builds.\",\n });\n }\n\n const sourcePackageValue = sourcePackages[0];\n if (!sourcePackageValue) {\n return validationFailure({\n code: \"NO_SOURCE_PACKAGE\",\n message: \"No defineWidgetPackage source package was found.\",\n });\n }\n\n const shapeErrors = validateSourcePackageShape(sourcePackageValue);\n if (shapeErrors.length > 0) {\n return { success: false, errors: shapeErrors };\n }\n const sourcePackage = sourcePackageValue as SourceWidgetPackageMetadata;\n\n const errors: WidgetPackageValidationError[] = [];\n const owner = options.owner ?? sourcePackage.packageType;\n if (owner !== \"company\" && owner !== \"droplet\") {\n errors.push({\n code: \"UNSUPPORTED_OWNER\",\n path: \"packageType\",\n message:\n 'Widget package publish/deploy currently supports company and droplet owners. Set packageType to \"company\" or \"droplet\".',\n });\n }\n\n const resolvedOwner = owner === \"droplet\" ? \"droplet\" : \"company\";\n const packageKey = resolvePackageKey(sourcePackage, resolvedOwner);\n const packageKeyPath = readNonEmptyTrimmedString(\n sourcePackage.packageStableId,\n )\n ? \"packageStableId\"\n : \"packageId\";\n if (!isUrlSafeDottedIdentifier(packageKey)) {\n errors.push({\n code: \"INVALID_PACKAGE_KEY\",\n path: packageKeyPath,\n message:\n \"Widget package key must be URL-safe dot-separated text (letters, numbers, '.', '_', '~', and '-' only).\",\n });\n }\n\n if (!isUrlSafeSemver(sourcePackage.version)) {\n errors.push({\n code: \"INVALID_VERSION\",\n path: \"version\",\n message:\n \"Widget package version must be a URL-safe SemVer version without build metadata (for example 1.2.3 or 1.2.3-beta.1).\",\n });\n }\n\n if (sourcePackage.widgets.length === 0) {\n errors.push({\n code: \"NO_WIDGETS\",\n path: \"widgets\",\n message: \"Widget package must include at least one widget.\",\n });\n }\n\n const packageId = `${resolvedOwner}.${packageKey}`;\n const seenNames = new Set<string>();\n const seenTypes = new Set<string>();\n const widgets: WidgetDescriptorMetadata[] = [];\n\n sourcePackage.widgets.forEach((widget, index) => {\n const path = `widgets[${index}]`;\n if (!isUrlSafeWidgetName(widget.name)) {\n errors.push({\n code: \"INVALID_WIDGET_NAME\",\n path: `${path}.name`,\n message:\n \"Widget name must be URL-safe text (letters, numbers, '_', '~', and '-' only) and cannot be empty.\",\n });\n return;\n }\n\n if (seenNames.has(widget.name)) {\n errors.push({\n code: \"DUPLICATE_WIDGET\",\n path: `${path}.name`,\n message: `Duplicate widget name \"${widget.name}\" found in package ${packageKey}.`,\n });\n return;\n }\n seenNames.add(widget.name);\n\n const type = `${packageId}.${widget.name}`;\n if (!isUrlSafeDottedIdentifier(type)) {\n errors.push({\n code: \"INVALID_WIDGET_TYPE\",\n path: `${path}.type`,\n message: `Generated widget type \"${type}\" is not URL-safe.`,\n });\n return;\n }\n\n if (seenTypes.has(type)) {\n errors.push({\n code: \"DUPLICATE_WIDGET\",\n path: `${path}.type`,\n message: `Duplicate generated widget type \"${type}\" found.`,\n });\n return;\n }\n seenTypes.add(type);\n\n widgets.push(sourceWidgetToDescriptor(widget, type));\n });\n\n if (errors.length > 0) return { success: false, errors };\n\n return {\n success: true,\n value: {\n sourcePackage,\n packageKey,\n packageId,\n owner: resolvedOwner,\n version: sourcePackage.version,\n widgets,\n },\n errors: [],\n };\n}\n\nexport function buildWidgetPackagePublishManifest(\n validated: ValidatedWidgetPackageBuild,\n options: {\n readonly cssUrls?: readonly string[];\n } = {},\n): WidgetPackagePublishManifest {\n return {\n manifestVersion: WIDGET_PACKAGE_MANIFEST_VERSION,\n runtime: \"remote-dom\",\n packageId: validated.packageId,\n source: validated.owner,\n version: validated.version,\n // Published manifests name package-local artifacts. Catalog producers\n // resolve these paths to absolute URLs before returning the descriptor to\n // a host, where portal-core performs its absolute-URL ingress validation.\n workerEntryUrl: toWidgetPackageArtifactPath(\"widget.js\"),\n cssUrls: mergeCssUrls(validated.sourcePackage.cssUrls, options.cssUrls),\n assetUrls: [],\n widgets: validated.widgets.map(({ name: _name, ...widget }) => widget),\n };\n}\n\nfunction sourceWidgetToDescriptor(\n widget: SourceWidgetMetadata,\n type: string,\n): WidgetDescriptorMetadata {\n const displayName = widget.displayName ?? widget.name;\n\n return {\n type,\n name: widget.name,\n displayName,\n description: widget.description ?? `Custom widget ${displayName}`,\n icon: widget.icon ?? DEFAULT_WIDGET_ICON,\n category: widget.category ?? DEFAULT_WIDGET_CATEGORY,\n propertySchema: normalizePropertySchema(widget.propertySchema, type),\n defaultProps: widget.defaultProps ?? {},\n container: normalizeContainer(widget.container),\n minSdkVersion: widget.minSdkVersion ?? DEFAULT_MIN_SDK_VERSION,\n resizable: normalizeResizable(widget.resizable),\n capabilities: normalizeCapabilities(widget.capabilities ?? []),\n };\n}\n\nfunction normalizeCapabilities(\n capabilities: readonly WidgetCapabilityMetadata[],\n): readonly Required<WidgetCapabilityMetadata>[] {\n return capabilities.map((capability) => ({\n name: capability.name,\n version: capability.version ?? \"1\",\n }));\n}\n\nfunction validationFailure(\n error: WidgetPackageValidationError,\n): WidgetPackageValidationResult {\n return { success: false, errors: [error] };\n}\n\nfunction validateSourcePackageShape(\n value: unknown,\n): WidgetPackageValidationError[] {\n if (!isRecord(value)) {\n return [\n {\n code: \"INVALID_SOURCE_PACKAGE\",\n message: \"Widget source package must be a JSON object.\",\n },\n ];\n }\n\n const errors: WidgetPackageValidationError[] = [];\n requireStringField(value, \"packageId\", \"packageId\", errors);\n requireStringField(value, \"version\", \"version\", errors);\n validateOptionalStringField(value, \"scope\", \"scope\", errors);\n validateOptionalStringField(\n value,\n \"packageStableId\",\n \"packageStableId\",\n errors,\n );\n validateOptionalStringField(value, \"packageType\", \"packageType\", errors);\n\n if (value.cssUrls !== undefined) {\n if (!Array.isArray(value.cssUrls)) {\n errors.push({\n code: \"INVALID_SOURCE_PACKAGE\",\n path: \"cssUrls\",\n message:\n \"Widget package cssUrls must be an array of strings when present.\",\n });\n } else {\n value.cssUrls.forEach((cssUrl, index) => {\n validateCssUrl(cssUrl, `cssUrls[${index}]`, errors);\n });\n }\n }\n\n if (!Array.isArray(value.widgets)) {\n errors.push({\n code: \"INVALID_SOURCE_PACKAGE\",\n path: \"widgets\",\n message: \"Widget package widgets must be an array.\",\n });\n return errors;\n }\n\n value.widgets.forEach((widget, index) => {\n const widgetPath = `widgets[${index}]`;\n if (!isRecord(widget)) {\n errors.push({\n code: \"INVALID_WIDGET_METADATA\",\n path: widgetPath,\n message: \"Widget metadata must be a JSON object.\",\n });\n return;\n }\n\n requireStringField(widget, \"name\", `${widgetPath}.name`, errors, {\n code: \"INVALID_WIDGET_NAME\",\n message: \"Widget name must be a string and cannot be empty.\",\n });\n validateOptionalStringField(\n widget,\n \"displayName\",\n `${widgetPath}.displayName`,\n errors,\n { requireNonEmpty: true },\n );\n validateOptionalStringField(\n widget,\n \"description\",\n `${widgetPath}.description`,\n errors,\n { requireNonEmpty: true },\n );\n validateOptionalStringField(widget, \"icon\", `${widgetPath}.icon`, errors, {\n requireNonEmpty: true,\n });\n validateOptionalStringField(\n widget,\n \"category\",\n `${widgetPath}.category`,\n errors,\n { requireNonEmpty: true },\n );\n validateOptionalStringField(\n widget,\n \"minSdkVersion\",\n `${widgetPath}.minSdkVersion`,\n errors,\n { requireNonEmpty: true },\n );\n validateOptionalJsonRecordField(\n widget,\n \"propertySchema\",\n `${widgetPath}.propertySchema`,\n errors,\n );\n validateOptionalJsonRecordField(\n widget,\n \"defaultProps\",\n `${widgetPath}.defaultProps`,\n errors,\n );\n validateOptionalCapabilities(\n widget,\n \"capabilities\",\n `${widgetPath}.capabilities`,\n errors,\n );\n });\n\n return errors;\n}\n\nfunction requireStringField(\n record: Record<string, unknown>,\n key: string,\n path: string,\n errors: WidgetPackageValidationError[],\n override?: Pick<WidgetPackageValidationError, \"code\" | \"message\">,\n): void {\n const value = record[key];\n if (typeof value === \"string\" && value.trim().length > 0) return;\n\n errors.push({\n code: override?.code ?? \"INVALID_SOURCE_PACKAGE\",\n path,\n message:\n override?.message ?? `${path} must be a string and cannot be empty.`,\n });\n}\n\nfunction validateOptionalStringField(\n record: Record<string, unknown>,\n key: string,\n path: string,\n errors: WidgetPackageValidationError[],\n options: { readonly requireNonEmpty?: boolean } = {},\n): void {\n const value = record[key];\n if (value === undefined) return;\n if (typeof value === \"string\") {\n if (!options.requireNonEmpty || value.trim().length > 0) return;\n errors.push({\n code: \"INVALID_SOURCE_PACKAGE\",\n path,\n message: `${path} must be a non-empty string when present.`,\n });\n return;\n }\n\n errors.push({\n code: \"INVALID_SOURCE_PACKAGE\",\n path,\n message: `${path} must be a string when present.`,\n });\n}\n\nfunction validateCssUrl(\n value: unknown,\n cssUrlPath: string,\n errors: WidgetPackageValidationError[],\n): void {\n if (typeof value !== \"string\" || value.trim().length === 0) {\n errors.push({\n code: \"INVALID_SOURCE_PACKAGE\",\n path: cssUrlPath,\n message: \"Widget package cssUrls entries must be non-empty strings.\",\n });\n return;\n }\n\n if (!isWidgetPackageCssArtifactPath(value)) {\n errors.push({\n code: \"INVALID_SOURCE_PACKAGE\",\n path: cssUrlPath,\n message:\n \"Widget package cssUrls entries must be top-level generated CSS artifact filenames matching [A-Za-z0-9._~-]+.css. Self-host external CSS or fonts via @font-face in widget CSS so the build can isolate them.\",\n });\n }\n}\n\nfunction validateOptionalJsonRecordField(\n record: Record<string, unknown>,\n key: string,\n path: string,\n errors: WidgetPackageValidationError[],\n): void {\n const value = record[key];\n if (value === undefined) return;\n if (isRecord(value) && isJsonValue(value)) return;\n\n errors.push({\n code: \"INVALID_WIDGET_METADATA\",\n path,\n message: `${path} must be a JSON object when present.`,\n });\n}\n\nfunction validateOptionalCapabilities(\n record: Record<string, unknown>,\n key: string,\n path: string,\n errors: WidgetPackageValidationError[],\n): void {\n const value = record[key];\n if (value === undefined) return;\n if (!Array.isArray(value)) {\n errors.push({\n code: \"INVALID_WIDGET_METADATA\",\n path,\n message: `${path} must be an array when present.`,\n });\n return;\n }\n\n const names = new Set<string>();\n value.forEach((candidate, index) => {\n const capabilityPath = `${path}[${index}]`;\n if (!isRecord(candidate)) {\n errors.push({\n code: \"INVALID_WIDGET_METADATA\",\n path: capabilityPath,\n message: `${capabilityPath} must be a JSON object.`,\n });\n return;\n }\n\n const name = candidate.name;\n if (!isCanonicalMetadataString(name)) {\n errors.push({\n code: \"INVALID_WIDGET_METADATA\",\n path: `${capabilityPath}.name`,\n message: `${capabilityPath}.name must be a non-empty string without surrounding whitespace or control characters.`,\n });\n return;\n }\n\n const version = candidate.version;\n if (version !== undefined && !isCanonicalMetadataString(version)) {\n errors.push({\n code: \"INVALID_WIDGET_METADATA\",\n path: `${capabilityPath}.version`,\n message: `${capabilityPath}.version must be a non-empty string without surrounding whitespace or control characters when present.`,\n });\n }\n\n if (names.has(name)) {\n errors.push({\n code: \"INVALID_WIDGET_METADATA\",\n path: `${capabilityPath}.name`,\n message: `${path} must not contain duplicate capability name ${JSON.stringify(name)}.`,\n });\n return;\n }\n names.add(name);\n });\n}\n\nfunction isCanonicalMetadataString(value: unknown): value is string {\n return (\n typeof value === \"string\" &&\n value.length > 0 &&\n value === value.trim() &&\n !/\\p{Cc}/u.test(value)\n );\n}\n\nfunction mergeCssUrls(\n sourceCssUrls: readonly string[] | undefined,\n generatedCssUrls: readonly string[] | undefined,\n): WidgetPackageArtifactPath[] {\n const generatedCssUrlSet = new Set(generatedCssUrls ?? []);\n generatedCssUrls?.forEach((cssUrl, index) => {\n validateGeneratedCssArtifactUrl(cssUrl, `generated cssUrls[${index}]`);\n });\n sourceCssUrls?.forEach((cssUrl, index) => {\n validateGeneratedCssArtifactUrl(cssUrl, `cssUrls[${index}]`);\n if (generatedCssUrlSet.has(cssUrl)) return;\n\n throw new Error(\n `cssUrls[${index}] URL ${JSON.stringify(cssUrl)} must match a generated top-level CSS artifact. ` +\n \"Import the CSS from the widget package bundle or remove it from cssUrls.\",\n );\n });\n\n return Array.from(\n new Set([...(sourceCssUrls ?? []), ...(generatedCssUrls ?? [])]),\n ).map(toWidgetPackageArtifactPath);\n}\n\nfunction toWidgetPackageArtifactPath(value: string): WidgetPackageArtifactPath {\n return value as WidgetPackageArtifactPath;\n}\n\nfunction validateGeneratedCssArtifactUrl(\n value: string,\n cssUrlPath: string,\n): void {\n if (value.trim().length === 0) {\n throw new Error(`${cssUrlPath} must be a non-empty string.`);\n }\n\n if (!isWidgetPackageCssArtifactPath(value)) {\n throw new Error(\n `${cssUrlPath} must be a top-level generated CSS artifact filename matching [A-Za-z0-9._~-]+.css. Self-host external CSS or fonts via @font-face in widget CSS so the build can isolate them.`,\n );\n }\n}\n\nfunction normalizePropertySchema(\n value: JsonObject | undefined,\n widgetType: string,\n): JsonObject {\n return { ...(value ?? {}), widgetType };\n}\n\nfunction normalizeContainer(value: unknown): WidgetRuntimeContainer {\n if (\n value === \"inline\" ||\n value === \"block\" ||\n value === \"card\" ||\n value === \"fullscreen\"\n ) {\n return value;\n }\n\n return DEFAULT_WIDGET_CONTAINER;\n}\n\nfunction normalizeResizable(value: unknown): WidgetRuntimeResizable {\n if (typeof value === \"boolean\") return value;\n if (value === \"horizontal\" || value === \"vertical\" || value === \"both\") {\n return value;\n }\n if (!isRecord(value)) return false;\n\n const horizontal = value.horizontal === true;\n const vertical = value.vertical === true;\n\n if (horizontal && vertical) return \"both\";\n if (horizontal) return \"horizontal\";\n if (vertical) return \"vertical\";\n return false;\n}\n\nfunction resolvePackageKey(\n sourcePackage: SourceWidgetPackageMetadata,\n owner: WidgetPackageOwnerKind,\n): string {\n const packageStableId = readNonEmptyTrimmedString(\n sourcePackage.packageStableId,\n );\n if (packageStableId) {\n return prefixPackageStableIdWithNonOwnerScope(\n packageStableId,\n sourcePackage.scope,\n );\n }\n\n return stripOwnerScopeFromPackageId(\n sourcePackage.packageId,\n sourcePackage.scope,\n owner,\n );\n}\n\nfunction prefixPackageStableIdWithNonOwnerScope(\n packageStableId: string,\n sourceScope: string | undefined,\n): string {\n const sourceScopeText = readNonEmptyTrimmedString(sourceScope);\n if (!sourceScopeText || isWidgetPackageOwnerKind(sourceScopeText)) {\n return packageStableId;\n }\n\n const sourceScopePrefix = `${sourceScopeText}.`;\n if (packageStableId.startsWith(sourceScopePrefix)) return packageStableId;\n return `${sourceScopeText}.${packageStableId}`;\n}\n\nfunction stripOwnerScopeFromPackageId(\n packageId: string,\n sourceScope: string | undefined,\n owner: WidgetPackageOwnerKind,\n): string {\n const ownerPrefix = `${owner}.`;\n if (\n packageId.startsWith(ownerPrefix) &&\n packageId.length > ownerPrefix.length\n ) {\n return packageId.slice(ownerPrefix.length);\n }\n\n const sourceScopeText = readNonEmptyTrimmedString(sourceScope);\n if (\n isWidgetPackageOwnerKind(sourceScopeText) &&\n packageId.startsWith(`${sourceScopeText}.`) &&\n packageId.length > sourceScopeText.length + 1\n ) {\n return packageId.slice(sourceScopeText.length + 1);\n }\n\n return packageId;\n}\n\nfunction readNonEmptyTrimmedString(\n value: string | undefined,\n): string | undefined {\n const trimmed = value?.trim();\n return trimmed ? trimmed : undefined;\n}\n\nfunction isWidgetPackageOwnerKind(\n value: string | undefined,\n): value is WidgetPackageOwnerKind {\n return value === \"company\" || value === \"droplet\";\n}\n\nfunction isUrlSafeDottedIdentifier(value: string): boolean {\n return URL_SAFE_DOTTED_IDENTIFIER_PATTERN.test(value);\n}\n\nfunction isUrlSafeWidgetName(value: string): boolean {\n return URL_SAFE_WIDGET_NAME_PATTERN.test(value);\n}\n\nfunction isUrlSafeSemver(value: string): boolean {\n return SEMVER_URL_SAFE_PATTERN.test(value);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n","import { execa } from \"execa\";\nimport fs from \"fs-extra\";\nimport path from \"node:path\";\nimport { createRequire } from \"node:module\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\nimport { type Result, success, failure } from \"@fluid-app/fluid-cli\";\nimport {\n loadSourceWidgetPackages,\n resolvePortalWidgetSourceConfig,\n} from \"./widget-package-config.js\";\nimport {\n buildWidgetPackagePublishManifest,\n validateSingleSourceWidgetPackage,\n type ValidatedWidgetPackageBuild,\n type WidgetPackageOwnerKind,\n} from \"./widget-package-validation.js\";\nimport {\n collectWidgetPackageArtifacts,\n createPublishManifestMetadata,\n isWidgetPackageCssArtifactPath,\n} from \"./widget-package-artifacts.js\";\n\nconst DEFAULT_PUBLISH_DIR = \".fluid/widget-dist\";\nconst DEFAULT_RUNTIME_VERSION = \"1\";\nconst TEMP_BUILD_DIR = \".fluid/widget-build\";\nconst TEMP_CLI_DIR = \".fluid/tmp\";\nconst PROJECT_VITE_CONFIG_CANDIDATES = [\n \"vite.config.ts\",\n \"vite.config.mts\",\n \"vite.config.js\",\n \"vite.config.mjs\",\n] as const;\nconst BLOCKED_FLUID_PROJECT_PLUGIN_NAMES = [\n \"fluid-portal-dev\",\n \"fluid-backend-dev\",\n] as const;\nconst require = createRequire(import.meta.url);\n\nexport interface BuildSharedWidgetPackageOptions {\n readonly projectDir: string;\n readonly publishDir?: string;\n readonly owner?: WidgetPackageOwnerKind;\n}\n\nexport interface SharedWidgetPackageBuildResult {\n readonly publishDir: string;\n readonly packageId: string;\n readonly version: string;\n readonly manifestPath: string;\n readonly publishManifestPath: string;\n readonly widgetScriptPath: string;\n}\n\nexport interface SharedWidgetPackageBuildError {\n readonly code:\n | \"CONFIG_LOAD_FAILED\"\n | \"VALIDATION_FAILED\"\n | \"INVALID_PUBLISH_DIR\"\n | \"WRITE_FAILED\";\n readonly message: string;\n readonly details?: string;\n}\n\nexport interface ValidateSharedWidgetPackagePublishDirOptions {\n readonly optionName?: string;\n}\n\nexport function validateSharedWidgetPackagePublishDir(\n projectDir: string,\n publishDir: string = DEFAULT_PUBLISH_DIR,\n options: ValidateSharedWidgetPackagePublishDirOptions = {},\n): void {\n const label = options.optionName ?? \"publishDir\";\n\n if (path.isAbsolute(publishDir)) {\n throw new Error(`${label} must be relative to the project directory.`);\n }\n\n const normalizedPublishDir = normalizePublishDirForValidation(publishDir);\n if (\n normalizedPublishDir === \".fluid\" ||\n !normalizedPublishDir.startsWith(\".fluid/\")\n ) {\n throw new Error(`${label} must be a relative path under .fluid/.`);\n }\n\n if (isPathWithinReservedDir(normalizedPublishDir, TEMP_BUILD_DIR)) {\n throw new Error(\n `${label} must not be ${TEMP_BUILD_DIR} because it is reserved for temporary builder output.`,\n );\n }\n\n if (isPathWithinReservedDir(normalizedPublishDir, TEMP_CLI_DIR)) {\n throw new Error(\n `${label} must not be ${TEMP_CLI_DIR} because it is reserved for Fluid CLI temporary files.`,\n );\n }\n\n const resolvedProjectDir = path.resolve(projectDir);\n const resolvedPublishDir = path.resolve(resolvedProjectDir, publishDir);\n const relative = path.relative(resolvedProjectDir, resolvedPublishDir);\n\n if (\n relative === \"\" ||\n relative.startsWith(\"..\") ||\n path.isAbsolute(relative)\n ) {\n throw new Error(`${label} must stay inside the project directory.`);\n }\n}\n\nexport async function buildSharedWidgetPackage(\n options: BuildSharedWidgetPackageOptions,\n): Promise<\n Result<SharedWidgetPackageBuildResult, SharedWidgetPackageBuildError>\n> {\n const publishDirInput = options.publishDir ?? DEFAULT_PUBLISH_DIR;\n try {\n await validateWidgetPackageOutputPaths(options.projectDir, publishDirInput);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n return failure({\n code: \"INVALID_PUBLISH_DIR\",\n message: \"Unsafe widget package publish directory\",\n details: error.message,\n });\n }\n\n const sourcePackageResult = await loadSourceWidgetPackages(\n options.projectDir,\n );\n if (!sourcePackageResult.success) {\n return failure({\n code: \"CONFIG_LOAD_FAILED\",\n message: sourcePackageResult.error.message,\n details: sourcePackageResult.error.details,\n });\n }\n\n const validation = validateSingleSourceWidgetPackage(\n sourcePackageResult.value,\n { owner: options.owner },\n );\n if (!validation.success || !validation.value) {\n return failure({\n code: \"VALIDATION_FAILED\",\n message: validation.errors.map((error) => error.message).join(\"\\n\"),\n });\n }\n\n try {\n await validateWidgetPackageOutputPaths(options.projectDir, publishDirInput);\n\n const publishDir = path.resolve(options.projectDir, publishDirInput);\n const widgetScriptPath = path.join(publishDir, \"widget.js\");\n await fs.emptyDir(publishDir);\n\n const tempDir = path.resolve(options.projectDir, TEMP_BUILD_DIR);\n await fs.emptyDir(tempDir);\n let buildFailure: { readonly error: unknown } | undefined;\n try {\n await buildWidgetScriptBundle({\n projectDir: options.projectDir,\n tempDir,\n publishDir,\n validated: validation.value,\n });\n } catch (error) {\n buildFailure = { error };\n }\n\n try {\n await fs.remove(tempDir);\n } catch {\n const cleanupFailure = new Error(\n \"Failed to remove reserved temporary build directory.\",\n );\n if (buildFailure) {\n const primaryError =\n buildFailure.error instanceof Error\n ? buildFailure.error\n : new Error(String(buildFailure.error));\n throw new AggregateError(\n [primaryError, cleanupFailure],\n `${primaryError.message}\\n${cleanupFailure.message}`,\n );\n }\n throw cleanupFailure;\n }\n\n if (buildFailure) {\n throw buildFailure.error;\n }\n\n const cssUrls = await collectCssUrls(publishDir);\n const descriptor = buildWidgetPackagePublishManifest(validation.value, {\n cssUrls,\n });\n const manifestContent = stableStringify(descriptor);\n const publishPackageId = descriptor.packageId;\n\n const manifestPath = path.join(publishDir, \"manifest.json\");\n const publishManifestPath = path.join(publishDir, \"publish-manifest.json\");\n\n await fs.writeFile(manifestPath, manifestContent, \"utf-8\");\n\n const artifacts = await collectWidgetPackageArtifacts(publishDir);\n const publishManifest = createPublishManifestMetadata({\n packageId: publishPackageId,\n version: validation.value.version,\n cliVersion: readCliVersion(),\n runtimeVersion: DEFAULT_RUNTIME_VERSION,\n manifestContent,\n artifacts,\n });\n await fs.writeFile(\n publishManifestPath,\n stableStringify(publishManifest),\n \"utf-8\",\n );\n\n return success({\n publishDir,\n packageId: validation.value.packageId,\n version: validation.value.version,\n manifestPath,\n publishManifestPath,\n widgetScriptPath,\n });\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n const isPublishDirValidationError =\n /^(publishDir|reserved temporary build directory) /.test(error.message);\n return failure({\n code: isPublishDirValidationError\n ? \"INVALID_PUBLISH_DIR\"\n : \"WRITE_FAILED\",\n message: isPublishDirValidationError\n ? \"Unsafe widget package publish directory\"\n : \"Failed to build shared widget package artifacts\",\n details: error.message,\n });\n }\n}\n\nexport function createWidgetPackageEntrySource(options: {\n readonly configImportPath: string;\n readonly cssImportPath?: string;\n readonly validated: ValidatedWidgetPackageBuild;\n}): string {\n return `import { createElement } from \"react\";\nimport { startWidgetPackage } from \"@fluid-app/portal-sdk/widgets/worker\";\n${options.cssImportPath ? `import ${JSON.stringify(options.cssImportPath)};` : \"\"}\n\nconst widgetConfig = await import(${JSON.stringify(options.configImportPath)});\nconst SOURCE_PACKAGE_MARKER = \"__fluidSourceWidgetPackage\";\nconst descriptors = ${JSON.stringify(options.validated.widgets, null, 2)};\n\nfunction isSourceWidgetPackage(value) {\n return Boolean(value && typeof value === \"object\" && value[SOURCE_PACKAGE_MARKER] === true);\n}\n\nfunction collectSourceWidgetPackages(mod) {\n return [\n mod.widgetPackage,\n ...(Array.isArray(mod.widgetPackages) ? mod.widgetPackages : []),\n mod.default,\n ].filter(isSourceWidgetPackage);\n}\n\nconst sourcePackage = collectSourceWidgetPackages(widgetConfig)[0];\nif (!sourcePackage) {\n throw new Error(\"No Fluid source widget package found while loading widget package worker bundle.\");\n}\n\nconst componentsByName = new Map(\n sourcePackage.widgets.map((widget) => [widget.name, widget.component]),\n);\n\nconst widgets = descriptors.map((descriptor) => {\n const component = componentsByName.get(descriptor.name);\n if (typeof component !== \"function\") {\n throw new Error(\"Widget package is missing Remote DOM component for \" + descriptor.name + \".\");\n }\n return {\n ...descriptor,\n component: (props) => createElement(component, props),\n };\n});\n\nstartWidgetPackage({ widgets });\n`;\n}\n\nasync function buildWidgetScriptBundle(options: {\n readonly projectDir: string;\n readonly tempDir: string;\n readonly publishDir: string;\n readonly validated: ValidatedWidgetPackageBuild;\n}): Promise<void> {\n const sourceConfig = await resolvePortalWidgetSourceConfig(\n options.projectDir,\n );\n if (!sourceConfig) {\n throw new Error(\"No portal widget source config found.\");\n }\n\n const entryPath = path.join(options.tempDir, \"widget-entry.ts\");\n const viteConfigPath = path.join(options.tempDir, \"vite.config.mjs\");\n const rootStylesPath = path.join(options.projectDir, \"styles.css\");\n await fs.writeFile(\n entryPath,\n createWidgetPackageEntrySource({\n configImportPath: pathToFileURL(sourceConfig.path).href,\n cssImportPath: (await fs.pathExists(rootStylesPath))\n ? pathToFileURL(rootStylesPath).href\n : undefined,\n validated: options.validated,\n }),\n \"utf-8\",\n );\n await fs.writeFile(\n viteConfigPath,\n createViteConfigSource({\n entryPath,\n publishDir: options.publishDir,\n projectConfigPath: await resolveProjectViteConfigPath(options.projectDir),\n }),\n \"utf-8\",\n );\n\n const viteCliPath = resolveViteCliPath(options.projectDir);\n await execa(\n process.execPath,\n [viteCliPath, \"build\", \"--config\", viteConfigPath],\n {\n cwd: options.projectDir,\n stdio: \"pipe\",\n env: {\n ...process.env,\n NODE_ENV: \"production\",\n },\n },\n );\n}\n\nexport function resolveViteCliPath(projectDir: string): string {\n const projectRequire = createRequire(path.join(projectDir, \"package.json\"));\n const projectViteCliPath = resolveViteCliPathFromRequire(projectRequire);\n if (projectViteCliPath) return projectViteCliPath;\n\n const cliViteCliPath = resolveViteCliPathFromRequire(require);\n if (cliViteCliPath) return cliViteCliPath;\n\n throw new Error(\n \"Unable to resolve Vite for the portal project. Install the project dependencies and ensure vite is available before building widget packages.\",\n );\n}\n\nexport async function resolveProjectViteConfigPath(\n projectDir: string,\n): Promise<string | undefined> {\n for (const fileName of PROJECT_VITE_CONFIG_CANDIDATES) {\n const candidate = path.join(projectDir, fileName);\n if (await fs.pathExists(candidate)) return candidate;\n }\n\n return undefined;\n}\n\nexport function createViteConfigSource(options: {\n readonly entryPath: string;\n readonly publishDir: string;\n readonly projectConfigPath?: string;\n}): string {\n const widgetConfigSource = createWidgetViteConfigObjectSource({\n ...options,\n disableConfigFileLookup: !options.projectConfigPath,\n });\n\n if (!options.projectConfigPath) {\n return `import { defineConfig } from \"vite\";\n\nexport default defineConfig(${widgetConfigSource});\n`;\n }\n\n return `import { defineConfig, loadConfigFromFile, mergeConfig } from \"vite\";\n\nconst projectConfigPath = ${JSON.stringify(options.projectConfigPath)};\nconst widgetConfig = ${widgetConfigSource};\nconst fluidWidgetDefineInvariants = {\n \"process.env.NODE_ENV\": JSON.stringify(\"production\"),\n};\nconst blockedFluidPluginNames = new Set(${JSON.stringify(BLOCKED_FLUID_PROJECT_PLUGIN_NAMES)});\n\nfunction isAllowedProjectPlugin(plugin) {\n return !plugin || typeof plugin !== \"object\" || !blockedFluidPluginNames.has(plugin.name);\n}\n\nfunction sanitizeProjectPlugins(pluginOption) {\n if (!Array.isArray(pluginOption)) {\n return isAllowedProjectPlugin(pluginOption) ? pluginOption : [];\n }\n\n return pluginOption\n .map((plugin) => Array.isArray(plugin) ? sanitizeProjectPlugins(plugin) : plugin)\n .filter(isAllowedProjectPlugin);\n}\n\nfunction copyProjectConfigValue(source, target, key) {\n if (Object.prototype.hasOwnProperty.call(source, key)) {\n target[key] = source[key];\n }\n}\n\nfunction sanitizeProjectConfig(config) {\n if (!config || typeof config !== \"object\") return {};\n\n const sanitized = {};\n if (Object.prototype.hasOwnProperty.call(config, \"plugins\")) {\n sanitized.plugins = sanitizeProjectPlugins(config.plugins);\n }\n copyProjectConfigValue(config, sanitized, \"resolve\");\n copyProjectConfigValue(config, sanitized, \"define\");\n copyProjectConfigValue(config, sanitized, \"css\");\n copyProjectConfigValue(config, sanitized, \"esbuild\");\n copyProjectConfigValue(config, sanitized, \"json\");\n copyProjectConfigValue(config, sanitized, \"assetsInclude\");\n copyProjectConfigValue(config, sanitized, \"envDir\");\n copyProjectConfigValue(config, sanitized, \"envPrefix\");\n return sanitized;\n}\n\nfunction copyConfigDefineValue(value) {\n return value && typeof value === \"object\" && !Array.isArray(value)\n ? { ...value }\n : {};\n}\n\nfunction mergeFluidWidgetDefineInvariants(mergedConfig, safeProjectConfig, widgetConfig) {\n mergedConfig.define = {\n ...copyConfigDefineValue(safeProjectConfig.define),\n ...copyConfigDefineValue(mergedConfig.define),\n ...copyConfigDefineValue(widgetConfig.define),\n ...fluidWidgetDefineInvariants,\n };\n return mergedConfig;\n}\n\nexport default defineConfig(async (configEnv) => {\n const projectConfig = await loadConfigFromFile(configEnv, projectConfigPath);\n const safeProjectConfig = sanitizeProjectConfig(projectConfig?.config ?? {});\n return mergeFluidWidgetDefineInvariants(mergeConfig(safeProjectConfig, widgetConfig), safeProjectConfig, widgetConfig);\n});\n`;\n}\n\nfunction createWidgetViteConfigObjectSource(options: {\n readonly entryPath: string;\n readonly publishDir: string;\n readonly disableConfigFileLookup: boolean;\n}): string {\n const configFileSource = options.disableConfigFileLookup\n ? \" configFile: false,\\n\"\n : \"\";\n return `(() => {\n const fluidWidgetDefineInvariants = {\n \"process.env.NODE_ENV\": JSON.stringify(\"production\"),\n };\n\n function createFluidWidgetBuildInvariants() {\n return {\n copyPublicDir: false,\n emptyOutDir: false,\n outDir: ${JSON.stringify(options.publishDir)},\n sourcemap: false,\n target: \"esnext\",\n cssCodeSplit: false,\n lib: {\n entry: ${JSON.stringify(options.entryPath)},\n formats: [\"es\"],\n fileName: () => \"widget.js\",\n cssFileName: \"widget\",\n },\n rollupOptions: {\n output: {\n inlineDynamicImports: true,\n hoistTransitiveImports: false,\n },\n },\n };\n }\n\n function fluidWidgetBuildInvariantsPlugin() {\n return {\n name: \"fluid-widget-build-invariants\",\n enforce: \"post\",\n config() {\n return { build: createFluidWidgetBuildInvariants() };\n },\n configResolved(config) {\n config.define = {\n ...(config.define ?? {}),\n ...fluidWidgetDefineInvariants,\n };\n Object.assign(config.build, createFluidWidgetBuildInvariants());\n },\n };\n }\n\n return {\n${configFileSource} publicDir: false,\n define: fluidWidgetDefineInvariants,\n resolve: {\n conditions: [\n \"module\",\n \"browser\",\n \"development|production\",\n ],\n },\n plugins: [fluidWidgetBuildInvariantsPlugin()],\n build: createFluidWidgetBuildInvariants(),\n };\n})()`;\n}\n\nasync function validateWidgetPackageOutputPaths(\n projectDir: string,\n publishDir: string,\n): Promise<void> {\n validateSharedWidgetPackagePublishDir(projectDir, publishDir);\n await rejectSymlinkedPathSegments({\n projectDir,\n relativePath: publishDir,\n label: \"publishDir\",\n });\n await rejectSymlinkedPathSegments({\n projectDir,\n relativePath: TEMP_BUILD_DIR,\n label: \"reserved temporary build directory\",\n });\n}\n\nasync function collectCssUrls(publishDir: string): Promise<string[]> {\n const entries = await fs.readdir(publishDir, { withFileTypes: true });\n return entries\n .filter(\n (entry) => entry.isFile() && isWidgetPackageCssArtifactPath(entry.name),\n )\n .map((entry) => entry.name)\n .sort();\n}\n\nasync function rejectSymlinkedPathSegments(options: {\n readonly projectDir: string;\n readonly relativePath: string;\n readonly label: string;\n}): Promise<void> {\n const resolvedProjectDir = path.resolve(options.projectDir);\n const resolvedTargetPath = path.resolve(\n resolvedProjectDir,\n options.relativePath,\n );\n const relative = path.relative(resolvedProjectDir, resolvedTargetPath);\n const segments = relative.split(path.sep).filter(Boolean);\n let currentPath = resolvedProjectDir;\n\n for (const segment of segments) {\n currentPath = path.join(currentPath, segment);\n let stat: fs.Stats;\n try {\n stat = await fs.lstat(currentPath);\n } catch (err) {\n if (isNodeError(err) && err.code === \"ENOENT\") return;\n throw err;\n }\n\n if (stat.isSymbolicLink()) {\n throw new Error(`${options.label} must not include symbolic links.`);\n }\n }\n}\n\nfunction normalizePublishDirForValidation(value: string): string {\n return toPosixPath(path.normalize(value)).replace(/\\/+$/, \"\") || \".\";\n}\n\nfunction isPathWithinReservedDir(value: string, reservedDir: string): boolean {\n return value === reservedDir || value.startsWith(`${reservedDir}/`);\n}\n\nfunction toPosixPath(value: string): string {\n return value.split(path.sep).join(\"/\");\n}\n\nfunction stableStringify(value: unknown): string {\n return `${JSON.stringify(value, null, 2)}\\n`;\n}\n\nfunction readCliVersion(): string {\n return resolveCliVersionFromPackageMetadata(\n path.dirname(fileURLToPath(import.meta.url)),\n require,\n );\n}\n\nexport function resolveCliVersionFromPackageMetadata(\n startDir: string,\n loadPackageMetadata: (packageJsonPath: string) => unknown,\n): string {\n for (const packageJsonPath of candidatePackageJsonPaths(startDir)) {\n let packageJson: unknown;\n try {\n packageJson = loadPackageMetadata(packageJsonPath);\n } catch (error) {\n // Missing candidates are expected during the upward walk; every other read\n // failure must stop the build so its provenance cannot be fabricated.\n if (isModuleResolutionError(error)) continue;\n throw new Error(\"Failed to read Fluid CLI package metadata.\", {\n cause: error,\n });\n }\n\n if (\n !isRecord(packageJson) ||\n packageJson.name !== \"@fluid-app/fluid-cli-portal\"\n ) {\n continue;\n }\n if (\n typeof packageJson.version !== \"string\" ||\n packageJson.version.length === 0\n ) {\n throw new Error(\n \"Fluid CLI package metadata does not declare a valid version.\",\n );\n }\n return packageJson.version;\n }\n\n throw new Error(\"Unable to find Fluid CLI package metadata.\");\n}\n\nfunction resolveViteCliPathFromRequire(\n moduleRequire: NodeJS.Require,\n): string | undefined {\n try {\n const packageJsonPath = moduleRequire.resolve(\"vite/package.json\");\n const packageJson = moduleRequire(packageJsonPath) as { bin?: unknown };\n const binPath = getViteBinPath(packageJson.bin);\n if (!binPath) return undefined;\n\n return path.resolve(path.dirname(packageJsonPath), binPath);\n } catch (err) {\n if (isModuleResolutionError(err)) return undefined;\n throw err;\n }\n}\n\nfunction getViteBinPath(bin: unknown): string | undefined {\n if (typeof bin === \"string\") return bin;\n if (!isRecord(bin)) return undefined;\n\n const viteBin = bin.vite;\n return typeof viteBin === \"string\" ? viteBin : undefined;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isNodeError(value: unknown): value is NodeJS.ErrnoException {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"code\" in value &&\n typeof value.code === \"string\"\n );\n}\n\nfunction isModuleResolutionError(value: unknown): boolean {\n return (\n isNodeError(value) &&\n (value.code === \"MODULE_NOT_FOUND\" || value.code === \"ERR_MODULE_NOT_FOUND\")\n );\n}\n\nfunction candidatePackageJsonPaths(startDir: string): string[] {\n const paths: string[] = [];\n let current = startDir;\n\n while (true) {\n paths.push(path.join(current, \"package.json\"));\n const parent = path.dirname(current);\n if (parent === current) return paths;\n current = parent;\n }\n}\n","/**\n * Shared widget package publish/upload helpers.\n *\n * These utilities cover the two owner scopes currently supported by the\n * portal widget package upload API: marketplace droplets and company-owned\n * Fluid OS packages. They intentionally do not register CLI commands; command\n * implementations can compose the typed payload builders, session/complete\n * requests, signed URL uploads, and dry-run orchestration exposed here.\n */\n\nimport type { FetchClient } from \"@fluid-app/fluid-cli\";\nimport { fluidOs } from \"@fluid-app/fluidos-api-client\";\nimport type { ArtifactMetadata } from \"./widget-package-artifacts.js\";\n\nexport type WidgetPackageUploadOwner =\n | { readonly kind: \"droplet\"; readonly uuid: string }\n | { readonly kind: \"company\" };\n\nexport type WidgetPackageArtifactMetadata = ArtifactMetadata;\n\nexport interface WidgetPackageUploadArtifact extends WidgetPackageArtifactMetadata {\n readonly body: BodyInit;\n}\n\nexport interface WidgetPackageSessionRequest {\n readonly version: string;\n readonly packageKey: string;\n readonly builderVersion: string;\n readonly cliVersion: string;\n readonly runtimeVersion: string;\n readonly manifestHash: string;\n readonly manifest: Record<string, unknown>;\n readonly artifacts: readonly WidgetPackageArtifactMetadata[];\n}\n\nexport interface WidgetPackagePublishRequest extends Omit<\n WidgetPackageSessionRequest,\n \"artifacts\"\n> {\n readonly artifacts: readonly WidgetPackageUploadArtifact[];\n}\n\nexport type WidgetPackageArtifactPayload = ArtifactMetadata;\n\nexport interface WidgetPackageUploadSessionPayload {\n readonly version: string;\n readonly package_key: string;\n readonly builder_version: string;\n readonly cli_version: string;\n readonly runtime_version: string;\n readonly manifest_hash: string;\n readonly manifest: Record<string, unknown>;\n readonly artifacts: readonly WidgetPackageArtifactPayload[];\n}\n\nexport interface WidgetPackageCompleteUploadPayload {\n readonly package_key: string;\n readonly artifacts: readonly WidgetPackageArtifactPayload[];\n}\n\nexport interface WidgetPackageSignedUpload {\n readonly path: string;\n readonly url: string;\n readonly contentType: string;\n readonly headers: Readonly<Record<string, string>>;\n}\n\nexport interface WidgetPackageUploadSession {\n readonly version: string;\n readonly uploads: readonly WidgetPackageSignedUpload[];\n readonly raw: unknown;\n}\n\nexport interface WidgetPackageArtifactUploadResult {\n readonly path: string;\n readonly status: number;\n readonly contentType: string;\n}\n\nexport interface WidgetPackageCompleteResult {\n readonly version: string;\n readonly status?: string;\n readonly packageKey?: string;\n readonly raw: unknown;\n}\n\nexport interface WidgetPackagePublishResult {\n readonly dryRun: boolean;\n readonly sessionPayload: WidgetPackageUploadSessionPayload;\n readonly session?: WidgetPackageUploadSession;\n readonly uploadedArtifacts?: readonly WidgetPackageArtifactUploadResult[];\n readonly complete?: WidgetPackageCompleteResult;\n}\n\ninterface BasePublishWidgetPackageVersionOptions {\n readonly owner: WidgetPackageUploadOwner;\n readonly request: WidgetPackagePublishRequest;\n readonly fetchImpl?: WidgetPackageUploadFetch;\n}\n\nexport interface DryRunPublishWidgetPackageVersionOptions extends BasePublishWidgetPackageVersionOptions {\n readonly client?: FetchClient;\n readonly dryRun: true;\n}\n\nexport interface PublishWidgetPackageVersionUploadOptions extends BasePublishWidgetPackageVersionOptions {\n readonly client: FetchClient;\n readonly dryRun?: boolean;\n}\n\nexport type PublishWidgetPackageVersionOptions =\n | DryRunPublishWidgetPackageVersionOptions\n | PublishWidgetPackageVersionUploadOptions;\n\ntype FluidOsCreateWidgetPackageVersionPayload = Parameters<\n typeof fluidOs.fluid_os_v0_create_widget_package_version\n>[1];\n\ntype FluidOsCompleteWidgetPackageVersionUploadPayload = NonNullable<\n Parameters<\n typeof fluidOs.fluid_os_v0_complete_widget_package_version_upload\n >[2]\n>;\n\nexport type WidgetPackageUploadFetch = (\n input: string | URL,\n init: RequestInit,\n) => Promise<Response>;\n\nexport class WidgetPackageUploadError extends Error {\n public readonly code: string;\n\n constructor(code: string, message: string) {\n super(message);\n this.name = \"WidgetPackageUploadError\";\n this.code = code;\n }\n}\n\nexport function buildWidgetPackageArtifactPayload(\n artifact: WidgetPackageArtifactMetadata,\n): WidgetPackageArtifactPayload {\n return {\n path: artifact.path,\n sha256: artifact.sha256,\n bytes: artifact.bytes,\n contentType: artifact.contentType,\n };\n}\n\nexport function buildWidgetPackageUploadSessionPayload(\n request: WidgetPackageSessionRequest,\n): WidgetPackageUploadSessionPayload {\n return {\n version: request.version,\n package_key: request.packageKey,\n builder_version: request.builderVersion,\n cli_version: request.cliVersion,\n runtime_version: request.runtimeVersion,\n manifest_hash: request.manifestHash,\n manifest: request.manifest,\n artifacts: request.artifacts.map(buildWidgetPackageArtifactPayload),\n };\n}\n\nexport function buildWidgetPackageCompleteUploadPayload(\n packageKey: string,\n artifacts: readonly WidgetPackageArtifactMetadata[],\n): WidgetPackageCompleteUploadPayload {\n return {\n package_key: packageKey,\n artifacts: artifacts.map(buildWidgetPackageArtifactPayload),\n };\n}\n\nexport function getWidgetPackageUploadSessionRoute(\n owner: WidgetPackageUploadOwner,\n): string {\n switch (owner.kind) {\n case \"droplet\":\n return `/api/droplets/${encodeURIComponent(owner.uuid)}/widget_package_versions`;\n case \"company\":\n return \"/api/company/fluid_os/widget_package_versions\";\n }\n}\n\nexport function getWidgetPackageCompleteUploadRoute(\n owner: WidgetPackageUploadOwner,\n version: string,\n): string {\n switch (owner.kind) {\n case \"droplet\":\n return `/api/droplets/${encodeURIComponent(owner.uuid)}/widget_package_versions/${encodeURIComponent(version)}/complete_upload`;\n case \"company\":\n return `/api/company/fluid_os/widget_package_versions/${encodeURIComponent(version)}/complete_upload`;\n }\n}\n\nexport async function requestWidgetPackageUploadSession(\n client: FetchClient,\n owner: WidgetPackageUploadOwner,\n request: WidgetPackageSessionRequest,\n): Promise<WidgetPackageUploadSession> {\n const payload = buildWidgetPackageUploadSessionPayload(request);\n\n try {\n const response =\n owner.kind === \"company\"\n ? await fluidOs.fluid_os_v0_create_widget_package_version(\n client,\n payload as FluidOsCreateWidgetPackageVersionPayload,\n )\n : await client.post<unknown>(\n getWidgetPackageUploadSessionRoute(owner),\n payload,\n );\n return normalizeWidgetPackageUploadSession(\n response,\n request.version,\n request.artifacts,\n );\n } catch (err) {\n if (err instanceof WidgetPackageUploadError) {\n throw err;\n }\n\n throw new WidgetPackageUploadError(\n \"SESSION_REQUEST_FAILED\",\n `Failed to create widget package upload session: ${formatUnknownError(err)}`,\n );\n }\n}\n\nexport async function uploadWidgetPackageArtifacts(\n session: WidgetPackageUploadSession,\n artifacts: readonly WidgetPackageUploadArtifact[],\n fetchImpl: WidgetPackageUploadFetch = fetch,\n): Promise<WidgetPackageArtifactUploadResult[]> {\n const uploadsByPath = new Map(\n session.uploads.map((upload) => [upload.path, upload] as const),\n );\n const results: WidgetPackageArtifactUploadResult[] = [];\n\n for (const artifact of artifacts) {\n const upload = uploadsByPath.get(artifact.path);\n if (!upload) {\n throw new WidgetPackageUploadError(\n \"MISSING_SIGNED_URL\",\n `Upload session did not include a signed URL for artifact \"${artifact.path}\".`,\n );\n }\n\n if (upload.contentType !== artifact.contentType) {\n throw new WidgetPackageUploadError(\n \"CONTENT_TYPE_MISMATCH\",\n `Upload session expected artifact \"${artifact.path}\" to use content type \"${upload.contentType}\", but local artifact uses \"${artifact.contentType}\".`,\n );\n }\n\n let response: Response;\n try {\n response = await fetchImpl(upload.url, {\n method: \"PUT\",\n headers: mergeUploadHeaders(upload.headers, upload.contentType),\n body: artifact.body,\n });\n } catch (err) {\n throw new WidgetPackageUploadError(\n \"ARTIFACT_UPLOAD_FAILED\",\n `Failed to upload artifact \"${artifact.path}\": ${formatUnknownError(err)}`,\n );\n }\n\n if (!response.ok) {\n const body = await response.text().catch(() => \"\");\n const details = body.trim() ? `: ${body.trim().slice(0, 500)}` : \"\";\n throw new WidgetPackageUploadError(\n \"ARTIFACT_UPLOAD_FAILED\",\n `Failed to upload artifact \"${artifact.path}\": PUT returned HTTP ${response.status}${details}`,\n );\n }\n\n results.push({\n path: artifact.path,\n status: response.status,\n contentType: upload.contentType,\n });\n }\n\n return results;\n}\n\nexport async function completeWidgetPackageUpload(\n client: FetchClient,\n owner: WidgetPackageUploadOwner,\n version: string,\n packageKey: string,\n artifacts: readonly WidgetPackageArtifactMetadata[],\n): Promise<WidgetPackageCompleteResult> {\n const payload = buildWidgetPackageCompleteUploadPayload(\n packageKey,\n artifacts,\n );\n\n try {\n const response =\n owner.kind === \"company\"\n ? await fluidOs.fluid_os_v0_complete_widget_package_version_upload(\n client,\n version,\n payload as FluidOsCompleteWidgetPackageVersionUploadPayload,\n )\n : await client.post<unknown>(\n getWidgetPackageCompleteUploadRoute(owner, version),\n payload,\n );\n return normalizeWidgetPackageCompleteResponse(response, version);\n } catch (err) {\n if (err instanceof WidgetPackageUploadError) {\n throw err;\n }\n\n throw new WidgetPackageUploadError(\n \"COMPLETE_UPLOAD_FAILED\",\n `Failed to complete widget package upload: ${formatUnknownError(err)}`,\n );\n }\n}\n\nexport async function publishWidgetPackageVersion(\n options: PublishWidgetPackageVersionOptions,\n): Promise<WidgetPackagePublishResult> {\n const sessionPayload = buildWidgetPackageUploadSessionPayload(\n options.request,\n );\n\n if (options.dryRun) {\n return { dryRun: true, sessionPayload };\n }\n\n if (!options.client) {\n throw new Error(\"A FetchClient is required when dryRun is false.\");\n }\n\n const session = await requestWidgetPackageUploadSession(\n options.client,\n options.owner,\n options.request,\n );\n const uploadedArtifacts = await uploadWidgetPackageArtifacts(\n session,\n options.request.artifacts,\n options.fetchImpl,\n );\n const complete = await completeWidgetPackageUpload(\n options.client,\n options.owner,\n session.version,\n options.request.packageKey,\n options.request.artifacts,\n );\n\n return {\n dryRun: false,\n sessionPayload,\n session,\n uploadedArtifacts,\n complete,\n };\n}\n\nexport function normalizeWidgetPackageUploadSession(\n response: unknown,\n requestedVersion: string,\n requestedArtifacts: readonly WidgetPackageArtifactMetadata[],\n): WidgetPackageUploadSession {\n const record = requireRecord(\n response,\n \"INVALID_SESSION_RESPONSE\",\n \"Upload session response must be a JSON object.\",\n );\n requireFiniteNumberProperty(record, \"version_id\", \"INVALID_SESSION_RESPONSE\");\n requireStringProperty(\n record,\n \"artifact_base_url\",\n \"INVALID_SESSION_RESPONSE\",\n );\n\n const uploadValues = record[\"uploads\"];\n if (!Array.isArray(uploadValues)) {\n throw new WidgetPackageUploadError(\n \"INVALID_SESSION_RESPONSE\",\n 'Upload session response must contain an \"uploads\" array.',\n );\n }\n\n const requestedArtifactsByPath = new Map(\n requestedArtifacts.map((artifact) => [artifact.path, artifact] as const),\n );\n const seenUploadPaths = new Set<string>();\n const uploads = uploadValues.map((value, index) => {\n const upload = requireRecord(\n value,\n \"INVALID_SESSION_RESPONSE\",\n `Upload session uploads[${index}] must be a JSON object.`,\n );\n const path = requireStringProperty(\n upload,\n \"path\",\n \"INVALID_SESSION_RESPONSE\",\n );\n const url = requireStringProperty(\n upload,\n \"upload_url\",\n \"INVALID_SESSION_RESPONSE\",\n );\n requireStringProperty(upload, \"public_url\", \"INVALID_SESSION_RESPONSE\");\n\n const requestedArtifact = requestedArtifactsByPath.get(path);\n if (!requestedArtifact) {\n throw new WidgetPackageUploadError(\n \"INVALID_SESSION_RESPONSE\",\n `Upload session response included unexpected artifact \"${path}\".`,\n );\n }\n if (seenUploadPaths.has(path)) {\n throw new WidgetPackageUploadError(\n \"INVALID_SESSION_RESPONSE\",\n `Upload session response included duplicate artifact \"${path}\".`,\n );\n }\n seenUploadPaths.add(path);\n\n return {\n path,\n url,\n contentType: requestedArtifact.contentType,\n headers: {},\n };\n });\n const missingUploadPaths = requestedArtifacts\n .map((artifact) => artifact.path)\n .filter((artifactPath) => !seenUploadPaths.has(artifactPath));\n\n if (missingUploadPaths.length > 0) {\n throw new WidgetPackageUploadError(\n \"INVALID_SESSION_RESPONSE\",\n `Upload session response is missing signed URLs for artifact(s): ${missingUploadPaths.join(\", \")}.`,\n );\n }\n\n return { version: requestedVersion, uploads, raw: response };\n}\n\nexport function normalizeWidgetPackageCompleteResponse(\n response: unknown,\n requestedVersion: string,\n): WidgetPackageCompleteResult {\n const record = requireRecord(\n response,\n \"INVALID_COMPLETE_RESPONSE\",\n \"Complete upload response must be a JSON object.\",\n );\n const responseVersion = requireStringProperty(\n record,\n \"version\",\n \"INVALID_COMPLETE_RESPONSE\",\n );\n\n if (responseVersion !== requestedVersion) {\n throw new WidgetPackageUploadError(\n \"INVALID_COMPLETE_RESPONSE\",\n `Complete upload response version \"${responseVersion}\" did not match requested version \"${requestedVersion}\".`,\n );\n }\n\n const packageKey = requireStringProperty(\n record,\n \"packageId\",\n \"INVALID_COMPLETE_RESPONSE\",\n );\n const status = requireStringProperty(\n record,\n \"status\",\n \"INVALID_COMPLETE_RESPONSE\",\n );\n requireStringProperty(record, \"scriptUrl\", \"INVALID_COMPLETE_RESPONSE\");\n requireStringProperty(record, \"manifestUrl\", \"INVALID_COMPLETE_RESPONSE\");\n requireStringArrayProperty(record, \"cssUrls\", \"INVALID_COMPLETE_RESPONSE\");\n\n return {\n version: responseVersion,\n status,\n packageKey,\n raw: response,\n };\n}\n\nfunction normalizeHeaderName(name: string): string {\n return name.toLowerCase();\n}\n\nfunction mergeUploadHeaders(\n headers: Readonly<Record<string, string>>,\n contentType: string,\n): Record<string, string> {\n const merged: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(headers)) {\n if (normalizeHeaderName(key) === \"content-type\") continue;\n merged[key] = value;\n }\n\n merged[\"Content-Type\"] = contentType;\n return merged;\n}\n\nfunction requireRecord(\n value: unknown,\n code: string,\n message: string,\n): Record<string, unknown> {\n if (!isRecord(value) || Array.isArray(value)) {\n throw new WidgetPackageUploadError(code, message);\n }\n return value;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction requireStringProperty(\n record: Record<string, unknown>,\n key: string,\n code: string,\n): string {\n const value = record[key];\n if (typeof value === \"string\" && value.trim().length > 0) {\n return value;\n }\n throw new WidgetPackageUploadError(\n code,\n `Response field \"${key}\" must be a non-empty string.`,\n );\n}\n\nfunction requireFiniteNumberProperty(\n record: Record<string, unknown>,\n key: string,\n code: string,\n): number {\n const value = record[key];\n if (typeof value === \"number\" && Number.isFinite(value)) {\n return value;\n }\n throw new WidgetPackageUploadError(\n code,\n `Response field \"${key}\" must be a finite number.`,\n );\n}\n\nfunction requireStringArrayProperty(\n record: Record<string, unknown>,\n key: string,\n code: string,\n): readonly string[] {\n const value = record[key];\n if (\n Array.isArray(value) &&\n value.every((entry) => typeof entry === \"string\" && entry.trim().length > 0)\n ) {\n return value;\n }\n throw new WidgetPackageUploadError(\n code,\n `Response field \"${key}\" must be an array of non-empty strings.`,\n );\n}\n\nfunction formatUnknownError(err: unknown): string {\n const base = err instanceof Error ? err.message : String(err);\n\n if (isRecord(err) && \"data\" in err) {\n return `${base} — ${safeStringify(err.data)}`;\n }\n\n return base;\n}\n\nfunction safeStringify(value: unknown): string {\n const seen = new WeakSet<object>();\n\n try {\n const serialized = JSON.stringify(value, (_key, item: unknown) => {\n if (typeof item === \"bigint\") return `${item}n`;\n if (typeof item !== \"object\" || item === null) return item;\n if (seen.has(item)) return \"[Circular]\";\n seen.add(item);\n return item;\n });\n return serialized ?? String(value);\n } catch {\n return \"[unserializable error data]\";\n }\n}\n","import { findProjectConfig, readConfig } from \"@fluid-app/fluid-cli\";\nimport type { FetchClient } from \"@fluid-app/fluid-cli\";\nimport { createFetchClient } from \"@fluid-app/fluidos-api-client\";\nimport chalk from \"chalk\";\nimport fs from \"fs-extra\";\nimport path from \"node:path\";\nimport {\n buildSharedWidgetPackage,\n validateSharedWidgetPackagePublishDir,\n} from \"../utils/widget-package-builder.js\";\nimport type {\n BuildSharedWidgetPackageOptions,\n SharedWidgetPackageBuildResult,\n} from \"../utils/widget-package-builder.js\";\nimport { publishWidgetPackageVersion } from \"../utils/widget-package-upload.js\";\nimport type {\n WidgetPackagePublishRequest,\n WidgetPackagePublishResult,\n WidgetPackageUploadOwner,\n} from \"../utils/widget-package-upload.js\";\nimport {\n collectWidgetPackageArtifacts,\n computeArtifactMetadata,\n sha256,\n type ArtifactMetadata,\n type PublishManifestMetadata,\n} from \"../utils/widget-package-artifacts.js\";\nimport type { WidgetPackageOwnerKind as WidgetPackageBuildOwner } from \"../utils/widget-package-validation.js\";\n\nexport interface PublishWidgetRuntimeArtifactsOptions {\n readonly projectDir: string;\n readonly outDir: string;\n readonly buildOwner: WidgetPackageBuildOwner;\n readonly uploadOwner: WidgetPackageUploadOwner;\n readonly dryRun?: boolean;\n readonly client?: FetchClient;\n}\n\nexport interface PublishWidgetRuntimeArtifactsResult {\n readonly build: SharedWidgetPackageBuildResult;\n readonly request: WidgetPackagePublishRequest;\n readonly publish: WidgetPackagePublishResult;\n}\n\nexport interface PublishWidgetRuntimeArtifactsDependencies {\n readonly buildSharedWidgetPackage?: typeof buildSharedWidgetPackage;\n readonly publishWidgetPackageVersion?: typeof publishWidgetPackageVersion;\n}\n\nexport async function publishWidgetRuntimeArtifacts(\n options: PublishWidgetRuntimeArtifactsOptions,\n dependencies: PublishWidgetRuntimeArtifactsDependencies = {},\n): Promise<PublishWidgetRuntimeArtifactsResult> {\n const buildPackage =\n dependencies.buildSharedWidgetPackage ?? buildSharedWidgetPackage;\n const publishVersion =\n dependencies.publishWidgetPackageVersion ?? publishWidgetPackageVersion;\n\n const isDryRun = options.dryRun === true;\n const client = options.client;\n\n if (!isDryRun && !client) {\n throw new Error(\"A FetchClient is required when dryRun is false.\");\n }\n\n const safeOutDir = validateWidgetPublishOutDir(\n options.projectDir,\n options.outDir,\n );\n const buildResult = await buildPackage({\n projectDir: options.projectDir,\n publishDir: safeOutDir,\n owner: options.buildOwner,\n } satisfies BuildSharedWidgetPackageOptions);\n\n if (!buildResult.success) {\n const details = buildResult.error.details\n ? `\\n${buildResult.error.details}`\n : \"\";\n throw new Error(`${buildResult.error.message}${details}`);\n }\n\n const request = await createWidgetPackagePublishRequest({\n publishDir: buildResult.value.publishDir,\n manifestPath: buildResult.value.manifestPath,\n publishManifestPath: buildResult.value.publishManifestPath,\n owner: options.buildOwner,\n });\n\n let publish: WidgetPackagePublishResult;\n if (isDryRun) {\n publish = await publishVersion({\n owner: options.uploadOwner,\n request,\n dryRun: true,\n });\n } else {\n if (!client) {\n throw new Error(\"A FetchClient is required when dryRun is false.\");\n }\n\n publish = await publishVersion({\n client,\n owner: options.uploadOwner,\n request,\n });\n }\n\n return { build: buildResult.value, request, publish };\n}\n\nexport async function createWidgetPackagePublishRequest(options: {\n readonly publishDir: string;\n readonly manifestPath: string;\n readonly publishManifestPath: string;\n readonly owner: WidgetPackageBuildOwner;\n}): Promise<WidgetPackagePublishRequest> {\n const manifestContent = await fs.readFile(options.manifestPath);\n const manifest = parseJsonRecord(\n manifestContent.toString(\"utf-8\"),\n options.manifestPath,\n );\n const publishManifest = await readPublishManifest(\n options.publishManifestPath,\n );\n assertManifestIdentityMatches(\n manifest,\n publishManifest,\n options.manifestPath,\n );\n assertMetadataMatches({\n label: \"manifestHash\",\n path: \"manifest.json\",\n expected: publishManifest.manifestHash,\n actual: sha256(manifestContent),\n });\n const collectedArtifacts = await collectWidgetPackageArtifacts(\n options.publishDir,\n );\n assertArtifactInventoryMatches(publishManifest.artifacts, collectedArtifacts);\n const artifacts = await readUploadArtifacts(\n options.publishDir,\n publishManifest.artifacts,\n );\n\n return {\n version: publishManifest.version,\n packageKey: stripOwnerPrefix(publishManifest.packageId, options.owner),\n builderVersion: publishManifest.builderVersion,\n cliVersion: publishManifest.cliVersion,\n runtimeVersion: publishManifest.runtimeVersion,\n manifestHash: publishManifest.manifestHash,\n manifest,\n artifacts,\n };\n}\n\nexport function createAuthenticatedWidgetPackageClient(): FetchClient {\n const { profile, profileName, token } = resolveAuthenticatedProfile();\n if (!token) {\n if (!profileName) {\n throw new Error(\n \"Not logged in. Run \" + chalk.cyan(\"fluid login\") + \" first.\",\n );\n }\n throw new Error(\n \"No auth token found for profile \" +\n chalk.cyan(profileName) +\n \". Run \" +\n chalk.cyan(\"fluid login\") +\n \" to re-authenticate.\",\n );\n }\n\n return createFetchClient({\n baseUrl:\n profile?.baseUrl ??\n process.env[\"FLUID_API_BASE\"] ??\n \"https://api.fluid.app\",\n getAuthToken: () => token,\n });\n}\n\ntype ProfileWithBaseUrl = ReturnType<typeof readConfig>[\"profiles\"][string] & {\n readonly baseUrl?: string;\n};\n\nfunction resolveAuthenticatedProfile(): {\n readonly profile?: ProfileWithBaseUrl;\n readonly profileName?: string;\n readonly token?: string;\n} {\n const config = readConfig();\n const projectConfig = findProjectConfig(process.cwd());\n\n if (projectConfig?.profile) {\n const profile = config.profiles[projectConfig.profile] as\n | ProfileWithBaseUrl\n | undefined;\n if (profile) {\n return {\n profile,\n profileName: projectConfig.profile,\n token: profile.token,\n };\n }\n }\n\n if (!config.activeProfile) return {};\n const profile = config.profiles[config.activeProfile] as\n | ProfileWithBaseUrl\n | undefined;\n if (!profile) return {};\n\n return {\n profile,\n profileName: config.activeProfile,\n token: profile.token,\n };\n}\n\nexport function printWidgetPublishSummary(options: {\n readonly title: string;\n readonly ownerLabel: string;\n readonly environment?: string;\n readonly outDir: string;\n readonly result: PublishWidgetRuntimeArtifactsResult;\n}): void {\n console.log();\n console.log(chalk.green.bold(`${options.title} complete`));\n console.log();\n console.log(chalk.gray(\"Owner: \") + chalk.white(options.ownerLabel));\n if (options.environment) {\n console.log(chalk.gray(\"Environment: \") + chalk.white(options.environment));\n }\n console.log(\n chalk.gray(\"Package: \") + chalk.white(options.result.build.packageId),\n );\n console.log(\n chalk.gray(\"Version: \") + chalk.white(options.result.build.version),\n );\n console.log(chalk.gray(\"Output: \") + chalk.cyan(options.outDir));\n console.log(\n chalk.gray(\"Artifacts: \") +\n chalk.white(String(options.result.request.artifacts.length)),\n );\n console.log();\n}\n\nexport function printRuntimeOnlyNotice(): void {\n console.log(\n chalk.yellow(\n \"This publishes widget runtime artifacts only; it does not deploy the hosted portal shell.\",\n ),\n );\n console.log();\n}\n\nexport function printDryRunSessionPayload(\n result: PublishWidgetRuntimeArtifactsResult,\n): void {\n console.log(chalk.bold(\"Dry-run upload session payload:\"));\n console.log(JSON.stringify(result.publish.sessionPayload, null, 2));\n console.log();\n}\n\nexport function validateWidgetPublishOutDir(\n projectDir: string,\n outDir: string,\n): string {\n validateSharedWidgetPackagePublishDir(projectDir, outDir, {\n optionName: \"--out-dir\",\n });\n\n return outDir;\n}\n\nasync function readJsonRecord(\n filePath: string,\n): Promise<Record<string, unknown>> {\n return parseJsonRecord(await fs.readFile(filePath, \"utf-8\"), filePath);\n}\n\nfunction parseJsonRecord(\n content: string,\n filePath: string,\n): Record<string, unknown> {\n const value = JSON.parse(content) as unknown;\n if (!isRecord(value) || Array.isArray(value)) {\n throw new Error(`${path.basename(filePath)} must contain a JSON object.`);\n }\n return value;\n}\n\nasync function readPublishManifest(\n filePath: string,\n): Promise<PublishManifestMetadata> {\n const record = await readJsonRecord(filePath);\n const artifactsValue = record[\"artifacts\"];\n if (!Array.isArray(artifactsValue)) {\n throw new Error(\"publish-manifest.json must include an artifacts array.\");\n }\n\n return {\n packageId: readRequiredString(record, \"packageId\", filePath),\n version: readRequiredString(record, \"version\", filePath),\n builderVersion: readRequiredString(record, \"builderVersion\", filePath),\n cliVersion: readRequiredString(record, \"cliVersion\", filePath),\n runtimeVersion: readRequiredString(record, \"runtimeVersion\", filePath),\n manifestHash: readRequiredString(record, \"manifestHash\", filePath),\n artifacts: artifactsValue.map((artifact, index) =>\n readArtifactMetadata(artifact, index),\n ),\n };\n}\n\nfunction readArtifactMetadata(value: unknown, index: number): ArtifactMetadata {\n if (!isRecord(value) || Array.isArray(value)) {\n throw new Error(\n `publish-manifest.json artifacts[${index}] must be a JSON object.`,\n );\n }\n\n return {\n path: readRequiredString(value, \"path\", \"publish-manifest.json\"),\n sha256: readRequiredString(value, \"sha256\", \"publish-manifest.json\"),\n bytes: readRequiredNumber(value, \"bytes\", \"publish-manifest.json\"),\n contentType: readRequiredString(\n value,\n \"contentType\",\n \"publish-manifest.json\",\n ),\n };\n}\n\nasync function readUploadArtifacts(\n publishDir: string,\n artifacts: readonly ArtifactMetadata[],\n): Promise<WidgetPackagePublishRequest[\"artifacts\"]> {\n const resolvedPublishDir = path.resolve(publishDir);\n\n return Promise.all(\n artifacts.map(async (artifact) => {\n const artifactPath = resolveArtifactPath(\n resolvedPublishDir,\n artifact.path,\n );\n const actual = await computeArtifactMetadata(\n artifactPath,\n resolvedPublishDir,\n );\n assertArtifactMetadataMatches(artifact, actual);\n\n return {\n ...actual,\n body: await fs.readFile(artifactPath),\n };\n }),\n );\n}\n\nfunction assertManifestIdentityMatches(\n manifest: Record<string, unknown>,\n publishManifest: PublishManifestMetadata,\n manifestPath: string,\n): void {\n assertMetadataMatches({\n label: \"packageId\",\n path: \"manifest.json\",\n expected: publishManifest.packageId,\n actual: readRequiredString(manifest, \"packageId\", manifestPath),\n });\n assertMetadataMatches({\n label: \"version\",\n path: \"manifest.json\",\n expected: publishManifest.version,\n actual: readRequiredString(manifest, \"version\", manifestPath),\n });\n}\n\nfunction assertArtifactInventoryMatches(\n expected: readonly ArtifactMetadata[],\n actual: readonly ArtifactMetadata[],\n): void {\n const expectedByPath = new Map<string, ArtifactMetadata>();\n for (const artifact of expected) {\n if (expectedByPath.has(artifact.path)) {\n throw new Error(\n `publish-manifest.json artifacts include duplicate path ${JSON.stringify(artifact.path)}. ` +\n \"Rebuild the widget package before publishing.\",\n );\n }\n expectedByPath.set(artifact.path, artifact);\n }\n\n const actualPaths = new Set<string>();\n for (const actualArtifact of actual) {\n actualPaths.add(actualArtifact.path);\n const expectedArtifact = expectedByPath.get(actualArtifact.path);\n if (!expectedArtifact) {\n throw new Error(\n `publish-manifest.json artifacts omit ${JSON.stringify(actualArtifact.path)}. ` +\n \"Rebuild the widget package before publishing.\",\n );\n }\n assertArtifactMetadataMatches(expectedArtifact, actualArtifact);\n }\n\n for (const expectedArtifact of expected) {\n if (actualPaths.has(expectedArtifact.path)) continue;\n throw new Error(\n `publish-manifest.json artifacts include ${JSON.stringify(expectedArtifact.path)}, but that file is not present on disk. ` +\n \"Rebuild the widget package before publishing.\",\n );\n }\n}\n\nfunction assertArtifactMetadataMatches(\n expected: ArtifactMetadata,\n actual: ArtifactMetadata,\n): void {\n assertMetadataMatches({\n label: \"path\",\n path: expected.path,\n expected: expected.path,\n actual: actual.path,\n });\n assertMetadataMatches({\n label: \"sha256\",\n path: expected.path,\n expected: expected.sha256,\n actual: actual.sha256,\n });\n assertMetadataMatches({\n label: \"bytes\",\n path: expected.path,\n expected: expected.bytes,\n actual: actual.bytes,\n });\n assertMetadataMatches({\n label: \"contentType\",\n path: expected.path,\n expected: expected.contentType,\n actual: actual.contentType,\n });\n}\n\nfunction assertMetadataMatches(options: {\n readonly label: string;\n readonly path: string;\n readonly expected: string | number;\n readonly actual: string | number;\n}): void {\n if (options.expected === options.actual) return;\n\n throw new Error(\n `publish-manifest.json ${options.label} for ${JSON.stringify(options.path)} does not match the file on disk. ` +\n `Recorded ${JSON.stringify(options.expected)}, computed ${JSON.stringify(options.actual)}. ` +\n \"Rebuild the widget package before publishing.\",\n );\n}\n\nfunction resolveArtifactPath(publishDir: string, artifactPath: string): string {\n const resolved = path.resolve(publishDir, artifactPath);\n const relative = path.relative(publishDir, resolved);\n if (relative.startsWith(\"..\") || path.isAbsolute(relative)) {\n throw new Error(\n `Artifact path ${JSON.stringify(artifactPath)} escapes the publish directory.`,\n );\n }\n return resolved;\n}\n\nfunction stripOwnerPrefix(\n packageId: string,\n owner: WidgetPackageBuildOwner,\n): string {\n const prefix = `${owner}.`;\n if (packageId.startsWith(prefix) && packageId.length > prefix.length) {\n return packageId.slice(prefix.length);\n }\n return packageId;\n}\n\nfunction readRequiredString(\n record: Record<string, unknown>,\n key: string,\n filePath: string,\n): string {\n const value = record[key];\n if (typeof value === \"string\" && value.trim().length > 0) return value;\n throw new Error(`${path.basename(filePath)} must include string ${key}.`);\n}\n\nfunction readRequiredNumber(\n record: Record<string, unknown>,\n key: string,\n filePath: string,\n): number {\n const value = record[key];\n if (typeof value === \"number\" && Number.isFinite(value)) return value;\n throw new Error(`${path.basename(filePath)} must include number ${key}.`);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n","import { Command } from \"commander\";\nimport type { PluginContext } from \"@fluid-app/fluid-cli\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport type { PortalDeployOptions } from \"../types.js\";\nimport {\n createAuthenticatedWidgetPackageClient,\n printRuntimeOnlyNotice,\n printDryRunSessionPayload,\n printWidgetPublishSummary,\n publishWidgetRuntimeArtifacts,\n} from \"./widget-package-publish.js\";\n\nconst DEFAULT_OUT_DIR = \".fluid/widget-dist\";\nconst DEFAULT_ENVIRONMENT = \"production\";\n\nexport const deployCommand: Command = new Command(\"deploy\")\n .description(\"Publish company-owned portal widget runtime artifacts\")\n .option(\n \"-e, --environment <name>\",\n \"Target environment label for output reporting\",\n DEFAULT_ENVIRONMENT,\n )\n .option(\n \"-o, --out-dir <dir>\",\n \"Widget artifact output directory\",\n DEFAULT_OUT_DIR,\n )\n .option(\"--dry-run\", \"Build and validate upload payload without publishing\")\n .action(async (options: PortalDeployOptions) => {\n const environment = options.environment ?? DEFAULT_ENVIRONMENT;\n const outDir = options.outDir ?? DEFAULT_OUT_DIR;\n const dryRun = options.dryRun === true;\n\n console.log();\n console.log(chalk.blue.bold(\"Fluid Portal Deploy\"));\n console.log();\n printRuntimeOnlyNotice();\n console.log(chalk.gray(\"Environment: \") + chalk.white(environment));\n console.log(chalk.gray(\"Output: \") + chalk.cyan(outDir));\n if (dryRun)\n console.log(chalk.yellow(\"Dry run: no upload will be created.\"));\n console.log();\n\n const spinner = ora(\"Building company-owned widget package...\").start();\n\n try {\n const result = await publishWidgetRuntimeArtifacts({\n projectDir: process.cwd(),\n outDir,\n buildOwner: \"company\",\n uploadOwner: { kind: \"company\" },\n dryRun,\n ...(dryRun ? {} : { client: createAuthenticatedWidgetPackageClient() }),\n });\n\n spinner.succeed(\"Built widget runtime artifacts\");\n\n if (dryRun) {\n console.log(\n chalk.yellow(\"Dry run complete — upload session was not requested.\"),\n );\n console.log();\n printDryRunSessionPayload(result);\n } else {\n console.log(chalk.green(\"Published widget package version.\"));\n }\n\n printWidgetPublishSummary({\n title: dryRun ? \"Portal deploy dry run\" : \"Portal deploy\",\n ownerLabel: \"company\",\n environment,\n outDir,\n result,\n });\n } catch (err) {\n spinner.fail(\"Portal deploy failed\");\n console.error(\n chalk.red(\"Error:\") +\n \" \" +\n (err instanceof Error ? err.message : String(err)),\n );\n process.exit(1);\n }\n });\n\nexport function registerDeployCommand(ctx: PluginContext): void {\n ctx.program.addCommand(deployCommand);\n}\n","import { Command } from \"commander\";\nimport type { PluginContext } from \"@fluid-app/fluid-cli\";\nimport chalk from \"chalk\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n/**\n * Severity levels for drift diagnostics.\n * - ERROR: Security-critical or breaking drift (e.g., missing CSP)\n * - WARN: Stale config that may cause subtle issues\n * - INFO: Cosmetic or non-functional differences\n */\nexport type Severity = \"error\" | \"warn\" | \"info\";\n\nexport interface Diagnostic {\n file: string;\n severity: Severity;\n message: string;\n}\n\n/** Files that are managed by the SDK and should match the canonical template. */\nconst INFRASTRUCTURE_FILES = [\n \"index.html\",\n \"tsconfig.json\",\n \"vite.config.ts\",\n \".oxlintrc.json\",\n] as const;\n\n/** Entry files with expected content patterns after Phase 2 absorption. */\nconst ENTRY_CHECKS = [\n {\n file: \"src/main.tsx\",\n expectedPattern: \"createPortal\",\n hint: 'main.tsx should use createPortal() from @fluid-app/portal-sdk. Run \"fluid create\" to see the latest template.',\n },\n {\n file: \"src/index.css\",\n expectedPattern: \"@fluid-app/portal-sdk/globals.css\",\n hint: \"index.css should import @fluid-app/portal-sdk/globals.css instead of inline theme tokens.\",\n },\n] as const;\n\n/** Files that should NOT exist after Phase 2 (absorbed into SDK). */\nconst REMOVED_FILES = [\n {\n file: \"src/App.tsx\",\n hint: \"App.tsx is no longer needed — createPortal() handles the AppShell wiring internally.\",\n },\n {\n file: \"src/fluid.config.ts\",\n hint: \"fluid.config.ts is no longer needed — pass config overrides to createPortal() directly.\",\n },\n] as const;\n\nfunction readFileOrNull(filePath: string): string | null {\n try {\n return readFileSync(filePath, \"utf-8\");\n } catch {\n return null;\n }\n}\n\nconst _currentFile = fileURLToPath(import.meta.url);\nconst _currentDir = dirname(_currentFile);\n\nfunction findTemplateDir(): string | null {\n // Walk up from current file to find package root (works in both dist/ and src/)\n let dir = _currentDir;\n while (!existsSync(join(dir, \"package.json\"))) {\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n\n const templateDir = join(dir, \"templates\");\n if (existsSync(join(templateDir, \"base\"))) return templateDir;\n return null;\n}\n\nexport function checkInfrastructureDrift(\n cwd: string,\n templateDir: string,\n): Diagnostic[] {\n const diagnostics: Diagnostic[] = [];\n\n for (const file of INFRASTRUCTURE_FILES) {\n const portalPath = join(cwd, file);\n const templatePath = join(templateDir, \"base\", file);\n\n if (!existsSync(portalPath)) {\n diagnostics.push({\n file,\n severity: \"warn\",\n message: `Missing file: ${file}`,\n });\n continue;\n }\n\n if (!existsSync(templatePath)) {\n // Template doesn't have this file — skip\n continue;\n }\n\n const portalContent = readFileOrNull(portalPath);\n const templateContent = readFileOrNull(templatePath);\n\n if (portalContent !== null && templateContent !== null) {\n if (portalContent.trim() !== templateContent.trim()) {\n diagnostics.push({\n file,\n severity: \"warn\",\n message: `Content differs from canonical template. Review and update if needed.`,\n });\n }\n }\n }\n\n return diagnostics;\n}\n\nexport function checkEntryPatterns(cwd: string): Diagnostic[] {\n const diagnostics: Diagnostic[] = [];\n\n for (const check of ENTRY_CHECKS) {\n const filePath = join(cwd, check.file);\n const content = readFileOrNull(filePath);\n\n if (content === null) {\n diagnostics.push({\n file: check.file,\n severity: \"error\",\n message: `Missing file. ${check.hint}`,\n });\n continue;\n }\n\n if (!content.includes(check.expectedPattern)) {\n diagnostics.push({\n file: check.file,\n severity: \"warn\",\n message: `Does not contain expected pattern \"${check.expectedPattern}\". ${check.hint}`,\n });\n }\n }\n\n return diagnostics;\n}\n\nexport function checkRemovedFiles(cwd: string): Diagnostic[] {\n const diagnostics: Diagnostic[] = [];\n\n for (const check of REMOVED_FILES) {\n if (existsSync(join(cwd, check.file))) {\n diagnostics.push({\n file: check.file,\n severity: \"info\",\n message: `File can be removed. ${check.hint}`,\n });\n }\n }\n\n return diagnostics;\n}\n\nfunction formatDiagnostic(d: Diagnostic): string {\n const icon =\n d.severity === \"error\"\n ? chalk.red(\"ERROR\")\n : d.severity === \"warn\"\n ? chalk.yellow(\" WARN\")\n : chalk.blue(\" INFO\");\n\n return ` ${icon} ${chalk.bold(d.file)}\\n ${chalk.dim(d.message)}`;\n}\n\nexport const doctorCommand: Command = new Command(\"doctor\")\n .description(\n \"Check portal for scaffold drift and report stale infrastructure files\",\n )\n .action(async () => {\n const cwd = process.cwd();\n\n // Validate we're in a portal project\n const packageJsonPath = join(cwd, \"package.json\");\n if (!existsSync(packageJsonPath)) {\n console.error(\n chalk.red(\"Error: No package.json found in current directory\"),\n );\n console.error(\n chalk.yellow(\"Make sure you're in a Fluid portal project directory\"),\n );\n process.exit(1);\n }\n\n let packageJson: {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n };\n try {\n packageJson = JSON.parse(readFileSync(packageJsonPath, \"utf-8\"));\n } catch {\n console.error(chalk.red(\"Error: Could not parse package.json\"));\n console.error(chalk.yellow(\"Ensure package.json contains valid JSON\"));\n process.exit(1);\n }\n const deps = {\n ...packageJson.dependencies,\n ...packageJson.devDependencies,\n };\n\n if (!deps[\"@fluid-app/portal-sdk\"]) {\n console.error(\n chalk.red(\"Error: @fluid-app/portal-sdk not found in dependencies\"),\n );\n console.error(\n chalk.yellow(\n \"This command must be run from a Fluid portal project directory\",\n ),\n );\n process.exit(1);\n }\n\n console.log();\n console.log(chalk.bold(\"Fluid Portal Doctor\"));\n console.log(chalk.dim(\"Checking for scaffold drift...\\n\"));\n\n const diagnostics: Diagnostic[] = [];\n\n // Check entry file patterns\n diagnostics.push(...checkEntryPatterns(cwd));\n\n // Check for files that should be removed\n diagnostics.push(...checkRemovedFiles(cwd));\n\n // Check infrastructure drift against templates (if available)\n const templateDir = findTemplateDir();\n if (templateDir) {\n diagnostics.push(...checkInfrastructureDrift(cwd, templateDir));\n } else {\n console.log(\n chalk.dim(\n \" (Skipping infrastructure diff — template files not available)\\n\",\n ),\n );\n }\n\n // Report results\n if (diagnostics.length === 0) {\n console.log(chalk.green(\" All clear — no drift detected.\\n\"));\n return;\n }\n\n const errors = diagnostics.filter((d) => d.severity === \"error\");\n const warns = diagnostics.filter((d) => d.severity === \"warn\");\n const infos = diagnostics.filter((d) => d.severity === \"info\");\n\n for (const d of [...errors, ...warns, ...infos]) {\n console.log(formatDiagnostic(d));\n console.log();\n }\n\n // Summary\n const parts: string[] = [];\n if (errors.length > 0) parts.push(chalk.red(`${errors.length} error(s)`));\n if (warns.length > 0)\n parts.push(chalk.yellow(`${warns.length} warning(s)`));\n if (infos.length > 0) parts.push(chalk.blue(`${infos.length} info`));\n console.log(` ${parts.join(\", \")}\\n`);\n\n if (errors.length > 0) {\n process.exit(1);\n }\n });\n\nexport function registerDoctorCommand(ctx: PluginContext): void {\n ctx.program.addCommand(doctorCommand);\n}\n","import { Command } from \"commander\";\nimport type { PluginContext } from \"@fluid-app/fluid-cli\";\nimport { getAuthToken } from \"@fluid-app/fluid-cli\";\nimport chalk from \"chalk\";\nimport { createFetchClient, fluidOs } from \"@fluid-app/fluidos-api-client\";\nimport prompts from \"prompts\";\nimport path from \"node:path\";\nimport { readMappings } from \"../utils/mappings.js\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst PORTAL_SYNC_DIR = \".portal-sync\";\n\nfunction getApiBase(): string {\n return process.env[\"FLUID_API_BASE\"] ?? \"https://api.fluid.app\";\n}\n\nfunction requireToken(): string {\n const token = getAuthToken();\n if (!token) {\n console.error(\n chalk.red(\"Error:\") +\n \" Not logged in. Run \" +\n chalk.cyan(\"`fluid login`\") +\n \" first.\",\n );\n process.exit(1);\n }\n return token;\n}\n\nfunction createClient(token: string) {\n return createFetchClient({\n baseUrl: getApiBase(),\n getAuthToken: () => token,\n });\n}\n\nasync function requireDefinitionId(): Promise<number> {\n const cwd = process.cwd();\n const mappings = await readMappings(path.join(cwd, PORTAL_SYNC_DIR));\n if (!mappings) {\n console.error(\n chalk.red(\"Error:\") +\n \" No definition pulled. Run \" +\n chalk.cyan(\"`fluid portal pull`\") +\n \" first.\",\n );\n process.exit(1);\n }\n return mappings.definition.id;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Subcommands\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst createVersionCommand = new Command(\"create\")\n .description(\"Create a new version (snapshot) of the portal definition\")\n .option(\"--activate\", \"Activate the version immediately after creation\")\n .action(async (options: { activate?: boolean }) => {\n const token = requireToken();\n const client = createClient(token);\n const definitionId = await requireDefinitionId();\n\n console.log();\n console.log(chalk.bold(\"Creating version...\"));\n\n let result;\n try {\n result = await fluidOs.fluid_os_v0_create_fluid_osversion(\n client,\n definitionId,\n );\n } catch (err) {\n console.error(\n chalk.red(\"Error:\") +\n \" Failed to create version — \" +\n (err instanceof Error ? err.message : String(err)),\n );\n process.exit(1);\n }\n\n const version = result.version;\n\n if (!version?.id) {\n console.error(\n chalk.red(\"Error:\") +\n \" Failed to create version — unexpected response.\",\n );\n process.exit(1);\n }\n\n console.log();\n console.log(chalk.green(\"Version created successfully.\"));\n console.log();\n console.log(chalk.gray(\"Version ID: \") + chalk.white(version.id));\n\n let active = version.active ?? false;\n\n if (options.activate) {\n console.log(\"Activating version...\");\n try {\n await fluidOs.fluid_os_v0_update_fluid_osversion(\n client,\n definitionId,\n version.id,\n {\n version: { active: true },\n },\n );\n } catch (err) {\n console.error(\n chalk.red(\"Error:\") +\n \" Failed to activate version — \" +\n (err instanceof Error ? err.message : String(err)),\n );\n process.exit(1);\n }\n active = true;\n }\n\n console.log(\n chalk.gray(\"Active: \") +\n (active ? chalk.green(\"yes\") : chalk.gray(\"no\")),\n );\n console.log();\n });\n\nconst listVersionCommand = new Command(\"list\")\n .description(\"List all versions of the portal definition\")\n .action(async () => {\n const token = requireToken();\n const client = createClient(token);\n const definitionId = await requireDefinitionId();\n\n let result;\n try {\n result = await fluidOs.fluid_os_v0_list_fluid_osversions(\n client,\n definitionId,\n );\n } catch (err) {\n console.error(\n chalk.red(\"Error:\") +\n \" Failed to list versions — \" +\n (err instanceof Error ? err.message : String(err)),\n );\n process.exit(1);\n }\n\n const versions = result.version;\n\n if (!Array.isArray(versions) || versions.length === 0) {\n console.log();\n console.log(chalk.yellow(\"No versions found.\"));\n console.log(\n \"Run \" +\n chalk.cyan(\"`fluid portal version create`\") +\n \" to publish a version.\",\n );\n console.log();\n return;\n }\n\n console.log();\n\n // Print table header\n const COL_ID = \"Version ID\".padEnd(36);\n const COL_ACT = \"Active\".padEnd(6);\n const COL_PUB = \"Published\";\n console.log(chalk.gray(` ${COL_ID} ${COL_ACT} ${COL_PUB}`));\n\n for (const v of versions) {\n const id = String(v.id).padEnd(36);\n const active = v.active ? chalk.green(\"\\u2713\") + \" \" : \" \";\n const published = v.published_at\n ? new Date(v.published_at).toLocaleString()\n : \"\\u2014\";\n console.log(` ${id} ${active} ${published}`);\n }\n\n console.log();\n });\n\nconst activateVersionCommand = new Command(\"activate\")\n .description(\"Activate a specific version, making it the live version\")\n .argument(\"<version-id>\", \"The version ID to activate\")\n .option(\"-y, --yes\", \"Skip confirmation prompt\")\n .action(async (versionId: string, options: { yes?: boolean }) => {\n const token = requireToken();\n const client = createClient(token);\n const definitionId = await requireDefinitionId();\n\n if (!options.yes) {\n const { confirm } = await prompts({\n type: \"confirm\",\n name: \"confirm\",\n message: `Activate version ${versionId}? This will make it the live version.`,\n initial: false,\n });\n\n if (!confirm) {\n console.log(chalk.yellow(\"Aborted.\"));\n return;\n }\n }\n\n console.log();\n console.log(chalk.bold(\"Activating version...\"));\n\n try {\n await fluidOs.fluid_os_v0_update_fluid_osversion(\n client,\n definitionId,\n versionId,\n {\n version: { active: true },\n },\n );\n } catch (err) {\n console.error(\n chalk.red(\"Error:\") +\n \" Failed to activate version — \" +\n (err instanceof Error ? err.message : String(err)),\n );\n process.exit(1);\n }\n\n console.log();\n console.log(chalk.green(\"Version \" + versionId + \" is now active.\"));\n console.log();\n });\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Version command group\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport const versionCommand: Command = new Command(\"version\")\n .description(\"Manage portal definition versions\")\n .addCommand(createVersionCommand)\n .addCommand(listVersionCommand)\n .addCommand(activateVersionCommand);\n\nexport function registerVersionCommand(ctx: PluginContext): void {\n ctx.program.addCommand(versionCommand);\n}\n","import { Command } from \"commander\";\nimport type { PluginContext } from \"@fluid-app/fluid-cli\";\nimport chalk from \"chalk\";\nimport ora from \"ora\";\nimport type { WidgetPublishOptions } from \"../types.js\";\nimport {\n createAuthenticatedWidgetPackageClient,\n printDryRunSessionPayload,\n printWidgetPublishSummary,\n printRuntimeOnlyNotice,\n publishWidgetRuntimeArtifacts,\n} from \"./widget-package-publish.js\";\n\nconst DEFAULT_OUT_DIR = \".fluid/widget-dist\";\n\nconst publishSubcommand = new Command(\"publish\")\n .description(\"Publish droplet-owned widget runtime artifacts\")\n .requiredOption(\n \"--droplet <uuid>\",\n \"Droplet UUID that owns the widget package\",\n )\n .option(\n \"-o, --out-dir <dir>\",\n \"Widget artifact output directory\",\n DEFAULT_OUT_DIR,\n )\n .option(\"--dry-run\", \"Build and validate upload payload without publishing\")\n .action(async (options: WidgetPublishOptions) => {\n const outDir = options.outDir ?? DEFAULT_OUT_DIR;\n const dryRun = options.dryRun === true;\n\n console.log();\n console.log(chalk.blue.bold(\"Fluid Widget Publish\"));\n console.log();\n printRuntimeOnlyNotice();\n console.log(chalk.gray(\"Droplet: \") + chalk.white(options.droplet));\n console.log(chalk.gray(\"Output: \") + chalk.cyan(outDir));\n if (dryRun)\n console.log(chalk.yellow(\"Dry run: no upload will be created.\"));\n console.log();\n\n const spinner = ora(\"Building droplet-owned widget package...\").start();\n\n try {\n const result = await publishWidgetRuntimeArtifacts({\n projectDir: process.cwd(),\n outDir,\n buildOwner: \"droplet\",\n uploadOwner: { kind: \"droplet\", uuid: options.droplet },\n dryRun,\n ...(dryRun ? {} : { client: createAuthenticatedWidgetPackageClient() }),\n });\n\n spinner.succeed(\"Built widget runtime artifacts\");\n\n if (dryRun) {\n console.log(\n chalk.yellow(\"Dry run complete — upload session was not requested.\"),\n );\n console.log();\n printDryRunSessionPayload(result);\n } else {\n console.log(chalk.green(\"Published widget package version.\"));\n }\n\n printWidgetPublishSummary({\n title: dryRun ? \"Widget publish dry run\" : \"Widget publish\",\n ownerLabel: `droplet ${options.droplet}`,\n outDir,\n result,\n });\n } catch (err) {\n spinner.fail(\"Widget publish failed\");\n console.error(\n chalk.red(\"Error:\") +\n \" \" +\n (err instanceof Error ? err.message : String(err)),\n );\n process.exit(1);\n }\n });\n\nexport const topLevelWidgetCommand: Command = new Command(\"widget\")\n .description(\"Publish Fluid widget packages\")\n .addCommand(publishSubcommand);\n\nexport function registerTopLevelWidgetCommand(ctx: PluginContext): void {\n ctx.program.addCommand(topLevelWidgetCommand);\n}\n","/**\n * @fluid-app/fluid-cli-portal\n *\n * Fluid CLI plugin for building portal applications.\n * Auto-discovered by @fluid-app/fluid-cli via the fluid-cli-* naming convention.\n */\n\nimport { Command } from \"commander\";\nimport type { FluidPlugin, PluginContext } from \"@fluid-app/fluid-cli\";\nimport { createCommand } from \"./commands/create.js\";\nimport { devCommand } from \"./commands/dev.js\";\nimport { buildCommand } from \"./commands/build.js\";\nimport { pullCommand } from \"./commands/pull.js\";\nimport { pushCommand } from \"./commands/push.js\";\nimport { cloneCommand } from \"./commands/clone.js\";\nimport { widgetCommand } from \"./commands/widget-create.js\";\nimport { deployCommand } from \"./commands/deploy.js\";\nimport { doctorCommand } from \"./commands/doctor.js\";\nimport { versionCommand } from \"./commands/version.js\";\n\nconst plugin: FluidPlugin = {\n name: \"fluid-cli-portal\",\n version: \"0.1.0\",\n async register(ctx: PluginContext) {\n const portal = new Command(\"portal\").description(\n \"Build and develop portal applications\",\n );\n\n portal.addCommand(createCommand);\n portal.addCommand(devCommand);\n portal.addCommand(buildCommand);\n portal.addCommand(pullCommand);\n portal.addCommand(pushCommand);\n portal.addCommand(cloneCommand);\n portal.addCommand(deployCommand);\n portal.addCommand(widgetCommand);\n portal.addCommand(doctorCommand);\n portal.addCommand(versionCommand);\n\n ctx.program.addCommand(portal);\n },\n};\n\nexport default plugin;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Re-exports for programmatic usage (e.g., @fluid-app/create-portal-app)\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport type {\n ProjectConfig,\n CreateOptions,\n DevOptions,\n BuildOptions,\n TemplateVariables,\n SelectedPageTemplate,\n TemplateName,\n WidgetCreateOptions,\n PortalDeployOptions,\n WidgetPublishOptions,\n} from \"./types.js\";\n\nexport { TEMPLATES } from \"./types.js\";\n\nexport {\n getInstallCommand,\n getRunCommand,\n runPackageManager,\n installDependencies,\n} from \"./utils/package-manager.js\";\n\nexport {\n getTemplatePaths,\n copyTemplate,\n directoryExists,\n fileExists,\n pathExists,\n createDirectory,\n getSdkVersion,\n readFileSafe,\n writeFileSafe,\n createDirectorySafe,\n copyTemplateSafe,\n getSdkVersionSafe,\n FILE_SYSTEM_ERRORS,\n type FileSystemErrorCode,\n type FileSystemError,\n type TemplatePaths,\n} from \"./utils/file-system.js\";\n\nexport { promptProjectConfig } from \"./utils/prompts.js\";\n\nexport {\n scaffoldFromGitHub,\n rewriteProjectName,\n writeFluidrcProfile,\n TEMPLATE_REPO_URL,\n CANONICAL_PROJECT_NAME,\n type ScaffoldFromGitHubOptions,\n} from \"./utils/scaffold-github.js\";\n\nexport {\n GitError,\n gitMissingError,\n isGitAvailable,\n ensureGitAvailable,\n gitClone,\n gitInit,\n isGitRepo,\n getOriginUrl,\n hasUncommittedChanges,\n gitFetch,\n gitAheadBehindVsFetchHead,\n gitMergeFastForward,\n gitAutoCommit,\n gitPush,\n} from \"./lib/git.js\";\n\nexport {\n createPortalGitCredential,\n portalGitCredentialPath,\n type PortalGitCredential,\n type GitCredentialOutcome,\n type CreatePortalGitCredentialOptions,\n} from \"./api/git-credential.js\";\n\nexport {\n runPortalGitSync,\n type PortalGitSyncResult,\n type PortalGitSyncOptions,\n} from \"./lib/git-sync.js\";\n\nexport { cloneCommand } from \"./commands/clone.js\";\n\n// Expose the standalone Command for create-portal-app\nexport {\n createCommand,\n createPortalProject,\n type CreatePortalProjectDependencies,\n} from \"./commands/create.js\";\nexport { pullCommand } from \"./commands/pull.js\";\nexport { pushCommand } from \"./commands/push.js\";\nexport { widgetCommand } from \"./commands/widget-create.js\";\nexport { topLevelWidgetCommand } from \"./commands/widget-publish.js\";\nexport { deployCommand } from \"./commands/deploy.js\";\nexport { versionCommand } from \"./commands/version.js\";\nexport { doctorCommand } from \"./commands/doctor.js\";\n\nexport {\n readMappings,\n writeMappings,\n deriveSlug,\n resolveSlugToId,\n resolveIdToSlug,\n updateMapping,\n removeMapping,\n type PortalMappings,\n type DefinitionMapping,\n type MappedResourceType,\n} from \"./utils/mappings.js\";\n\nexport {\n computeFileHash,\n readSnapshot,\n writeSnapshot,\n diffAgainstSnapshot,\n buildSnapshot,\n type Snapshot,\n type SnapshotDiff,\n type FileHash,\n type FileHashMap,\n} from \"./utils/snapshot.js\";\n\nexport {\n transformScreen,\n deriveScreenSlug,\n transformTheme,\n transformNavigation,\n transformNavigationItems,\n transformProfile,\n buildIdToSlugMap,\n buildNavigationIdToSlugMap,\n buildThemeIdToSlugMap,\n type LocalScreen,\n type LocalTheme,\n type LocalNavigation,\n type LocalNavigationItem,\n type LocalProfile,\n} from \"./utils/transform.js\";\n\nexport {\n categorizeChanges,\n validateCrossReferences,\n slugFromPath,\n type CategorizedChanges,\n type ValidationError,\n} from \"./utils/push-validation.js\";\n\nexport {\n buildSharedWidgetPackage,\n createWidgetPackageEntrySource,\n type BuildSharedWidgetPackageOptions,\n type SharedWidgetPackageBuildResult,\n type SharedWidgetPackageBuildError,\n} from \"./utils/widget-package-builder.js\";\n\nexport {\n resolvePortalWidgetSourceConfig,\n loadSourceWidgetPackages,\n type WidgetSourceConfig,\n type WidgetSourceConfigLoadError,\n} from \"./utils/widget-package-config.js\";\n\nexport {\n validateSingleSourceWidgetPackage,\n buildWidgetPackagePublishManifest,\n type SourceWidgetMetadata,\n type SourceWidgetPackageMetadata,\n type WidgetDescriptorMetadata,\n type ValidatedWidgetPackageBuild,\n type WidgetPackageValidationErrorCode,\n type WidgetPackageValidationError,\n type WidgetPackageValidationResult,\n type ValidateSourceWidgetPackageOptions,\n type WidgetPackageOwnerKind,\n type WidgetPackageArtifactPath,\n type WidgetPackagePublishManifest,\n} from \"./utils/widget-package-validation.js\";\n\nexport type { RemoteDomWidgetPackageDescriptor } from \"@fluid-app/portal-core/remote-dom-widget-package\";\n\nexport {\n WIDGET_PACKAGE_BUILDER_VERSION,\n computeArtifactMetadata,\n collectWidgetPackageArtifacts,\n createPublishManifestMetadata,\n getArtifactContentType,\n sha256,\n type ArtifactMetadata,\n type PublishManifestMetadata,\n} from \"./utils/widget-package-artifacts.js\";\n\nexport {\n publishWidgetRuntimeArtifacts,\n createWidgetPackagePublishRequest,\n createAuthenticatedWidgetPackageClient,\n printDryRunSessionPayload,\n printWidgetPublishSummary,\n printRuntimeOnlyNotice,\n validateWidgetPublishOutDir,\n type PublishWidgetRuntimeArtifactsOptions,\n type PublishWidgetRuntimeArtifactsResult,\n type PublishWidgetRuntimeArtifactsDependencies,\n} from \"./commands/widget-package-publish.js\";\n\nexport {\n buildWidgetPackageUploadSessionPayload,\n getWidgetPackageUploadSessionRoute,\n getWidgetPackageCompleteUploadRoute,\n requestWidgetPackageUploadSession,\n uploadWidgetPackageArtifacts,\n completeWidgetPackageUpload,\n publishWidgetPackageVersion,\n normalizeWidgetPackageUploadSession,\n normalizeWidgetPackageCompleteResponse,\n buildWidgetPackageCompleteUploadPayload,\n WidgetPackageUploadError,\n type WidgetPackageUploadOwner,\n type WidgetPackageArtifactMetadata,\n type WidgetPackageUploadArtifact,\n type WidgetPackageSessionRequest,\n type WidgetPackagePublishRequest,\n type WidgetPackageArtifactPayload,\n type WidgetPackageUploadSessionPayload,\n type WidgetPackageCompleteUploadPayload,\n type WidgetPackageSignedUpload,\n type WidgetPackageUploadSession,\n type WidgetPackageArtifactUploadResult,\n type WidgetPackageCompleteResult,\n type WidgetPackagePublishResult,\n type PublishWidgetPackageVersionOptions,\n type WidgetPackageUploadFetch,\n} from \"./utils/widget-package-upload.js\";\n"],"mappings":"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"}
|