@duffcloudservices/cli 0.3.2 → 0.3.3

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.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../package.json","../src/commands/auth.ts","../src/auth/device-flow.ts","../src/auth/credentials.ts","../src/commands/sites.ts","../src/api/portal-client.ts","../src/commands/init.ts","../src/templates/site-deploy.yml","../src/commands/validate.ts","../src/commands/plans.ts","../src/commands/capture-snapshots.ts"],"sourcesContent":["/**\n * Main CLI entry point.\n */\n\nimport { Command } from 'commander'\nimport chalk from 'chalk'\nimport { version } from '../package.json'\n\n// Import commands\nimport { loginCommand, logoutCommand, whoamiCommand } from './commands/auth.js'\nimport { listSitesCommand, showSiteCommand } from './commands/sites.js'\nimport { initCommand } from './commands/init.js'\nimport { validateCommand } from './commands/validate.js'\nimport { plansCommand } from './commands/plans.js'\nimport { captureSnapshotsCommand } from './commands/capture-snapshots.js'\n\nconst program = new Command()\n\nprogram\n .name('dcs')\n .description('DCS (Duff Cloud Services) CLI for customer site management')\n .version(version)\n\n// =============================================================================\n// Auth Commands\n// =============================================================================\n\nprogram\n .command('login')\n .description('Authenticate with DCS Portal using Google OAuth')\n .action(async () => {\n await loginCommand()\n })\n\nprogram\n .command('logout')\n .description('Clear stored credentials')\n .action(async () => {\n await logoutCommand()\n })\n\nprogram\n .command('whoami')\n .description('Display current authenticated user')\n .action(async () => {\n await whoamiCommand()\n })\n\n// =============================================================================\n// Site Commands\n// =============================================================================\n\nconst sitesCmd = program\n .command('sites')\n .description('Manage customer sites')\n\nsitesCmd\n .command('list')\n .description('List sites you have access to')\n .action(async () => {\n await listSitesCommand()\n })\n\nsitesCmd\n .command('show <slug>')\n .description('Show details for a specific site')\n .action(async (slug: string) => {\n await showSiteCommand(slug)\n })\n\n// =============================================================================\n// Init Command\n// =============================================================================\n\nprogram\n .command('init')\n .description('Initialize DCS integration for a customer site')\n .requiredOption('-s, --site-slug <slug>', 'Site slug (lowercase, hyphens)')\n .requiredOption('-n, --site-name <name>', 'Human-readable site name')\n .option('-f, --framework <framework>', 'Site framework (vue, astro)', 'vue')\n .option('-t, --target <dir>', 'Target directory', '.')\n .option('--dry-run', 'Show what would be created without writing files')\n .option('--force', 'Overwrite existing files')\n .action(async (options) => {\n await initCommand({\n siteSlug: options.siteSlug,\n siteName: options.siteName,\n framework: options.framework,\n target: options.target,\n dryRun: options.dryRun,\n force: options.force,\n })\n })\n\n// =============================================================================\n// Validate Command\n// =============================================================================\n\nprogram\n .command('validate')\n .description('Validate .dcs configuration files')\n .option('-t, --target <dir>', 'Target directory', '.')\n .option('--fix', 'Attempt to fix common issues')\n .option('-v, --verbose', 'Show detailed output')\n .action(async (options) => {\n await validateCommand({\n target: options.target,\n fix: options.fix,\n verbose: options.verbose,\n })\n })\n\n// =============================================================================\n// Plans Command\n// =============================================================================\n\nprogram\n .command('plans')\n .description('Generate DCS integration plan files for AI-assisted onboarding')\n .option('-t, --target <dir>', 'Target directory', '.')\n .option('--force', 'Overwrite existing plan files')\n .action(async (options) => {\n await plansCommand({\n target: options.target,\n force: options.force,\n })\n })\n\n// =============================================================================\n// Capture Snapshots Command\n// =============================================================================\n\nprogram\n .command('capture-snapshots')\n .description('Capture visual snapshots of site pages for the portal page editor')\n .option('-t, --target <dir>', 'Target directory containing .dcs/pages.yaml', '.')\n .option('-u, --base-url <url>', 'Base URL of the running site', 'http://localhost:5173')\n .option('-p, --pages <slugs>', 'Comma-separated list of page slugs to capture')\n .option('--dry-run', 'Show what would be captured without launching browser')\n .option('-v, --verbose', 'Show detailed output')\n .action(async (options) => {\n const pages = options.pages ? options.pages.split(',').map((s: string) => s.trim()) : undefined\n await captureSnapshotsCommand({\n target: options.target,\n baseUrl: options.baseUrl,\n dryRun: options.dryRun,\n verbose: options.verbose,\n pages,\n })\n })\n\n// =============================================================================\n// Error Handling\n// =============================================================================\n\nprogram.exitOverride()\n\ntry {\n await program.parseAsync(process.argv)\n} catch (error) {\n if (error instanceof Error) {\n // Commander errors (help, version) are expected\n if ((error as { code?: string }).code === 'commander.helpDisplayed' ||\n (error as { code?: string }).code === 'commander.version') {\n process.exit(0)\n }\n\n console.error(chalk.red('Error:'), error.message)\n process.exit(1)\n }\n}\n","{\n \"name\": \"@duffcloudservices/cli\",\n \"version\": \"0.3.2\",\n \"description\": \"CLI for DCS site onboarding and configuration\",\n \"type\": \"module\",\n \"bin\": {\n \"dcs\": \"./dist/index.js\"\n },\n \"main\": \"./dist/index.js\",\n \"files\": [\n \"dist\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"start\": \"node dist/index.js\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"type-check\": \"tsc --noEmit\",\n \"lint\": \"eslint src --ext .ts\",\n \"prepublishOnly\": \"pnpm run build\"\n },\n \"dependencies\": {\n \"chalk\": \"^5.3.0\",\n \"cli-table3\": \"^0.6.5\",\n \"commander\": \"^12.1.0\",\n \"conf\": \"^13.0.1\",\n \"handlebars\": \"^4.7.8\",\n \"inquirer\": \"^9.3.7\",\n \"js-yaml\": \"^4.1.0\",\n \"open\": \"^10.1.0\",\n \"ora\": \"^8.1.1\",\n \"playwright\": \"^1.49.0\"\n },\n \"devDependencies\": {\n \"@types/inquirer\": \"^9.0.7\",\n \"@types/js-yaml\": \"^4.0.9\",\n \"@types/node\": \"^20.11.0\",\n \"tsup\": \"^8.0.0\",\n \"typescript\": \"~5.6.3\",\n \"vitest\": \"^3.2.3\"\n },\n \"keywords\": [\n \"dcs\",\n \"cli\",\n \"cms\",\n \"onboarding\",\n \"duff-cloud-services\"\n ],\n \"author\": \"Duff Cloud Services\",\n \"license\": \"MIT\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/NateDuff/dcs\"\n },\n \"homepage\": \"https://portal.duffcloudservices.com\",\n \"bugs\": {\n \"url\": \"https://github.com/NateDuff/dcs/issues\"\n },\n \"engines\": {\n \"node\": \">=18.0.0\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n }\n}\n","/**\n * Authentication commands for the CLI.\n */\n\nimport chalk from 'chalk'\nimport { login as performLogin, type DeviceFlowOptions } from '../auth/device-flow.js'\nimport { clearCredentials, getCurrentUserEmail, getConfigPath } from '../auth/credentials.js'\n\n/**\n * Login command - authenticate with DCS Portal.\n */\nexport async function loginCommand(options: DeviceFlowOptions = {}): Promise<void> {\n try {\n await performLogin(options)\n } catch (error) {\n if (error instanceof Error) {\n console.error(chalk.red('Login failed:'), error.message)\n }\n process.exit(1)\n }\n}\n\n/**\n * Logout command - clear stored credentials.\n */\nexport async function logoutCommand(): Promise<void> {\n clearCredentials()\n console.log(chalk.green('✓'), 'Logged out successfully')\n}\n\n/**\n * Whoami command - display current user.\n */\nexport async function whoamiCommand(): Promise<void> {\n const email = getCurrentUserEmail()\n\n if (!email) {\n console.log(chalk.yellow('Not logged in.'))\n console.log('Run', chalk.cyan('dcs login'), 'to authenticate.')\n return\n }\n\n console.log('Logged in as:', chalk.green(email))\n console.log(chalk.dim('Config file:'), chalk.dim(getConfigPath()))\n}\n","/**\n * Device authorization flow for CLI authentication.\n *\n * Implements RFC 8628 (OAuth 2.0 Device Authorization Grant) style flow\n * for authenticating CLI users via the DCS Portal.\n */\n\nimport open from 'open'\nimport ora from 'ora'\nimport chalk from 'chalk'\nimport { storeCredentials, type StoredCredentials } from './credentials.js'\n\nconst DEFAULT_API_URL = 'https://portal.duffcloudservices.com'\n\nexport interface DeviceAuthResponse {\n deviceCode: string\n userCode: string\n verificationUri: string\n verificationUriComplete: string\n expiresIn: number\n interval: number\n}\n\nexport interface TokenResponse {\n accessToken: string\n refreshToken: string\n tokenType: string\n expiresIn: number\n email: string\n userId: string\n}\n\nexport interface DeviceFlowOptions {\n apiUrl?: string\n clientId?: string\n}\n\n/**\n * Error codes returned during device authorization polling.\n */\ntype DeviceAuthError = 'authorization_pending' | 'slow_down' | 'expired_token' | 'access_denied'\n\n/**\n * Sleep for a given number of milliseconds.\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n\n/**\n * Start the device authorization flow.\n */\nexport async function startDeviceAuth(options: DeviceFlowOptions = {}): Promise<DeviceAuthResponse> {\n const apiUrl = options.apiUrl || DEFAULT_API_URL\n const clientId = options.clientId || 'dcs-cli'\n\n const response = await fetch(`${apiUrl}/api/v1/cli/auth/device`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ client_id: clientId }),\n })\n\n if (!response.ok) {\n const error = await response.text()\n throw new Error(`Failed to start device authorization: ${error}`)\n }\n\n const data = await response.json()\n\n return {\n deviceCode: data.device_code,\n userCode: data.user_code,\n verificationUri: data.verification_uri,\n verificationUriComplete: data.verification_uri_complete,\n expiresIn: data.expires_in,\n interval: data.interval,\n }\n}\n\n/**\n * Poll for authorization completion.\n */\nasync function pollForToken(\n deviceCode: string,\n apiUrl: string\n): Promise<TokenResponse | { error: DeviceAuthError }> {\n const response = await fetch(`${apiUrl}/api/v1/cli/auth/token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n device_code: deviceCode,\n grant_type: 'urn:ietf:params:oauth:grant-type:device_code',\n }),\n })\n\n if (!response.ok) {\n const data = await response.json()\n return { error: data.error as DeviceAuthError }\n }\n\n const data = await response.json()\n\n return {\n accessToken: data.access_token,\n refreshToken: data.refresh_token,\n tokenType: data.token_type,\n expiresIn: data.expires_in,\n email: data.email,\n userId: data.user_id,\n }\n}\n\n/**\n * Complete the device authorization flow.\n *\n * This function:\n * 1. Starts the device authorization\n * 2. Opens the browser to the verification URL\n * 3. Polls for completion\n * 4. Stores credentials on success\n */\nexport async function login(options: DeviceFlowOptions = {}): Promise<StoredCredentials> {\n const apiUrl = options.apiUrl || DEFAULT_API_URL\n\n const spinner = ora('Starting authentication...').start()\n\n // Start device auth\n let deviceAuth: DeviceAuthResponse\n try {\n deviceAuth = await startDeviceAuth(options)\n } catch (error) {\n spinner.fail('Failed to start authentication')\n throw error\n }\n\n spinner.stop()\n\n // Display instructions\n console.log()\n console.log(chalk.bold('Please visit:'), chalk.cyan(deviceAuth.verificationUri))\n console.log(chalk.bold('And enter code:'), chalk.yellow.bold(deviceAuth.userCode))\n console.log()\n\n // Try to open browser\n try {\n await open(deviceAuth.verificationUriComplete)\n console.log(chalk.dim('(Browser opened automatically)'))\n } catch {\n console.log(chalk.dim('(Please open the link manually)'))\n }\n\n console.log()\n\n // Poll for completion\n const pollSpinner = ora('Waiting for authentication...').start()\n\n let interval = deviceAuth.interval\n const deadline = Date.now() + deviceAuth.expiresIn * 1000\n\n while (Date.now() < deadline) {\n await sleep(interval * 1000)\n\n try {\n const result = await pollForToken(deviceAuth.deviceCode, apiUrl)\n\n if ('error' in result) {\n if (result.error === 'authorization_pending') {\n continue\n }\n if (result.error === 'slow_down') {\n interval += 5\n continue\n }\n if (result.error === 'expired_token') {\n pollSpinner.fail('Authentication timed out')\n throw new Error('Authentication timed out. Please try again.')\n }\n if (result.error === 'access_denied') {\n pollSpinner.fail('Authentication denied')\n throw new Error('Authentication was denied.')\n }\n } else {\n // Success!\n const credentials: StoredCredentials = {\n accessToken: result.accessToken,\n refreshToken: result.refreshToken,\n expiresAt: Date.now() + result.expiresIn * 1000,\n email: result.email,\n userId: result.userId,\n }\n\n storeCredentials(credentials)\n\n pollSpinner.succeed(`Authenticated as ${chalk.green(result.email)}`)\n return credentials\n }\n } catch (error) {\n // Network error - keep polling\n if (error instanceof Error && error.message.includes('fetch')) {\n continue\n }\n throw error\n }\n }\n\n pollSpinner.fail('Authentication timed out')\n throw new Error('Authentication timed out. Please try again.')\n}\n\n/**\n * Refresh the access token using the refresh token.\n */\nexport async function refreshAccessToken(\n refreshToken: string,\n options: DeviceFlowOptions = {}\n): Promise<{ accessToken: string; expiresIn: number }> {\n const apiUrl = options.apiUrl || DEFAULT_API_URL\n\n const response = await fetch(`${apiUrl}/api/v1/cli/auth/refresh`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ refresh_token: refreshToken }),\n })\n\n if (!response.ok) {\n throw new Error('Failed to refresh access token. Please login again.')\n }\n\n const data = await response.json()\n\n return {\n accessToken: data.access_token,\n expiresIn: data.expires_in,\n }\n}\n","/**\n * Credential storage for CLI authentication.\n *\n * Stores access tokens, refresh tokens, and user info in a config file.\n * Uses the `conf` package for encrypted storage on supported platforms.\n */\n\nimport Conf from 'conf'\n\nexport interface StoredCredentials {\n accessToken: string\n refreshToken: string\n expiresAt: number\n email: string\n userId: string\n}\n\ninterface ConfigSchema {\n auth?: StoredCredentials\n}\n\nconst config = new Conf<ConfigSchema>({\n projectName: 'dcs-cli',\n schema: {\n auth: {\n type: 'object',\n properties: {\n accessToken: { type: 'string' },\n refreshToken: { type: 'string' },\n expiresAt: { type: 'number' },\n email: { type: 'string' },\n userId: { type: 'string' },\n },\n },\n },\n})\n\n/**\n * Store credentials after successful authentication.\n */\nexport function storeCredentials(credentials: StoredCredentials): void {\n config.set('auth', credentials)\n}\n\n/**\n * Get stored credentials.\n * Returns undefined if not authenticated.\n */\nexport function getCredentials(): StoredCredentials | undefined {\n return config.get('auth')\n}\n\n/**\n * Clear stored credentials (logout).\n */\nexport function clearCredentials(): void {\n config.delete('auth')\n}\n\n/**\n * Check if user is authenticated with valid (non-expired) credentials.\n */\nexport function isAuthenticated(): boolean {\n const creds = getCredentials()\n if (!creds) return false\n\n // Check if access token is expired (with 5 minute buffer)\n const bufferMs = 5 * 60 * 1000\n return creds.expiresAt > Date.now() + bufferMs\n}\n\n/**\n * Get the current user's email if authenticated.\n */\nexport function getCurrentUserEmail(): string | undefined {\n return getCredentials()?.email\n}\n\n/**\n * Get the current user's ID if authenticated.\n */\nexport function getCurrentUserId(): string | undefined {\n return getCredentials()?.userId\n}\n\n/**\n * Get the access token if available and not expired.\n */\nexport function getAccessToken(): string | undefined {\n const creds = getCredentials()\n if (!creds) return undefined\n\n // Return even if expired - the API client will handle refresh\n return creds.accessToken\n}\n\n/**\n * Get the refresh token if available.\n */\nexport function getRefreshToken(): string | undefined {\n return getCredentials()?.refreshToken\n}\n\n/**\n * Update the access token after a refresh.\n */\nexport function updateAccessToken(accessToken: string, expiresIn: number): void {\n const creds = getCredentials()\n if (!creds) return\n\n config.set('auth', {\n ...creds,\n accessToken,\n expiresAt: Date.now() + expiresIn * 1000,\n })\n}\n\n/**\n * Get the path to the config file (for debugging).\n */\nexport function getConfigPath(): string {\n return config.path\n}\n","/**\n * Sites commands for the CLI.\n */\n\nimport chalk from 'chalk'\nimport Table from 'cli-table3'\nimport { getPortalClient, type Site } from '../api/portal-client.js'\nimport { isAuthenticated } from '../auth/credentials.js'\n\n/**\n * List sites command - display sites the user has access to.\n */\nexport async function listSitesCommand(): Promise<void> {\n if (!isAuthenticated()) {\n console.log(chalk.yellow('Not logged in.'))\n console.log('Run', chalk.cyan('dcs login'), 'to authenticate.')\n process.exit(1)\n }\n\n const client = getPortalClient()\n\n try {\n const { sites } = await client.listSites()\n\n if (sites.length === 0) {\n console.log(chalk.yellow('No sites found.'))\n console.log('Ask your company admin to grant you access to sites.')\n return\n }\n\n console.log(chalk.bold('\\nYour Sites:\\n'))\n\n const table = new Table({\n head: [\n chalk.cyan('Slug'),\n chalk.cyan('Name'),\n chalk.cyan('Role'),\n chalk.cyan('Company'),\n chalk.cyan('URL'),\n ],\n style: {\n head: [],\n border: [],\n },\n })\n\n for (const site of sites) {\n table.push([\n site.slug,\n site.name,\n formatRole(site.role),\n site.companyName,\n site.productionUrl || chalk.dim('(not deployed)'),\n ])\n }\n\n console.log(table.toString())\n console.log()\n console.log(chalk.dim(`Total: ${sites.length} site(s)`))\n } catch (error) {\n if (error instanceof Error) {\n console.error(chalk.red('Error:'), error.message)\n }\n process.exit(1)\n }\n}\n\n/**\n * Show site details command.\n */\nexport async function showSiteCommand(slug: string): Promise<void> {\n if (!isAuthenticated()) {\n console.log(chalk.yellow('Not logged in.'))\n console.log('Run', chalk.cyan('dcs login'), 'to authenticate.')\n process.exit(1)\n }\n\n const client = getPortalClient()\n\n try {\n const site = await client.getSite(slug)\n displaySiteDetails(site)\n } catch (error) {\n if (error instanceof Error) {\n console.error(chalk.red('Error:'), error.message)\n }\n process.exit(1)\n }\n}\n\nfunction formatRole(role: string): string {\n switch (role) {\n case 'owner':\n return chalk.magenta(role)\n case 'admin':\n return chalk.red(role)\n case 'editor':\n return chalk.yellow(role)\n case 'viewer':\n return chalk.blue(role)\n default:\n return role\n }\n}\n\nfunction displaySiteDetails(site: Site): void {\n console.log()\n console.log(chalk.bold('Site Details'))\n console.log()\n console.log(' Slug: ', chalk.cyan(site.slug))\n console.log(' Name: ', site.name)\n console.log(' Role: ', formatRole(site.role))\n console.log(' Company: ', site.companyName)\n console.log(' Company ID: ', chalk.dim(site.companyId))\n if (site.productionUrl) {\n console.log(' URL: ', chalk.green(site.productionUrl))\n } else {\n console.log(' URL: ', chalk.dim('(not deployed)'))\n }\n console.log()\n}\n","/**\n * Portal API client for CLI operations.\n */\n\nimport { getAccessToken, getRefreshToken, updateAccessToken, isAuthenticated } from '../auth/credentials.js'\nimport { refreshAccessToken } from '../auth/device-flow.js'\n\nconst DEFAULT_API_URL = 'https://portal.duffcloudservices.com'\n\nexport interface Site {\n slug: string\n name: string\n role: 'owner' | 'admin' | 'editor' | 'viewer'\n companyId: string\n companyName: string\n productionUrl?: string\n}\n\nexport interface PortalClientOptions {\n apiUrl?: string\n}\n\nexport class PortalClient {\n private apiUrl: string\n\n constructor(options: PortalClientOptions = {}) {\n this.apiUrl = options.apiUrl || process.env.DCS_API_URL || DEFAULT_API_URL\n }\n\n /**\n * Make an authenticated API request.\n */\n private async request<T>(\n path: string,\n options: RequestInit = {}\n ): Promise<T> {\n // Get access token\n let accessToken = getAccessToken()\n\n // Check if we need to refresh\n if (!isAuthenticated() && getRefreshToken()) {\n const refreshToken = getRefreshToken()!\n try {\n const { accessToken: newToken, expiresIn } = await refreshAccessToken(refreshToken, {\n apiUrl: this.apiUrl,\n })\n updateAccessToken(newToken, expiresIn)\n accessToken = newToken\n } catch {\n throw new Error('Session expired. Please login again with `dcs login`.')\n }\n }\n\n if (!accessToken) {\n throw new Error('Not authenticated. Please login with `dcs login`.')\n }\n\n const response = await fetch(`${this.apiUrl}${path}`, {\n ...options,\n headers: {\n ...options.headers,\n Authorization: `Bearer ${accessToken}`,\n 'Content-Type': 'application/json',\n },\n })\n\n if (response.status === 401) {\n throw new Error('Session expired. Please login again with `dcs login`.')\n }\n\n if (!response.ok) {\n const error = await response.text()\n throw new Error(`API error (${response.status}): ${error}`)\n }\n\n return response.json()\n }\n\n /**\n * List sites accessible to the authenticated user.\n */\n async listSites(): Promise<{ sites: Site[] }> {\n return this.request('/api/v1/cli/sites')\n }\n\n /**\n * Get details for a specific site.\n */\n async getSite(slug: string): Promise<Site> {\n return this.request(`/api/v1/cli/sites/${slug}`)\n }\n\n /**\n * Validate that the user can create/modify a site.\n */\n async validateSiteAccess(\n slug: string,\n operation: 'read' | 'edit' | 'create'\n ): Promise<{ valid: boolean; role?: string; message?: string }> {\n try {\n const { sites } = await this.listSites()\n const site = sites.find((s) => s.slug === slug)\n\n if (!site) {\n if (operation === 'create') {\n // Check if user has any admin access to create new sites\n const canCreate = sites.some((s) => ['owner', 'admin'].includes(s.role))\n return canCreate\n ? { valid: true, message: 'New site will be created' }\n : { valid: false, message: 'You need admin access in a company to create new sites' }\n }\n return { valid: false, message: `Site '${slug}' not found or you don't have access` }\n }\n\n if (operation === 'read') {\n return { valid: true, role: site.role }\n }\n\n if (operation === 'edit') {\n const canEdit = ['owner', 'admin', 'editor'].includes(site.role)\n return canEdit\n ? { valid: true, role: site.role }\n : { valid: false, role: site.role, message: 'You need editor access to modify this site' }\n }\n\n if (operation === 'create') {\n return { valid: false, message: 'Site already exists' }\n }\n\n return { valid: false }\n } catch (error) {\n if (error instanceof Error) {\n return { valid: false, message: error.message }\n }\n return { valid: false, message: 'Unknown error' }\n }\n }\n}\n\n// Singleton instance\nlet clientInstance: PortalClient | null = null\n\nexport function getPortalClient(options?: PortalClientOptions): PortalClient {\n if (!clientInstance) {\n clientInstance = new PortalClient(options)\n }\n return clientInstance\n}\n","/**\n * Init command - initialize DCS integration for a customer site.\n */\n\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\nimport chalk from 'chalk'\nimport ora from 'ora'\nimport { getPortalClient } from '../api/portal-client.js'\nimport { isAuthenticated } from '../auth/credentials.js'\nimport siteDeployTemplate from '../templates/site-deploy.yml'\n\nexport interface InitOptions {\n siteSlug: string\n siteName: string\n framework?: 'vue' | 'astro'\n target?: string\n dryRun?: boolean\n force?: boolean\n}\n\n/**\n * Initialize DCS integration for a customer site.\n */\nexport async function initCommand(options: InitOptions): Promise<void> {\n const { siteSlug, siteName, framework = 'vue', target = '.', dryRun = false, force = false } = options\n\n // 1. Check authentication\n if (!isAuthenticated()) {\n console.log(chalk.yellow('Not logged in.'))\n console.log('Run', chalk.cyan('dcs login'), 'to authenticate.')\n process.exit(1)\n }\n\n // 2. Validate site access\n const spinner = ora('Validating site access...').start()\n const client = getPortalClient()\n\n try {\n const { sites } = await client.listSites()\n const existingSite = sites.find((s) => s.slug === siteSlug)\n\n if (existingSite) {\n if (!['owner', 'admin', 'editor'].includes(existingSite.role)) {\n spinner.fail(`You don't have edit access to site '${siteSlug}'`)\n process.exit(1)\n }\n spinner.succeed(`Site '${siteSlug}' found (role: ${existingSite.role})`)\n } else {\n // Check if user can create new sites\n const canCreate = sites.some((s) => ['owner', 'admin'].includes(s.role))\n if (!canCreate) {\n spinner.fail('You need admin access in a company to create new sites')\n process.exit(1)\n }\n spinner.info(`Creating new site: ${siteSlug}`)\n }\n } catch (error) {\n spinner.fail('Failed to validate site access')\n if (error instanceof Error) {\n console.error(chalk.red('Error:'), error.message)\n }\n process.exit(1)\n }\n\n // 3. Resolve target directory\n const targetDir = path.resolve(target)\n console.log(chalk.dim(`Target directory: ${targetDir}`))\n\n // 4. Generate configuration files\n const generatedFiles: string[] = []\n const timestamp = new Date().toISOString()\n\n try {\n // .dcs/site.yaml\n const siteYaml = generateSiteYaml({ siteSlug, siteName, timestamp, framework })\n generatedFiles.push(await writeFile(targetDir, '.dcs/site.yaml', siteYaml, { dryRun, force }))\n\n // .dcs/pages.yaml\n const pagesYaml = generatePagesYaml({ siteSlug, timestamp })\n generatedFiles.push(await writeFile(targetDir, '.dcs/pages.yaml', pagesYaml, { dryRun, force }))\n\n // .dcs/content.yaml\n const contentYaml = generateContentYaml({ timestamp })\n generatedFiles.push(await writeFile(targetDir, '.dcs/content.yaml', contentYaml, { dryRun, force }))\n\n // .dcs/seo.yaml\n const seoYaml = generateSeoYaml({ siteName, timestamp })\n generatedFiles.push(await writeFile(targetDir, '.dcs/seo.yaml', seoYaml, { dryRun, force }))\n\n // .dcs/SECTION-CONVENTIONS.md\n const sectionConventions = generateSectionConventions()\n generatedFiles.push(\n await writeFile(targetDir, '.dcs/SECTION-CONVENTIONS.md', sectionConventions, { dryRun, force })\n )\n\n // .github/copilot-instructions.md\n const copilotInstructions = generateCopilotInstructions({ siteName })\n generatedFiles.push(\n await writeFile(targetDir, '.github/copilot-instructions.md', copilotInstructions, { dryRun, force })\n )\n\n // .github/workflows/site-deploy.yml\n const deployWorkflow = generateDeployWorkflow()\n generatedFiles.push(\n await writeFile(targetDir, '.github/workflows/site-deploy.yml', deployWorkflow, { dryRun, force })\n )\n\n // .plans/README.md\n const plansReadme = generatePlansReadme({ siteName, framework })\n generatedFiles.push(await writeFile(targetDir, '.plans/README.md', plansReadme, { dryRun, force }))\n\n // .plans/00-audit-site.md\n const plan00 = generatePlan00AuditSite({ framework })\n generatedFiles.push(await writeFile(targetDir, '.plans/00-audit-site.md', plan00, { dryRun, force }))\n\n // .plans/01-integrate-content.md\n const plan01 = generatePlan01IntegrateContent()\n generatedFiles.push(await writeFile(targetDir, '.plans/01-integrate-content.md', plan01, { dryRun, force }))\n\n // .plans/02-configure-seo.md\n const plan02 = generatePlan02ConfigureSeo()\n generatedFiles.push(await writeFile(targetDir, '.plans/02-configure-seo.md', plan02, { dryRun, force }))\n\n } catch (error) {\n if (error instanceof Error) {\n console.error(chalk.red('Error:'), error.message)\n }\n process.exit(1)\n }\n\n // 5. Display results\n console.log()\n console.log(chalk.bold('Generated files:'))\n for (const file of generatedFiles) {\n console.log(` ${chalk.green('✓')} ${file}`)\n }\n\n // 6. Post-init instructions\n console.log()\n console.log(chalk.bold('Next steps:'))\n console.log(' 1. Install CMS package:', chalk.cyan('pnpm add @duffcloudservices/cms'))\n console.log(' 2. Configure Vite plugins in your build config')\n console.log(' 3. Follow the plans in', chalk.cyan('.plans/'), 'directory')\n console.log()\n}\n\n// =============================================================================\n// File Generation Helpers\n// =============================================================================\n\ninterface WriteOptions {\n dryRun: boolean\n force: boolean\n}\n\nasync function writeFile(\n targetDir: string,\n relativePath: string,\n content: string,\n options: WriteOptions\n): Promise<string> {\n const fullPath = path.join(targetDir, relativePath)\n\n if (options.dryRun) {\n console.log(chalk.dim(`Would create: ${relativePath}`))\n return relativePath\n }\n\n // Check if file exists\n try {\n await fs.access(fullPath)\n if (!options.force) {\n console.log(chalk.yellow(`Skipped (exists): ${relativePath}`))\n return relativePath\n }\n } catch {\n // File doesn't exist, continue\n }\n\n // Create directory if needed\n await fs.mkdir(path.dirname(fullPath), { recursive: true })\n\n // Write file\n await fs.writeFile(fullPath, content, 'utf8')\n\n return relativePath\n}\n\n// =============================================================================\n// Template Generators\n// =============================================================================\n\nfunction generateSiteYaml(data: {\n siteSlug: string\n siteName: string\n timestamp: string\n framework: string\n}): string {\n return `# DCS Site Configuration\n# This file defines the site identity and Azure resource bindings.\n# Generated by: @duffcloudservices/cli\n\nsite_name: ${data.siteName}\nsite_slug: ${data.siteSlug}\n\n# Azure Static Web App resource ID\n# Set this after provisioning the SWA resource in Azure\nswa_resource_id: \"\"\n\n# URLs are populated after first deployment\nproduction_url: \"\"\npreview_url: \"\"\n\n# Google Analytics Configuration\ngoogle_analytics_id: \"\"\n\n# Azure Authentication (Managed Identity)\n# These values are used by GitHub Actions for OIDC authentication\nazure:\n client_id: \"\"\n tenant_id: \"\"\n subscription_id: \"\"\n\n# Portal API Configuration\nportal_api_url: \"https://portal.duffcloudservices.com\"\n\n# Site metadata for portal\nmetadata:\n framework: ${data.framework}\n managed_by: dcs\n created_at: \"${data.timestamp}\"\n`\n}\n\nfunction generatePagesYaml(data: { siteSlug: string; timestamp: string }): string {\n return `# .dcs/pages.yaml\n# Page registry for DCS CMS integration and snapshot capture.\n# Generated by: @duffcloudservices/cli\n#\n# This file defines page metadata only. Text content is stored in content.yaml\n# and text keys are auto-discovered during snapshot capture.\n\nversion: 3\nsiteSlug: ${data.siteSlug}\nlastUpdated: \"${data.timestamp}\"\ngeneratedBy: dcs-cli\n\npages:\n # Home page - always protected\n - slug: home\n path: /\n type: static\n title: Home\n deletable: false\n\n# Excluded paths - never captured\nexcluded:\n - /404\n - /dev-*\n - /_*\n\n# Snapshot configuration\nsnapshot:\n viewport:\n width: 1280\n height: 720\n waitAfterLoad: 3000\n captureFullPage: true\n`\n}\n\nfunction generateContentYaml(data: { timestamp: string }): string {\n return `# .dcs/content.yaml\n# Managed by DCS Portal - Do not edit manually\n# This file contains all text content for the site.\n#\n# Content is organized by page slug, with each page containing\n# key-value pairs for editable text. The \\`global\\` section contains\n# text shared across all pages (navigation, footer, etc.).\n\nversion: 1\nlastUpdated: \"${data.timestamp}\"\nupdatedBy: \"dcs-cli\"\n\n# Global text content (shared across all pages)\nglobal: {}\n\n# Page-specific text content\npages: {}\n`\n}\n\nfunction generateSeoYaml(data: { siteName: string; timestamp: string }): string {\n return `# .dcs/seo.yaml\n# Managed by DCS Portal - Do not edit manually\n# SEO configuration for all pages and global defaults.\n\nversion: 1\nlastUpdated: \"${data.timestamp}\"\nupdatedBy: \"dcs-cli\"\n\n# Global/site-wide SEO defaults\nglobal:\n siteName: \"${data.siteName}\"\n siteUrl: \"\" # Set your production URL\n locale: en_US\n defaultTitle: \"${data.siteName}\"\n defaultDescription: \"\"\n titleTemplate: \"%s | ${data.siteName}\"\n\n # Social media handles\n social:\n twitter: \"\"\n linkedin: \"\"\n\n # Default images for social sharing\n images:\n logo: \"\"\n ogDefault: \"\"\n twitterDefault: \"\"\n\n # Default robots directive\n robots: \"index, follow\"\n\n# Page-specific SEO configurations\npages:\n home:\n title: Home\n description: \"\"\n noTitleTemplate: true\n openGraph:\n type: website\n twitter:\n card: summary_large_image\n`\n}\n\nfunction generateSectionConventions(): string {\n return `# Section Conventions for DCS CMS Integration\n\nThis document describes the HTML attributes used by the DCS visual page editor\nfor section identification and snapshot capture.\n\n## Required Attributes\n\n### \\`data-dcs-section\\`\n\nMarks an element as an editable section. The value should match the section's\nidentifier in content.yaml.\n\n\\`\\`\\`html\n<section data-dcs-section=\"hero\">\n <!-- Section content -->\n</section>\n\\`\\`\\`\n\n### \\`data-dcs-text\\`\n\nMarks an element's text as editable via the portal. The value should be the\ntext key from content.yaml.\n\n\\`\\`\\`html\n<h1 data-dcs-text=\"hero.title\">{{ t('hero.title') }}</h1>\n\\`\\`\\`\n\n## Best Practices\n\n1. Use semantic section names that describe the content\n2. Keep text keys hierarchical (section.element)\n3. Ensure all user-editable text has a data-dcs-text attribute\n4. Don't nest sections unnecessarily\n`\n}\n\nfunction generateCopilotInstructions(data: { siteName: string }): string {\n return `# GitHub Copilot Instructions for ${data.siteName}\n\n## Project Overview\n\nThis is a **DCS-managed customer site**. Content and feature changes are managed\nthrough the DCS Portal at https://portal.duffcloudservices.com.\n\n## Technology Stack\n\n- Vue 3 + Composition API\n- VitePress (or Vite)\n- @duffcloudservices/cms for content management\n- Azure Static Web Apps for hosting\n\n## Content Management\n\n### Text Content\n\nUse the \\`useTextContent\\` composable for all editable text:\n\n\\`\\`\\`vue\n<script setup lang=\"ts\">\nimport { useTextContent } from '@duffcloudservices/cms'\n\nconst { t } = useTextContent({\n pageSlug: 'home',\n defaults: {\n 'hero.title': 'Default Title',\n 'hero.subtitle': 'Default subtitle'\n }\n})\n</script>\n\n<template>\n <h1 data-dcs-text=\"hero.title\">{{ t('hero.title') }}</h1>\n <p data-dcs-text=\"hero.subtitle\">{{ t('hero.subtitle') }}</p>\n</template>\n\\`\\`\\`\n\n### SEO\n\nUse the \\`useSEO\\` composable for page SEO:\n\n\\`\\`\\`vue\n<script setup lang=\"ts\">\nimport { useSEO } from '@duffcloudservices/cms'\n\nconst { applyHead } = useSEO('home')\napplyHead()\n</script>\n\\`\\`\\`\n\n## Key Conventions\n\n1. All user-editable text must use \\`useTextContent\\`\n2. All pages must have SEO configured via \\`useSEO\\`\n3. Section elements need \\`data-dcs-section\\` attributes\n4. Text elements need \\`data-dcs-text\\` attributes\n5. See \\`.dcs/SECTION-CONVENTIONS.md\\` for details\n`\n}\n\nfunction generatePlansReadme(data: { siteName: string; framework: string }): string {\n return `# DCS Integration Plans for ${data.siteName}\n\nThis directory contains step-by-step plans for integrating this site with the\nDCS content management system.\n\n## Overview\n\nThese plans guide AI assistants (like GitHub Copilot) through the integration\nprocess. Follow them in order for best results.\n\n## Plans\n\n1. **00-audit-site.md** - Analyze current site structure\n2. **01-create-use-text-content.md** - Set up the text content composable\n3. **02-integrate-home-page.md** - Add CMS integration to home page\n4. **03-integrate-remaining-pages.md** - Complete other pages\n5. **04-capture-snapshots.md** - Configure visual editor snapshots\n6. **05-verify-deployment.md** - Test and verify deployment\n\n## Framework\n\nThis site uses: **${data.framework}**\n\n## Getting Started\n\n1. Open plan 00 and ask Copilot to execute it\n2. Review the output and make any needed adjustments\n3. Continue with subsequent plans\n\n## Configuration Files\n\n- \\`.dcs/site.yaml\\` - Site identity and Azure config\n- \\`.dcs/pages.yaml\\` - Page registry for CMS\n- \\`.dcs/content.yaml\\` - Text content (managed by Portal)\n- \\`.dcs/seo.yaml\\` - SEO configuration (managed by Portal)\n`\n}\n\nfunction generateDeployWorkflow(): string {\n return siteDeployTemplate\n}\n\nfunction generatePlan00AuditSite(data: { framework: string }): string {\n return `# Plan 00: Audit Site Structure\n\n## Objective\n\nAnalyze the current site structure to understand what pages exist, their routes,\nand identify all text content that should be managed via DCS.\n\n## Framework\n\nThis site uses: **${data.framework}**\n\n## Tasks\n\n### 1. List All Pages\n\nIdentify all page components/routes in the project:\n\n- For Vue/VitePress: Look in \\`src/views/\\`, \\`src/pages/\\`, or \\`.vitepress/\\`\n- For Astro: Look in \\`src/pages/\\`\n\nCreate a list of:\n- Route path (e.g., \\`/\\`, \\`/about\\`, \\`/services\\`)\n- Component file path\n- Page title (from existing meta or content)\n\n### 2. Identify Text Content\n\nFor each page, list all user-facing text that should be editable:\n\n- Headings (h1, h2, etc.)\n- Paragraphs and body text\n- Button labels\n- Navigation links\n- Footer content\n\nUse this naming convention for text keys:\n\\`\\`\\`\n{pageSlug}.{section}.{element}\n\nExamples:\n- home.hero.title\n- home.hero.subtitle\n- home.cta.buttonText\n- about.intro.heading\n\\`\\`\\`\n\n### 3. Update pages.yaml\n\nAdd all discovered pages to \\`.dcs/pages.yaml\\`:\n\n\\`\\`\\`yaml\npages:\n - slug: home\n path: /\n type: static\n title: Home\n deletable: false\n \n - slug: about\n path: /about\n type: static\n title: About Us\n deletable: true\n\\`\\`\\`\n\n### 4. Document Findings\n\nCreate a brief summary of:\n- Total number of pages\n- Estimated number of text keys per page\n- Any complex sections (carousels, tabs, etc.)\n- Shared components that contain text (header, footer)\n\n## Output\n\nAfter completing this plan, you should have:\n1. Updated \\`.dcs/pages.yaml\\` with all pages\n2. A mental map of text content to migrate\n3. Understanding of component structure\n`\n}\n\nfunction generatePlan01IntegrateContent(): string {\n return `# Plan 01: Integrate Text Content\n\n## Objective\n\nSet up the \\`useTextContent\\` composable and migrate all static text to the\nDCS content management system.\n\n## Prerequisites\n\n- Completed Plan 00 (site audit)\n- \\`@duffcloudservices/cms\\` package installed\n\n## Tasks\n\n### 1. Install DCS CMS Package\n\n\\`\\`\\`bash\npnpm add @duffcloudservices/cms\n\\`\\`\\`\n\n### 2. Configure Vite Plugin\n\nAdd the DCS content plugin to your Vite config:\n\n\\`\\`\\`typescript\n// vite.config.ts\nimport { dcsContentPlugin } from '@duffcloudservices/cms/vite'\n\nexport default defineConfig({\n plugins: [\n vue(),\n dcsContentPlugin({\n contentPath: '.dcs/content.yaml',\n }),\n ],\n})\n\\`\\`\\`\n\n### 3. Integrate Home Page\n\nStart with the home page as a template:\n\n\\`\\`\\`vue\n<script setup lang=\"ts\">\nimport { useTextContent } from '@duffcloudservices/cms'\n\nconst { t } = useTextContent({\n pageSlug: 'home',\n defaults: {\n 'hero.title': 'Your Default Title',\n 'hero.subtitle': 'Your default subtitle text',\n 'hero.cta': 'Get Started',\n }\n})\n</script>\n\n<template>\n <section data-dcs-section=\"hero\">\n <h1 data-dcs-text=\"hero.title\">{{ t('hero.title') }}</h1>\n <p data-dcs-text=\"hero.subtitle\">{{ t('hero.subtitle') }}</p>\n <button data-dcs-text=\"hero.cta\">{{ t('hero.cta') }}</button>\n </section>\n</template>\n\\`\\`\\`\n\n### 4. Key Patterns\n\n**Text Keys**: Use hierarchical naming\n- \\`{section}.{element}\\` for page-specific content\n- Prefix with \\`global.\\` for shared content\n\n**Default Values**: Always provide defaults that match current content\n- This ensures the site works before CMS content is loaded\n- Defaults serve as fallback if content fetch fails\n\n**Data Attributes**: Required for visual editor\n- \\`data-dcs-section\\` on section containers\n- \\`data-dcs-text\\` on text elements (value = text key)\n\n### 5. Migrate Remaining Pages\n\nApply the same pattern to all pages identified in Plan 00.\n\nFor each page:\n1. Import \\`useTextContent\\`\n2. Define defaults for all text content\n3. Replace static text with \\`{{ t('key') }}\\`\n4. Add \\`data-dcs-text\\` attributes\n\n### 6. Handle Global Content\n\nCreate a shared composable for navigation/footer:\n\n\\`\\`\\`typescript\n// composables/useGlobalContent.ts\nimport { useTextContent } from '@duffcloudservices/cms'\n\nexport function useGlobalContent() {\n return useTextContent({\n pageSlug: 'global',\n defaults: {\n 'nav.home': 'Home',\n 'nav.about': 'About',\n 'footer.copyright': '© 2025 Company Name',\n }\n })\n}\n\\`\\`\\`\n\n## Verification\n\nAfter completing this plan:\n1. Run \\`pnpm dev\\` - site should display normally with defaults\n2. Check browser console for any missing text key warnings\n3. Verify all text elements have \\`data-dcs-text\\` attributes\n`\n}\n\nfunction generatePlan02ConfigureSeo(): string {\n return `# Plan 02: Configure SEO\n\n## Objective\n\nSet up the \\`useSEO\\` composable to manage page metadata through DCS Portal.\n\n## Prerequisites\n\n- Completed Plan 01 (content integration)\n- \\`@duffcloudservices/cms\\` package installed\n\n## Tasks\n\n### 1. Configure Vite Plugin\n\nAdd the DCS SEO plugin to your Vite config:\n\n\\`\\`\\`typescript\n// vite.config.ts\nimport { dcsContentPlugin, dcsSeoPlugin } from '@duffcloudservices/cms/vite'\n\nexport default defineConfig({\n plugins: [\n vue(),\n dcsContentPlugin({ contentPath: '.dcs/content.yaml' }),\n dcsSeoPlugin({ seoPath: '.dcs/seo.yaml' }),\n ],\n})\n\\`\\`\\`\n\n### 2. Add SEO to Pages\n\nFor each page, add the \\`useSEO\\` composable:\n\n\\`\\`\\`vue\n<script setup lang=\"ts\">\nimport { useTextContent, useSEO } from '@duffcloudservices/cms'\n\n// Content setup\nconst { t } = useTextContent({ pageSlug: 'home', defaults: { ... } })\n\n// SEO setup - applies head tags automatically\nconst { applyHead } = useSEO('home')\napplyHead()\n</script>\n\\`\\`\\`\n\n### 3. Configure Default SEO\n\nUpdate \\`.dcs/seo.yaml\\` with site-wide defaults:\n\n\\`\\`\\`yaml\nglobal:\n siteName: \"Your Site Name\"\n siteUrl: \"https://yoursite.com\"\n locale: en_US\n defaultTitle: \"Your Site Name\"\n defaultDescription: \"Your site description for search engines\"\n titleTemplate: \"%s | Your Site Name\"\n \n social:\n twitter: \"@yourhandle\"\n linkedin: \"company/yourcompany\"\n \n images:\n logo: \"/images/logo.png\"\n ogDefault: \"/images/og-default.png\"\n\npages:\n home:\n title: Home\n description: \"Welcome to our site\"\n noTitleTemplate: true # Home page often skips \" | Site Name\"\n openGraph:\n type: website\n twitter:\n card: summary_large_image\n \n about:\n title: About Us\n description: \"Learn more about our company\"\n\\`\\`\\`\n\n### 4. Page-Specific Overrides\n\nEach page can have custom SEO settings that override globals:\n\n\\`\\`\\`yaml\npages:\n services:\n title: Our Services\n description: \"Explore our professional services\"\n canonical: \"https://yoursite.com/services\"\n openGraph:\n title: \"Services - Custom OG Title\"\n description: \"Custom description for social sharing\"\n image: \"/images/services-og.png\"\n\\`\\`\\`\n\n### 5. Dynamic Pages (Blogs, etc.)\n\nFor dynamic pages like blog posts, you'll pass SEO data dynamically:\n\n\\`\\`\\`vue\n<script setup lang=\"ts\">\nimport { useSEO } from '@duffcloudservices/cms'\n\nconst props = defineProps<{ post: BlogPost }>()\n\nconst { applyHead } = useSEO('blog-post', {\n title: props.post.title,\n description: props.post.excerpt,\n openGraph: {\n type: 'article',\n image: props.post.featuredImage,\n }\n})\napplyHead()\n</script>\n\\`\\`\\`\n\n## Verification\n\nAfter completing this plan:\n1. View page source - check for meta tags\n2. Use browser dev tools to inspect \\`<head>\\`\n3. Test with social sharing debuggers (Facebook, Twitter, LinkedIn)\n4. Validate with Google's Rich Results Test\n`\n}\n","# Site Deployment Workflow\n# Generated by @duffcloudservices/cli\n# This workflow deploys the site to Azure Static Web Apps using OIDC authentication.\n\nname: Site Deployment\n\non:\n push:\n branches:\n - 'release/**'\n - 'master'\n workflow_dispatch:\n inputs:\n trigger_reason:\n description: 'Reason for manual deployment'\n required: false\n type: string\n default: 'Manual deployment'\n\n# Prevent concurrent deployments for the same branch\nconcurrency:\n group: site-deploy-${{ github.ref }}\n cancel-in-progress: false\n\npermissions:\n contents: read\n issues: write\n pull-requests: read\n id-token: write # Required for Azure OIDC authentication\n\njobs:\n deploy:\n name: Deploy to Azure Static Web App\n runs-on: ubuntu-latest\n # Skip deployment on branch creation events\n if: github.event.before != '0000000000000000000000000000000000000000'\n\n steps:\n - name: Determine deployment environment\n id: environment\n run: |\n BRANCH=\"${{ github.ref_name }}\"\n echo \"Branch: $BRANCH\"\n \n if [ \"$BRANCH\" == \"master\" ]; then\n echo \"environment=Production\" >> $GITHUB_OUTPUT\n echo \"environment_name=Production\" >> $GITHUB_OUTPUT\n echo \"swa_environment=\" >> $GITHUB_OUTPUT\n else\n echo \"environment=preview\" >> $GITHUB_OUTPUT\n echo \"environment_name=Preview\" >> $GITHUB_OUTPUT\n echo \"swa_environment=preview\" >> $GITHUB_OUTPUT\n fi\n\n - name: Checkout repository\n uses: actions/checkout@v4\n\n - name: Read site configuration\n id: config\n run: |\n CONFIG_FILE=\".dcs/site.yaml\"\n \n if [ ! -f \"$CONFIG_FILE\" ]; then\n echo \"::error::Missing DCS site configuration file: $CONFIG_FILE\"\n exit 1\n fi\n \n SITE_NAME=$(grep -E '^site_name:' \"$CONFIG_FILE\" | sed 's/site_name:\\s*//' | tr -d '\"' | tr -d \"'\")\n SITE_SLUG=$(grep -E '^site_slug:' \"$CONFIG_FILE\" | sed 's/site_slug:\\s*//' | tr -d '\"' | tr -d \"'\")\n SWA_RESOURCE_ID=$(grep -E '^swa_resource_id:' \"$CONFIG_FILE\" | sed 's/swa_resource_id:\\s*//' | tr -d '\"' | tr -d \"'\")\n PORTAL_API_URL=$(grep -E '^portal_api_url:' \"$CONFIG_FILE\" | sed 's/portal_api_url:\\s*//' | tr -d '\"' | tr -d \"'\")\n PORTAL_API_URL=\"${PORTAL_API_URL:-https://portal.duffcloudservices.com}\"\n GOOGLE_ANALYTICS_ID=$(grep -E '^google_analytics_id:' \"$CONFIG_FILE\" | sed 's/google_analytics_id:\\s*//' | tr -d '\"' | tr -d \"'\")\n PROD_APPINSIGHTS_CONNECTION_STRING=$(grep -E '^prod_appinsights_connection_string:' \"$CONFIG_FILE\" | sed 's/prod_appinsights_connection_string:\\s*//' | tr -d '\"' | tr -d \"'\" || true)\n DEV_APPINSIGHTS_CONNECTION_STRING=$(grep -E '^dev_appinsights_connection_string:' \"$CONFIG_FILE\" | sed 's/dev_appinsights_connection_string:\\s*//' | tr -d '\"' | tr -d \"'\" || true)\n \n # Azure Authentication\n AZURE_CLIENT_ID=$(grep -E '^\\s*client_id:' \"$CONFIG_FILE\" | sed 's/.*client_id:\\s*//' | tr -d '\"' | tr -d \"'\")\n AZURE_TENANT_ID=$(grep -E '^\\s*tenant_id:' \"$CONFIG_FILE\" | sed 's/.*tenant_id:\\s*//' | tr -d '\"' | tr -d \"'\")\n AZURE_SUBSCRIPTION_ID=$(grep -E '^\\s*subscription_id:' \"$CONFIG_FILE\" | sed 's/.*subscription_id:\\s*//' | tr -d '\"' | tr -d \"'\")\n \n # Build Configuration\n APP_LOCATION=$(grep -E '^\\s*app_location:' \"$CONFIG_FILE\" | sed 's/.*app_location:\\s*//' | tr -d '\"' | tr -d \"'\")\n OUTPUT_LOCATION=$(grep -E '^\\s*output_location:' \"$CONFIG_FILE\" | sed 's/.*output_location:\\s*//' | tr -d '\"' | tr -d \"'\")\n \n APP_LOCATION=\"${APP_LOCATION:-dist}\"\n OUTPUT_LOCATION=\"${OUTPUT_LOCATION:-.}\"\n\n if [ -z \"$SWA_RESOURCE_ID\" ]; then\n echo \"::error::Missing swa_resource_id in $CONFIG_FILE\"\n exit 1\n fi\n \n echo \"site_name=$SITE_NAME\" >> $GITHUB_OUTPUT\n echo \"site_slug=$SITE_SLUG\" >> $GITHUB_OUTPUT\n echo \"swa_resource_id=$SWA_RESOURCE_ID\" >> $GITHUB_OUTPUT\n echo \"app_location=$APP_LOCATION\" >> $GITHUB_OUTPUT\n echo \"output_location=$OUTPUT_LOCATION\" >> $GITHUB_OUTPUT\n echo \"azure_client_id=$AZURE_CLIENT_ID\" >> $GITHUB_OUTPUT\n echo \"azure_tenant_id=$AZURE_TENANT_ID\" >> $GITHUB_OUTPUT\n echo \"azure_subscription_id=$AZURE_SUBSCRIPTION_ID\" >> $GITHUB_OUTPUT\n echo \"portal_api_url=$PORTAL_API_URL\" >> $GITHUB_OUTPUT\n echo \"google_analytics_id=$GOOGLE_ANALYTICS_ID\" >> $GITHUB_OUTPUT\n echo \"prod_appinsights_connection_string=$PROD_APPINSIGHTS_CONNECTION_STRING\" >> $GITHUB_OUTPUT\n echo \"dev_appinsights_connection_string=$DEV_APPINSIGHTS_CONNECTION_STRING\" >> $GITHUB_OUTPUT\n\n - name: Azure login via OIDC\n uses: azure/login@v2\n with:\n client-id: ${{ steps.config.outputs.azure_client_id || vars.AZURE_CLIENT_ID }}\n tenant-id: ${{ steps.config.outputs.azure_tenant_id || vars.AZURE_TENANT_ID }}\n subscription-id: ${{ steps.config.outputs.azure_subscription_id || vars.AZURE_SUBSCRIPTION_ID }}\n\n - name: Get deployment tokens\n id: tokens\n run: |\n # Get access token for DCS API\n ACCESS_TOKEN=$(az account get-access-token --resource \"api://304711b9-8532-4659-beb7-c85726de9ae5\" --query accessToken -o tsv)\n \n SITE_NAME=\"${{ steps.config.outputs.site_name }}\"\n SITE_SLUG=\"${{ steps.config.outputs.site_slug }}\"\n PORTAL_API_URL=\"${{ steps.config.outputs.portal_api_url }}\"\n \n RESPONSE=$(curl -s -X POST \"${PORTAL_API_URL}/api/v1/sites/deployment-tokens\" \\\n -H \"Authorization: Bearer $ACCESS_TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d \"{\\\"siteName\\\": \\\"$SITE_NAME\\\", \\\"siteSlug\\\": \\\"$SITE_SLUG\\\"}\")\n \n SWA_TOKEN=$(echo $RESPONSE | jq -r .swaToken)\n \n if [ -z \"$SWA_TOKEN\" ] || [ \"$SWA_TOKEN\" == \"null\" ]; then\n echo \"::error::Failed to retrieve SWA token from DCS API\"\n echo \"Response: $RESPONSE\"\n exit 1\n fi\n \n echo \"::add-mask::$SWA_TOKEN\"\n echo \"swa_token=$SWA_TOKEN\" >> $GITHUB_OUTPUT\n\n - name: Setup Node.js\n uses: actions/setup-node@v4\n with:\n node-version: \"22\"\n\n - name: Install pnpm\n uses: pnpm/action-setup@v4\n\n - name: Setup pnpm cache\n uses: actions/cache@v4\n with:\n path: ~/.pnpm-store\n key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}\n restore-keys: |\n ${{ runner.os }}-pnpm-\n\n - name: Install dependencies\n run: pnpm install --frozen-lockfile\n\n - name: Create environment file\n run: |\n cat > .env << EOF\n VITE_APP_VERSION=\"${{ github.sha }}\"\n VITE_BACKEND_URI=\"${{ steps.config.outputs.portal_api_url }}\"\n VITE_SITE_SLUG=\"${{ steps.config.outputs.site_slug }}\"\n VITE_GOOGLE_ANALYTICS_ID=\"${{ steps.config.outputs.google_analytics_id }}\"\n VITE_PROD_APPINSIGHTS_CONNECTION_STRING=\"${{ steps.config.outputs.prod_appinsights_connection_string }}\"\n VITE_DEV_APPINSIGHTS_CONNECTION_STRING=\"${{ steps.config.outputs.dev_appinsights_connection_string }}\"\n EOF\n\n - name: Build site\n run: pnpm run build\n\n - name: Inject Google Analytics\n if: steps.config.outputs.google_analytics_id != ''\n run: |\n GA_ID=\"${{ steps.config.outputs.google_analytics_id }}\"\n OUTPUT_DIR=\"${{ steps.config.outputs.app_location }}\"\n echo \"Injecting Google Analytics tag ${GA_ID} into ${OUTPUT_DIR}...\"\n find \"$OUTPUT_DIR\" -name \"*.html\" -exec sed -i \"s|</head>|<script async src=\\\"https://www.googletagmanager.com/gtag/js?id=${GA_ID}\\\"></script><script>window.dataLayer=window.dataLayer\\|\\|[];function gtag(){dataLayer.push(arguments)}gtag('js',new Date());gtag('config','${GA_ID}')</script>\\n</head>|\" {} +\n INJECTED=$(grep -rl \"googletagmanager\" \"$OUTPUT_DIR\" --include=\"*.html\" | wc -l)\n echo \"Injected GA tag into ${INJECTED} HTML file(s)\"\n\n - name: Deploy to Azure Static Web Apps\n uses: Azure/static-web-apps-deploy@v1\n with:\n azure_static_web_apps_api_token: ${{ steps.tokens.outputs.swa_token }}\n repo_token: ${{ secrets.GITHUB_TOKEN }}\n action: \"upload\"\n app_location: ${{ steps.config.outputs.app_location }}\n output_location: ${{ steps.config.outputs.output_location }}\n skip_app_build: true\n deployment_environment: ${{ steps.environment.outputs.swa_environment != '' && steps.environment.outputs.swa_environment || '' }}\n","/**\n * Validate command - validate existing .dcs configuration files.\n */\n\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\nimport chalk from 'chalk'\nimport yaml from 'js-yaml'\n\nexport interface ValidateOptions {\n target?: string\n fix?: boolean\n verbose?: boolean\n}\n\ninterface ValidationResult {\n file: string\n valid: boolean\n errors: string[]\n warnings: string[]\n}\n\n/**\n * Validate .dcs configuration files for a customer site.\n */\nexport async function validateCommand(options: ValidateOptions): Promise<void> {\n const { target = '.', fix = false, verbose = false } = options\n\n const targetDir = path.resolve(target)\n const dcsDir = path.join(targetDir, '.dcs')\n\n console.log(chalk.bold('Validating DCS configuration...'))\n console.log(chalk.dim(`Directory: ${targetDir}`))\n console.log()\n\n // Check if .dcs directory exists\n try {\n await fs.access(dcsDir)\n } catch {\n console.log(chalk.red('No .dcs directory found.'))\n console.log('Run', chalk.cyan('dcs init'), 'to create DCS configuration.')\n process.exit(1)\n }\n\n const results: ValidationResult[] = []\n\n // Validate each config file\n results.push(await validateSiteYaml(dcsDir, verbose))\n results.push(await validatePagesYaml(dcsDir, verbose))\n results.push(await validateContentYaml(dcsDir, verbose))\n results.push(await validateSeoYaml(dcsDir, verbose))\n\n // Display results\n console.log(chalk.bold('Validation Results:'))\n console.log()\n\n let hasErrors = false\n let hasWarnings = false\n\n for (const result of results) {\n if (!result.valid || result.errors.length > 0) {\n hasErrors = true\n console.log(`${chalk.red('✗')} ${result.file}`)\n for (const error of result.errors) {\n console.log(` ${chalk.red('error:')} ${error}`)\n }\n } else if (result.warnings.length > 0) {\n hasWarnings = true\n console.log(`${chalk.yellow('!')} ${result.file}`)\n } else {\n console.log(`${chalk.green('✓')} ${result.file}`)\n }\n\n for (const warning of result.warnings) {\n console.log(` ${chalk.yellow('warning:')} ${warning}`)\n }\n }\n\n console.log()\n\n if (hasErrors) {\n console.log(chalk.red('Validation failed with errors.'))\n if (fix) {\n console.log(chalk.yellow('Some errors can be fixed automatically. Run with --fix flag.'))\n }\n process.exit(1)\n } else if (hasWarnings) {\n console.log(chalk.yellow('Validation passed with warnings.'))\n } else {\n console.log(chalk.green('All configuration files are valid.'))\n }\n}\n\n// =============================================================================\n// File Validators\n// =============================================================================\n\nasync function validateSiteYaml(dcsDir: string, verbose: boolean): Promise<ValidationResult> {\n const filePath = path.join(dcsDir, 'site.yaml')\n const result: ValidationResult = {\n file: '.dcs/site.yaml',\n valid: true,\n errors: [],\n warnings: [],\n }\n\n try {\n const content = await fs.readFile(filePath, 'utf8')\n const config = yaml.load(content) as Record<string, unknown>\n\n // Required fields\n if (!config.site_name) {\n result.errors.push('Missing required field: site_name')\n result.valid = false\n }\n\n if (!config.site_slug) {\n result.errors.push('Missing required field: site_slug')\n result.valid = false\n } else if (!/^[a-z0-9-]+$/.test(config.site_slug as string)) {\n result.errors.push('site_slug must be lowercase alphanumeric with hyphens only')\n result.valid = false\n }\n\n // Warnings for empty optional fields\n if (!config.swa_resource_id) {\n result.warnings.push('swa_resource_id is not set (required for deployment)')\n }\n\n if (!config.production_url) {\n result.warnings.push('production_url is not set')\n }\n\n if (verbose) {\n console.log(chalk.dim(` site_name: ${config.site_name}`))\n console.log(chalk.dim(` site_slug: ${config.site_slug}`))\n }\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n result.errors.push('File not found')\n } else {\n result.errors.push(`Parse error: ${(error as Error).message}`)\n }\n result.valid = false\n }\n\n return result\n}\n\nasync function validatePagesYaml(dcsDir: string, verbose: boolean): Promise<ValidationResult> {\n const filePath = path.join(dcsDir, 'pages.yaml')\n const result: ValidationResult = {\n file: '.dcs/pages.yaml',\n valid: true,\n errors: [],\n warnings: [],\n }\n\n try {\n const content = await fs.readFile(filePath, 'utf8')\n const config = yaml.load(content) as Record<string, unknown>\n\n // Required fields\n if (config.version === undefined) {\n result.errors.push('Missing required field: version')\n result.valid = false\n }\n\n if (!config.siteSlug) {\n result.errors.push('Missing required field: siteSlug')\n result.valid = false\n }\n\n // Validate pages array\n const pages = config.pages as Array<Record<string, unknown>> | undefined\n if (!pages || !Array.isArray(pages)) {\n result.errors.push('Missing or invalid pages array')\n result.valid = false\n } else {\n // Check for home page\n const hasHome = pages.some((p) => p.slug === 'home')\n if (!hasHome) {\n result.warnings.push('No home page defined (slug: home)')\n }\n\n // Validate each page\n for (let i = 0; i < pages.length; i++) {\n const page = pages[i]\n if (!page.slug) {\n result.errors.push(`Page ${i}: missing slug`)\n result.valid = false\n }\n if (!page.path) {\n result.errors.push(`Page ${i}: missing path`)\n result.valid = false\n }\n if (!page.type) {\n result.warnings.push(`Page '${page.slug}': missing type (defaulting to static)`)\n }\n }\n\n if (verbose) {\n console.log(chalk.dim(` ${pages.length} pages defined`))\n }\n }\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n result.errors.push('File not found')\n } else {\n result.errors.push(`Parse error: ${(error as Error).message}`)\n }\n result.valid = false\n }\n\n return result\n}\n\nasync function validateContentYaml(dcsDir: string, verbose: boolean): Promise<ValidationResult> {\n const filePath = path.join(dcsDir, 'content.yaml')\n const result: ValidationResult = {\n file: '.dcs/content.yaml',\n valid: true,\n errors: [],\n warnings: [],\n }\n\n try {\n const content = await fs.readFile(filePath, 'utf8')\n const config = yaml.load(content) as Record<string, unknown>\n\n // Required fields\n if (config.version === undefined) {\n result.errors.push('Missing required field: version')\n result.valid = false\n }\n\n // Validate structure\n if (config.global !== undefined && typeof config.global !== 'object') {\n result.errors.push('global must be an object')\n result.valid = false\n }\n\n if (config.pages !== undefined && typeof config.pages !== 'object') {\n result.errors.push('pages must be an object')\n result.valid = false\n }\n\n if (verbose) {\n const globalKeys = config.global ? Object.keys(config.global as object).length : 0\n const pageCount = config.pages ? Object.keys(config.pages as object).length : 0\n console.log(chalk.dim(` ${globalKeys} global keys, ${pageCount} page sections`))\n }\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n result.errors.push('File not found')\n } else {\n result.errors.push(`Parse error: ${(error as Error).message}`)\n }\n result.valid = false\n }\n\n return result\n}\n\nasync function validateSeoYaml(dcsDir: string, verbose: boolean): Promise<ValidationResult> {\n const filePath = path.join(dcsDir, 'seo.yaml')\n const result: ValidationResult = {\n file: '.dcs/seo.yaml',\n valid: true,\n errors: [],\n warnings: [],\n }\n\n try {\n const content = await fs.readFile(filePath, 'utf8')\n const config = yaml.load(content) as Record<string, unknown>\n\n // Required fields\n if (config.version === undefined) {\n result.errors.push('Missing required field: version')\n result.valid = false\n }\n\n // Validate global section\n const global = config.global as Record<string, unknown> | undefined\n if (!global) {\n result.errors.push('Missing required section: global')\n result.valid = false\n } else {\n if (!global.siteName) {\n result.warnings.push('global.siteName is not set')\n }\n if (!global.siteUrl) {\n result.warnings.push('global.siteUrl is not set (required for SEO)')\n }\n if (!global.defaultDescription) {\n result.warnings.push('global.defaultDescription is not set')\n }\n }\n\n // Validate pages section\n const pages = config.pages as Record<string, unknown> | undefined\n if (pages && typeof pages === 'object') {\n const pageKeys = Object.keys(pages)\n if (!pageKeys.includes('home')) {\n result.warnings.push('No SEO configuration for home page')\n }\n\n if (verbose) {\n console.log(chalk.dim(` ${pageKeys.length} page SEO configs`))\n }\n }\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n result.errors.push('File not found')\n } else {\n result.errors.push(`Parse error: ${(error as Error).message}`)\n }\n result.valid = false\n }\n\n return result\n}\n","/**\n * Plans command - generate/regenerate Copilot prompt plans for DCS integration.\n */\n\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\nimport chalk from 'chalk'\nimport yaml from 'js-yaml'\n\nexport interface PlansOptions {\n target?: string\n force?: boolean\n}\n\n/**\n * Generate DCS integration plan files for AI-assisted onboarding.\n */\nexport async function plansCommand(options: PlansOptions): Promise<void> {\n const { target = '.', force = false } = options\n\n const targetDir = path.resolve(target)\n const dcsDir = path.join(targetDir, '.dcs')\n const plansDir = path.join(targetDir, '.plans')\n\n console.log(chalk.bold('Generating DCS integration plans...'))\n console.log(chalk.dim(`Directory: ${targetDir}`))\n console.log()\n\n // Check if .dcs directory exists\n try {\n await fs.access(dcsDir)\n } catch {\n console.log(chalk.red('No .dcs directory found.'))\n console.log('Run', chalk.cyan('dcs init'), 'first to create DCS configuration.')\n process.exit(1)\n }\n\n // Read site config for context\n let siteName = 'Customer Site'\n let framework = 'vue'\n try {\n const siteYaml = await fs.readFile(path.join(dcsDir, 'site.yaml'), 'utf8')\n const siteConfig = yaml.load(siteYaml) as Record<string, unknown>\n siteName = (siteConfig.site_name as string) || siteName\n const metadata = siteConfig.metadata as Record<string, unknown> | undefined\n framework = (metadata?.framework as string) || framework\n } catch {\n console.log(chalk.yellow('Warning: Could not read site.yaml'))\n }\n\n // Generate plan files\n const plans: Array<{ name: string; content: string }> = [\n { name: '00-audit-site.md', content: generateAuditPlan(siteName) },\n { name: '01-create-use-text-content.md', content: generateTextContentPlan(siteName, framework) },\n { name: '02-integrate-home-page.md', content: generateHomePagePlan(siteName) },\n { name: '03-integrate-remaining-pages.md', content: generateRemainingPagesPlan(siteName) },\n { name: '04-capture-snapshots.md', content: generateSnapshotsPlan(siteName) },\n { name: '05-verify-deployment.md', content: generateVerifyPlan(siteName) },\n ]\n\n // Create plans directory\n await fs.mkdir(plansDir, { recursive: true })\n\n // Write plan files\n const generatedFiles: string[] = []\n for (const plan of plans) {\n const filePath = path.join(plansDir, plan.name)\n\n // Check if file exists\n try {\n await fs.access(filePath)\n if (!force) {\n console.log(chalk.yellow(`Skipped (exists): .plans/${plan.name}`))\n continue\n }\n } catch {\n // File doesn't exist, continue\n }\n\n await fs.writeFile(filePath, plan.content, 'utf8')\n generatedFiles.push(plan.name)\n console.log(`${chalk.green('✓')} .plans/${plan.name}`)\n }\n\n console.log()\n if (generatedFiles.length > 0) {\n console.log(chalk.green(`Generated ${generatedFiles.length} plan files.`))\n } else {\n console.log(chalk.yellow('No new files generated. Use --force to overwrite existing files.'))\n }\n}\n\n// =============================================================================\n// Plan Templates\n// =============================================================================\n\nfunction generateAuditPlan(siteName: string): string {\n return `# Plan 00: Audit ${siteName} Site Structure\n\n## Objective\n\nAnalyze the current site structure to understand:\n- What pages exist and their routing structure\n- Current component organization\n- Existing content patterns\n- Build/deploy configuration\n\n## Steps\n\n### Step 1: List All Pages\n\nScan the project for page components:\n\n\\`\\`\\`\n# For VitePress\nls docs/**/*.md\n\n# For Vue/Vite\nls src/pages/**/*.vue\nls src/views/**/*.vue\n\\`\\`\\`\n\n### Step 2: Identify Content Patterns\n\nLook for:\n- Hardcoded text strings in templates\n- Existing i18n or content management\n- Props-based content passing\n- API-driven content\n\n### Step 3: Document Current Structure\n\nCreate a list of:\n1. All page routes\n2. Reusable components with text content\n3. Layout components (header, footer)\n4. Any existing SEO implementation\n\n### Step 4: Update pages.yaml\n\nUpdate \\`.dcs/pages.yaml\\` with all discovered pages:\n\n\\`\\`\\`yaml\npages:\n - slug: home\n path: /\n type: static\n title: Home\n deletable: false\n # Add discovered pages here...\n\\`\\`\\`\n\n## Output\n\n- Updated \\`.dcs/pages.yaml\\` with all pages\n- List of components requiring content integration\n- Notes on any existing content management to migrate\n`\n}\n\nfunction generateTextContentPlan(siteName: string, framework: string): string {\n return `# Plan 01: Create useTextContent Composable for ${siteName}\n\n## Objective\n\nSet up the text content composable for runtime/build-time content management.\n\n## Prerequisites\n\n- \\`@duffcloudservices/cms\\` package installed\n- Vite/VitePress project configured\n\n## Steps\n\n### Step 1: Install CMS Package\n\n\\`\\`\\`bash\npnpm add @duffcloudservices/cms\n\\`\\`\\`\n\n### Step 2: Configure Vite Plugin\n\n${framework === 'vue' ? `\nIn \\`vite.config.ts\\`:\n\n\\`\\`\\`typescript\nimport { defineConfig } from 'vite'\nimport vue from '@vitejs/plugin-vue'\nimport { dcsContentPlugin } from '@duffcloudservices/cms/plugins'\n\nexport default defineConfig({\n plugins: [\n vue(),\n dcsContentPlugin({ path: '.dcs/content.yaml' }),\n ],\n})\n\\`\\`\\`\n` : `\nIn \\`.vitepress/config.ts\\`:\n\n\\`\\`\\`typescript\nimport { defineConfig } from 'vitepress'\nimport { dcsContentPlugin } from '@duffcloudservices/cms/plugins'\n\nexport default defineConfig({\n vite: {\n plugins: [\n dcsContentPlugin({ path: '.dcs/content.yaml' }),\n ],\n },\n})\n\\`\\`\\`\n`}\n\n### Step 3: Create Site Composable Wrapper (Optional)\n\nCreate a site-specific wrapper for consistent defaults:\n\n\\`\\`\\`typescript\n// src/composables/useContent.ts\nimport { useTextContent as baseuseTextContent } from '@duffcloudservices/cms'\n\nexport function useContent(pageSlug: string, defaults: Record<string, string> = {}) {\n return baseuseTextContent({ pageSlug, defaults })\n}\n\\`\\`\\`\n\n## Verification\n\n1. Run \\`pnpm dev\\` - should start without errors\n2. Import and use composable in a test component\n3. Verify content.yaml values are accessible via \\`t()\\` function\n`\n}\n\nfunction generateHomePagePlan(siteName: string): string {\n return `# Plan 02: Integrate Home Page for ${siteName}\n\n## Objective\n\nAdd DCS CMS integration to the home page, including:\n- Text content management\n- SEO configuration\n- Data attributes for visual editor\n\n## Steps\n\n### Step 1: Identify Home Page Component\n\nFind the main home page component:\n- VitePress: \\`docs/index.md\\` or \\`docs/.vitepress/theme/components/Home.vue\\`\n- Vue: \\`src/pages/index.vue\\` or \\`src/views/Home.vue\\`\n\n### Step 2: Add Text Content Integration\n\nReplace hardcoded text with \\`t()\\` function calls:\n\n\\`\\`\\`vue\n<script setup lang=\"ts\">\nimport { useTextContent, useSEO } from '@duffcloudservices/cms'\n\nconst { t } = useTextContent({\n pageSlug: 'home',\n defaults: {\n 'hero.title': 'Welcome to ${siteName}',\n 'hero.subtitle': 'Your subtitle here',\n 'hero.cta': 'Get Started',\n // Add all text content...\n }\n})\n\n// Apply SEO\nconst { applyHead } = useSEO('home')\napplyHead()\n</script>\n\n<template>\n <section data-dcs-section=\"hero\">\n <h1 data-dcs-text=\"hero.title\">{{ t('hero.title') }}</h1>\n <p data-dcs-text=\"hero.subtitle\">{{ t('hero.subtitle') }}</p>\n <button data-dcs-text=\"hero.cta\">{{ t('hero.cta') }}</button>\n </section>\n</template>\n\\`\\`\\`\n\n### Step 3: Update content.yaml\n\nAdd all discovered text keys to \\`.dcs/content.yaml\\`:\n\n\\`\\`\\`yaml\npages:\n home:\n hero.title: \"Welcome to ${siteName}\"\n hero.subtitle: \"Your subtitle here\"\n hero.cta: \"Get Started\"\n\\`\\`\\`\n\n### Step 4: Update seo.yaml\n\nConfigure SEO for the home page in \\`.dcs/seo.yaml\\`:\n\n\\`\\`\\`yaml\npages:\n home:\n title: Home\n description: \"Description for ${siteName}\"\n noTitleTemplate: true\n openGraph:\n type: website\n\\`\\`\\`\n\n## Verification\n\n1. Run \\`pnpm dev\\` and verify home page renders correctly\n2. Check browser console for any errors\n3. Verify SEO meta tags in page source\n4. Test that data-dcs-* attributes are present\n`\n}\n\nfunction generateRemainingPagesPlan(siteName: string): string {\n return `# Plan 03: Integrate Remaining Pages for ${siteName}\n\n## Objective\n\nApply the same integration pattern to all remaining pages identified in the audit.\n\n## Steps\n\n### For Each Page\n\n1. **Open the page component**\n\n2. **Add imports and composable setup**:\n\n\\`\\`\\`vue\n<script setup lang=\"ts\">\nimport { useTextContent, useSEO } from '@duffcloudservices/cms'\n\nconst { t } = useTextContent({\n pageSlug: 'PAGE_SLUG', // Match pages.yaml slug\n defaults: {\n // Add all text content with sensible defaults\n }\n})\n\nconst { applyHead } = useSEO('PAGE_SLUG')\napplyHead()\n</script>\n\\`\\`\\`\n\n3. **Replace all hardcoded text** with \\`t()\\` calls\n\n4. **Add data-dcs-* attributes** to sections and text elements\n\n5. **Update content.yaml** with the page's text content\n\n6. **Update seo.yaml** with SEO configuration\n\n### Global Components\n\nDon't forget shared components like:\n- Header/Navigation\n- Footer\n- Sidebar\n\nThese should use \\`pageSlug: 'global'\\` for shared content.\n\n## Checklist\n\n- [ ] About page\n- [ ] Contact page\n- [ ] Services/Products pages\n- [ ] Blog index (if applicable)\n- [ ] Header component\n- [ ] Footer component\n- [ ] Any other pages...\n\n## Verification\n\nFor each page:\n1. Page renders correctly\n2. No console errors\n3. SEO meta tags present\n4. Data attributes present\n`\n}\n\nfunction generateSnapshotsPlan(siteName: string): string {\n return `# Plan 04: Configure Snapshot Capture for ${siteName}\n\n## Objective\n\nEnsure the visual page editor can capture accurate snapshots of all pages.\n\n## Prerequisites\n\n- All pages integrated with \\`useTextContent\\`\n- All pages have \\`data-dcs-section\\` attributes\n- All editable text has \\`data-dcs-text\\` attributes\n\n## Steps\n\n### Step 1: Verify pages.yaml\n\nEnsure all pages are listed in \\`.dcs/pages.yaml\\`:\n\n\\`\\`\\`yaml\npages:\n - slug: home\n path: /\n type: static\n title: Home\n deletable: false\n # All other pages...\n\nexcluded:\n - /404\n - /dev-*\n - /_*\n\\`\\`\\`\n\n### Step 2: Configure Snapshot Settings\n\nReview snapshot configuration:\n\n\\`\\`\\`yaml\nsnapshot:\n viewport:\n width: 1280\n height: 720\n waitAfterLoad: 3000 # Adjust if page has animations\n captureFullPage: true\n\\`\\`\\`\n\n### Step 3: Test Local Snapshots\n\nBuild and preview the site:\n\n\\`\\`\\`bash\npnpm build\npnpm preview\n\\`\\`\\`\n\nManually verify each page:\n1. Opens correctly\n2. All content is visible\n3. No loading spinners or placeholders\n\n### Step 4: Verify Data Attributes\n\nUse browser DevTools to check:\n- Each section has \\`data-dcs-section\\`\n- Each editable text element has \\`data-dcs-text\\`\n- Attribute values match content.yaml keys\n\n## Verification\n\n1. Site builds without errors\n2. All pages accessible at documented paths\n3. Data attributes present in rendered HTML\n4. No dynamic content that would prevent snapshot capture\n`\n}\n\nfunction generateVerifyPlan(siteName: string): string {\n return `# Plan 05: Verify Deployment for ${siteName}\n\n## Objective\n\nTest the complete integration before handing off to the DCS Portal.\n\n## Steps\n\n### Step 1: Run Validation\n\n\\`\\`\\`bash\nnpx @duffcloudservices/cli validate\n\\`\\`\\`\n\nFix any reported errors or warnings.\n\n### Step 2: Build Test\n\n\\`\\`\\`bash\npnpm build\n\\`\\`\\`\n\nEnsure no build errors related to:\n- Missing content keys\n- SEO configuration\n- Vite plugin issues\n\n### Step 3: Local Preview\n\n\\`\\`\\`bash\npnpm preview\n\\`\\`\\`\n\nVerify:\n- [ ] Home page loads correctly\n- [ ] All pages accessible\n- [ ] SEO meta tags correct\n- [ ] No console errors\n- [ ] Content displays correctly\n\n### Step 4: Deploy to Staging\n\nDeploy to Azure Static Web Apps staging environment:\n\n\\`\\`\\`bash\n# If using GitHub Actions, push to a PR branch\ngit push origin feature/dcs-integration\n\\`\\`\\`\n\n### Step 5: Portal Registration\n\n1. Log in to DCS Portal\n2. Navigate to Sites > Add Site\n3. Enter site slug and GitHub repo\n4. Trigger initial snapshot capture\n\n### Step 6: Visual Editor Test\n\n1. Open the site in DCS Portal\n2. Navigate to Pages\n3. Select a page\n4. Verify visual editor loads correctly\n5. Test editing a text field\n6. Save and verify changes deploy\n\n## Success Criteria\n\n- [ ] All validation passes\n- [ ] Site builds and deploys\n- [ ] Portal can capture snapshots\n- [ ] Visual editor works\n- [ ] Content changes deploy correctly\n\n## Handoff\n\nOnce verified:\n1. Merge the integration PR\n2. Notify the site owner\n3. Provide portal access credentials\n`\n}\n","/**\n * Capture visual snapshots of customer site pages for the portal's visual page editor.\n *\n * This command:\n * 1. Reads page configuration from .dcs/pages.yaml\n * 2. Launches a headless browser via Playwright\n * 3. Captures full-page screenshots and section bounds\n * 4. Detects text keys from data-text-key attributes\n * 5. Outputs JSON manifests and PNG screenshots\n *\n * The output is consumed by the DCS portal to render the visual page editor.\n */\n\nimport fs from 'fs'\nimport path from 'path'\nimport yaml from 'js-yaml'\nimport chalk from 'chalk'\nimport ora from 'ora'\nimport type { Browser, ElementHandle, Page } from 'playwright'\n\n// =============================================================================\n// Types\n// =============================================================================\n\ninterface PagesConfig {\n siteSlug: string\n version: number | string\n pages: PageConfig[]\n snapshot: {\n viewport: { width: number; height: number }\n waitAfterLoad: number\n captureFullPage: boolean\n outputDir?: string\n }\n}\n\ninterface PageConfig {\n slug: string\n path: string\n type: string\n title: string\n deletable: boolean\n textKeys?: string[]\n /** For dynamic pages: template pattern like /topics/:topic */\n pathTemplate?: string\n /** For dynamic pages: list of actual slugs to capture */\n instances?: string[]\n}\n\ninterface ContentConfig {\n global?: Record<string, unknown>\n pages?: Record<string, Record<string, unknown>>\n}\n\ninterface TextKeyInfo {\n key: string\n currentValue: string\n elementType: string\n bounds: {\n x: number\n y: number\n width: number\n height: number\n }\n domPath?: string\n}\n\ninterface SectionInfo {\n sectionId: string\n sectionType: 'header' | 'footer' | 'hero' | 'content' | 'gallery' | 'cta' | 'custom'\n displayName: string\n domPath: string\n bounds: {\n x: number\n y: number\n width: number\n height: number\n }\n textKeys: TextKeyInfo[]\n isEditable: boolean\n isStructuralOnly: boolean\n}\n\ninterface PageSnapshot {\n pageSlug: string\n path: string\n type: string\n deletable: boolean\n title: string\n capturedAt: string\n previewUrl: string\n viewport: { width: number; height: number }\n pageHeight: number\n sections: SectionInfo[]\n}\n\ninterface SnapshotManifest {\n siteSlug: string\n capturedAt: string\n capturedVersion?: string\n deploymentRef?: string\n pagesConfigVersion: number | string\n pages: Array<{\n pageSlug: string\n pagePath: string\n pageType: string\n deletable: boolean\n title: string\n capturedAt: string\n sectionCount: number\n hasSnapshot: boolean\n }>\n}\n\ninterface TargetedSnapshotsConfig {\n mode: 'skip' | 'targeted' | 'full'\n pages: string[]\n reason: string\n triggeredBy?: string\n}\n\ninterface CaptureSnapshotsOptions {\n target: string\n baseUrl: string\n dryRun?: boolean\n verbose?: boolean\n pages?: string[]\n}\n\nconst CARD_THUMBNAIL_ASPECT_RATIO = 16 / 9\nconst ASSET_WAIT_TIMEOUT_MS = 5000\n\nasync function disableCaptureMotion(page: Page): Promise<void> {\n await page.addStyleTag({\n content: `\n *, *::before, *::after {\n animation-delay: -1ms !important;\n animation-duration: 1ms !important;\n animation-iteration-count: 1 !important;\n scroll-behavior: auto !important;\n transition-delay: 0s !important;\n transition-duration: 0s !important;\n }\n `,\n }).catch(() => {\n // Some pages can navigate before style injection completes; capture can continue without this stabilization.\n })\n}\n\nasync function waitForAboveFoldAssets(page: Page): Promise<void> {\n await page.evaluate(async (timeoutMs) => {\n const waitForFonts = typeof document.fonts?.ready?.then === 'function'\n ? document.fonts.ready.catch(() => undefined)\n : Promise.resolve()\n\n const visibleImages = Array.from(document.images).filter((img) => {\n const rect = img.getBoundingClientRect()\n return rect.bottom >= -100 && rect.top <= window.innerHeight * 1.5\n })\n\n const waitForImages = Promise.all(visibleImages.map((img) => {\n if (img.complete && img.naturalWidth > 0) {\n return Promise.resolve()\n }\n return new Promise<void>((resolve) => {\n const done = () => resolve()\n img.addEventListener('load', done, { once: true })\n img.addEventListener('error', done, { once: true })\n })\n }))\n\n await Promise.race([\n Promise.all([waitForFonts, waitForImages]),\n new Promise((resolve) => window.setTimeout(resolve, timeoutMs)),\n ])\n }, ASSET_WAIT_TIMEOUT_MS).catch((error) => {\n console.warn(` Warning: could not verify above-fold assets: ${error instanceof Error ? error.message : String(error)}`)\n })\n}\n\nasync function lazyLoadPage(page: Page, viewportHeight: number): Promise<number> {\n let pageHeight = await page.evaluate(() => document.documentElement.scrollHeight)\n const scrollStep = Math.max(viewportHeight, 1)\n\n for (let scrollY = 0; scrollY < pageHeight; scrollY += scrollStep) {\n await page.evaluate((y) => window.scrollTo(0, y), scrollY)\n await page.waitForTimeout(100)\n }\n\n await page.waitForTimeout(200)\n pageHeight = await page.evaluate(() => document.documentElement.scrollHeight)\n\n return pageHeight\n}\n\nasync function resetScrollForFullPageCapture(page: Page): Promise<void> {\n await page.evaluate(() => {\n window.scrollTo(0, 0)\n window.dispatchEvent(new Event('scroll'))\n })\n await page.waitForFunction(() => Math.abs(window.scrollY) < 1, undefined, { timeout: 1000 }).catch(() => undefined)\n await page.evaluate(() => new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve)))).catch(() => undefined)\n await page.waitForTimeout(350)\n await waitForAboveFoldAssets(page)\n}\n\nasync function withFullPageCaptureLayout<T>(page: Page, capture: () => Promise<T>): Promise<T> {\n const styleHandle = await page.addStyleTag({\n content: `\n html[data-dcs-full-page-snapshot=\"true\"] header[data-section=\"header\"],\n html[data-dcs-full-page-snapshot=\"true\"] [data-section-type=\"header\"],\n html[data-dcs-full-page-snapshot=\"true\"] [role=\"banner\"] {\n position: static !important;\n transform: none !important;\n }\n\n html[data-dcs-full-page-snapshot=\"true\"] [data-dcs-pin-full-page-nav=\"true\"] {\n position: absolute !important;\n top: 0 !important;\n right: 0 !important;\n bottom: auto !important;\n left: 0 !important;\n transform: none !important;\n }\n\n html[data-dcs-full-page-snapshot=\"true\"] .VPNavBar {\n transform: none !important;\n }\n `,\n }).catch(() => null)\n\n await page.evaluate(() => {\n const candidates = Array.from(document.querySelectorAll(\n 'header[data-section=\"header\"] > nav, [data-section-type=\"header\"] > nav, [role=\"banner\"] > nav, .VPNav'\n ))\n candidates.forEach((element) => {\n const style = window.getComputedStyle(element)\n const className = element instanceof HTMLElement && typeof element.className === 'string' ? element.className : ''\n if (style.position === 'fixed' || style.position === 'sticky' || /\\b(fixed|sticky)\\b/.test(className) || element.classList.contains('VPNav')) {\n element.setAttribute('data-dcs-pin-full-page-nav', 'true')\n }\n })\n document.documentElement.setAttribute('data-dcs-full-page-snapshot', 'true')\n window.scrollTo(0, 0)\n window.dispatchEvent(new Event('scroll'))\n })\n await page.evaluate(() => new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve)))).catch(() => undefined)\n\n try {\n return await capture()\n } finally {\n await styleHandle?.evaluate((el) => el.parentNode?.removeChild(el)).catch(() => undefined)\n await page.evaluate(() => {\n document.documentElement.removeAttribute('data-dcs-full-page-snapshot')\n document.querySelectorAll('[data-dcs-pin-full-page-nav]').forEach((element) => {\n element.removeAttribute('data-dcs-pin-full-page-nav')\n })\n }).catch(() => undefined)\n }\n}\n\nasync function captureHeaderThumbnail(\n page: Page,\n outputPath: string,\n viewport: { width: number; height: number }\n): Promise<void> {\n await page.setViewportSize(viewport)\n await page.evaluate(() => window.scrollTo(0, 0))\n await page.waitForTimeout(200)\n await waitForAboveFoldAssets(page)\n\n const clip = await page.evaluate((aspectRatio) => {\n const width = Math.max(document.documentElement.clientWidth, window.innerWidth, 1)\n const pageHeight = Math.max(document.documentElement.scrollHeight, document.body?.scrollHeight ?? 0, 1)\n const height = Math.min(Math.round(width / aspectRatio), pageHeight)\n return { x: 0, y: 0, width, height }\n }, CARD_THUMBNAIL_ASPECT_RATIO)\n\n await page.screenshot({\n path: outputPath,\n clip,\n })\n}\n\n// =============================================================================\n// Section Detection Selectors\n// =============================================================================\n\nconst SECTION_SELECTORS = {\n explicit: '[data-section]',\n header: 'header, [role=\"banner\"], .header, #header',\n footer: 'footer, [role=\"contentinfo\"], .footer, #footer',\n hero: '[class*=\"hero\"], [data-section-type=\"hero\"], .hero-section, #hero',\n content: 'main, [role=\"main\"], article, .content, .main-content',\n gallery: '[class*=\"gallery\"], [data-section-type=\"gallery\"]',\n cta: '[class*=\"cta\"], [data-section-type=\"cta\"], .call-to-action',\n} as const\n\n// =============================================================================\n// Configuration Loaders\n// =============================================================================\n\nfunction loadPagesConfig(targetDir: string): PagesConfig {\n const configPath = path.join(targetDir, '.dcs', 'pages.yaml')\n if (!fs.existsSync(configPath)) {\n throw new Error(`Configuration file not found: ${configPath}`)\n }\n const content = fs.readFileSync(configPath, 'utf-8')\n return yaml.load(content) as PagesConfig\n}\n\nfunction loadContentConfig(targetDir: string): ContentConfig | null {\n const contentPath = path.join(targetDir, '.dcs', 'content.yaml')\n if (!fs.existsSync(contentPath)) {\n return null\n }\n const content = fs.readFileSync(contentPath, 'utf-8')\n return yaml.load(content) as ContentConfig\n}\n\nfunction loadTargetedSnapshotsConfig(targetDir: string): TargetedSnapshotsConfig | null {\n const targetedPath = path.join(targetDir, '.dcs', 'targeted-snapshots.yaml')\n if (!fs.existsSync(targetedPath)) {\n return null\n }\n const content = fs.readFileSync(targetedPath, 'utf-8')\n return yaml.load(content) as TargetedSnapshotsConfig\n}\n\nfunction getValidTextKeysForPage(\n contentConfig: ContentConfig | null,\n pageSlug: string\n): Map<string, string> {\n const keys = new Map<string, string>()\n if (!contentConfig) return keys\n\n // Add global keys\n if (contentConfig.global) {\n for (const [key, value] of Object.entries(contentConfig.global)) {\n keys.set(key, typeof value === 'string' ? value : JSON.stringify(value))\n }\n }\n\n // Add page-specific keys\n const pageContent = contentConfig.pages?.[pageSlug]\n if (pageContent) {\n for (const [key, value] of Object.entries(pageContent)) {\n const fullKey = `${pageSlug}.${key}`\n keys.set(fullKey, typeof value === 'string' ? value : JSON.stringify(value))\n }\n }\n\n return keys\n}\n\n// =============================================================================\n// Page Resolution\n// =============================================================================\n\ninterface ResolvedPage {\n slug: string\n path: string\n config: PageConfig\n}\n\nasync function resolveAllPages(config: PagesConfig): Promise<ResolvedPage[]> {\n const resolved: ResolvedPage[] = []\n\n for (const page of config.pages) {\n if (page.type === 'dynamic' && page.instances) {\n // Expand dynamic pages into individual instances\n for (const instance of page.instances) {\n const instancePath = page.pathTemplate\n ? page.pathTemplate.replace(/:[\\w]+/, instance)\n : `${page.path}/${instance}`\n\n resolved.push({\n slug: `${page.slug}-${instance}`,\n path: instancePath,\n config: { ...page, slug: `${page.slug}-${instance}` },\n })\n }\n } else {\n resolved.push({\n slug: page.slug,\n path: page.path,\n config: page,\n })\n }\n }\n\n return resolved\n}\n\n// =============================================================================\n// DOM Helpers\n// =============================================================================\n\nasync function getDomPath(element: ElementHandle): Promise<string> {\n return element.evaluate((el: Element) => {\n const parts: string[] = []\n let current: Element | null = el\n\n while (current && current !== document.body) {\n let selector = current.tagName.toLowerCase()\n\n if (current.id) {\n selector += `#${current.id}`\n } else {\n const classes = Array.from(current.classList)\n .filter((c) => !c.match(/^(v-|nuxt-|vue-|react-|ng-|_)/))\n .slice(0, 2)\n .join('.')\n if (classes) {\n selector += `.${classes}`\n }\n }\n\n parts.unshift(selector)\n current = current.parentElement\n }\n\n return parts.join(' > ')\n })\n}\n\nasync function generateSectionDisplayName(\n element: ElementHandle,\n sectionType: string,\n index: number\n): Promise<string> {\n // Try to find a heading within the section\n const headingText = await element.evaluate((el: Element) => {\n const heading = el.querySelector('h1, h2, h3, h4, [class*=\"title\"], [class*=\"heading\"]')\n if (heading && heading.textContent) {\n return heading.textContent.trim().slice(0, 50)\n }\n return null\n })\n\n if (headingText) return headingText\n\n // Fall back to section type + index\n const typeLabels: Record<string, string> = {\n header: 'Header',\n footer: 'Footer',\n hero: 'Hero Section',\n content: 'Content Section',\n gallery: 'Gallery',\n cta: 'Call to Action',\n }\n\n return typeLabels[sectionType] || `Section ${index + 1}`\n}\n\n// =============================================================================\n// Text Key Detection\n// =============================================================================\n\nasync function detectTextKeys(\n _page: Page,\n sectionElement: ElementHandle,\n _pageSlug: string,\n _validTextKeys: Map<string, string>\n): Promise<TextKeyInfo[]> {\n const textKeys: TextKeyInfo[] = []\n\n // Find editable text and managed image markers within this section.\n const textKeyElements = await sectionElement.$$('[data-text-key], [data-dcs-text], [data-dcs-image-key]')\n\n for (const element of textKeyElements) {\n const keyInfo = await element.evaluate((el: Element) => {\n const rect = el.getBoundingClientRect()\n const explicitImageUrl = el.getAttribute('data-dcs-image-url')\n const imageUrl = el instanceof HTMLImageElement ? el.currentSrc || el.src : ''\n return {\n key: el.getAttribute('data-text-key') || el.getAttribute('data-dcs-text') || el.getAttribute('data-dcs-image-key') || '',\n elementType: el.tagName.toLowerCase(),\n currentValue: (explicitImageUrl || imageUrl || el.textContent?.trim() || '').slice(0, 500),\n bounds: {\n x: rect.left + window.scrollX,\n y: rect.top + window.scrollY,\n width: rect.width,\n height: rect.height,\n },\n }\n })\n\n if (keyInfo.key && !textKeys.some((textKey) => textKey.key === keyInfo.key)) {\n const domPath = await getDomPath(element)\n textKeys.push({\n key: keyInfo.key,\n currentValue: keyInfo.currentValue,\n elementType: keyInfo.elementType,\n domPath,\n bounds: {\n x: Math.round(keyInfo.bounds.x),\n y: Math.round(keyInfo.bounds.y),\n width: Math.round(keyInfo.bounds.width),\n height: Math.round(keyInfo.bounds.height),\n },\n })\n }\n }\n\n return textKeys\n}\n\n// =============================================================================\n// Fallback Section Detection\n// =============================================================================\n\nasync function detectFallbackContentSections(\n page: Page,\n existingSections: SectionInfo[]\n): Promise<ElementHandle[]> {\n // Find large content blocks that might be sections\n const candidates = await page.$$('section, article, div[class*=\"section\"], div[class*=\"container\"]')\n\n const results: ElementHandle[] = []\n const existingBounds = existingSections.map((s) => s.bounds)\n\n for (const candidate of candidates) {\n const bounds = await candidate.evaluate((el: Element) => {\n const rect = el.getBoundingClientRect()\n return {\n y: rect.top + window.scrollY,\n height: rect.height,\n }\n })\n\n // Skip if too small or overlaps with existing sections\n if (bounds.height < 100) continue\n\n const overlaps = existingBounds.some(\n (eb) =>\n bounds.y < eb.y + eb.height && bounds.y + bounds.height > eb.y\n )\n\n if (!overlaps) {\n results.push(candidate)\n }\n }\n\n return results\n}\n\n// =============================================================================\n// Page Capture\n// =============================================================================\n\nasync function capturePageSnapshot(\n browser: Browser,\n pageInfo: ResolvedPage,\n snapshotConfig: PagesConfig['snapshot'],\n siteSlug: string,\n validTextKeys: Map<string, string>,\n outputDir: string,\n verbose: boolean\n): Promise<PageSnapshot> {\n const context = await browser.newContext({\n viewport: snapshotConfig.viewport,\n ignoreHTTPSErrors: true,\n reducedMotion: 'reduce',\n })\n const page = await context.newPage()\n\n // Build page URL and append dcs-hide-ribbon to suppress preview ribbon in screenshots\n const rawUrl = pageInfo.path.startsWith('http')\n ? pageInfo.path\n : `${process.env.SITE_BASE_URL || 'http://localhost:5173'}${pageInfo.path}`\n const pageUrl = new URL(rawUrl)\n pageUrl.searchParams.set('dcs-hide-ribbon', '')\n const url = pageUrl.toString()\n\n if (verbose) {\n console.log(` Capturing: ${pageInfo.slug} (${url})`)\n }\n\n try {\n await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30000 })\n await disableCaptureMotion(page)\n await page.waitForLoadState('networkidle', { timeout: 10000 }).catch(() => {\n if (verbose) console.log(` Note: networkidle timeout, continuing anyway`)\n })\n } catch (error) {\n console.error(` Failed to load: ${error}`)\n await context.close()\n throw error\n }\n\n await page.waitForTimeout(snapshotConfig.waitAfterLoad)\n await waitForAboveFoldAssets(page)\n\n // Create output directory for this page\n const pageOutputDir = path.join(outputDir, siteSlug, pageInfo.slug)\n fs.mkdirSync(path.join(pageOutputDir, 'sections'), { recursive: true })\n\n const title = await page.title()\n\n // Scroll through the page once so lazy-loaded content is present before the full-page capture.\n let pageHeight = await lazyLoadPage(page, snapshotConfig.viewport.height)\n await resetScrollForFullPageCapture(page)\n\n // Capture full page screenshot with fixed nav pinned to the document origin.\n const fullPagePath = path.join(pageOutputDir, 'full-page.png')\n await withFullPageCaptureLayout(page, () =>\n page.screenshot({\n path: fullPagePath,\n fullPage: snapshotConfig.captureFullPage,\n })\n )\n\n // Capture the header/hero-focused card image at the configured desktop viewport.\n const thumbnailPath = path.join(pageOutputDir, 'thumbnail.png')\n await captureHeaderThumbnail(page, thumbnailPath, snapshotConfig.viewport)\n\n // Ensure we're at scroll position 0 before capturing bounds.\n await page.evaluate(() => window.scrollTo(0, 0))\n await page.waitForTimeout(100)\n\n // Detect sections\n const sections: SectionInfo[] = []\n const seenDomPaths = new Set<string>()\n\n // Helper to get accurate bounds for fixed/sticky elements\n // For fixed/sticky elements, we need to calculate their \"document position\"\n // based on their role (header = top, footer = bottom)\n const getAccurateBounds = async (el: ElementHandle, sectionType?: string) => {\n return el.evaluate((element: Element, type: string | undefined) => {\n const rect = element.getBoundingClientRect()\n const style = window.getComputedStyle(element)\n const position = style.position\n const isFixed = position === 'fixed' || position === 'sticky'\n \n let y = rect.top + window.scrollY\n \n // For fixed/sticky headers, they should be at y=0 (top of page)\n if (isFixed && (type === 'header' || element.tagName.toLowerCase() === 'header')) {\n y = 0\n }\n // For fixed/sticky footers, they should be at the bottom of the page\n else if (isFixed && (type === 'footer' || element.tagName.toLowerCase() === 'footer')) {\n y = document.documentElement.scrollHeight - rect.height\n }\n \n return {\n x: rect.left + window.scrollX,\n y,\n width: rect.width,\n height: rect.height,\n isFixed,\n }\n }, sectionType)\n }\n\n // Phase 1: Explicit [data-section] elements\n const explicitElements = await page.$$(SECTION_SELECTORS.explicit)\n\n for (let i = 0; i < explicitElements.length; i++) {\n const element = explicitElements[i]\n\n const domPath = await getDomPath(element)\n if (seenDomPaths.has(domPath)) continue\n seenDomPaths.add(domPath)\n\n // First, get basic metadata\n const basicMetadata = await element.evaluate((el: Element) => {\n return {\n sectionId: el.getAttribute('data-section') || '',\n sectionLabel: el.getAttribute('data-section-label') || '',\n sectionType: el.getAttribute('data-section-type') || '',\n isDynamic: el.hasAttribute('data-dynamic'),\n }\n })\n\n // Determine section type early so we can pass it to bounds calculation\n let resolvedType: SectionInfo['sectionType'] = 'content'\n if (basicMetadata.sectionType) {\n resolvedType = basicMetadata.sectionType as SectionInfo['sectionType']\n } else if (['header', 'footer'].includes(basicMetadata.sectionId)) {\n resolvedType = basicMetadata.sectionId as SectionInfo['sectionType']\n } else if (basicMetadata.sectionId.includes('hero')) {\n resolvedType = 'hero'\n } else if (basicMetadata.sectionId.includes('gallery')) {\n resolvedType = 'gallery'\n }\n\n // Get accurate bounds with fixed/sticky handling\n const bounds = await getAccurateBounds(element, resolvedType)\n\n if (!bounds || bounds.height < 10) continue\n\n const sectionId = basicMetadata.sectionId || `section-${i}`\n const sectionPath = path.join(pageOutputDir, 'sections', `${sectionId}.png`)\n\n try {\n await element.screenshot({ path: sectionPath })\n } catch {\n if (verbose) console.log(` Skipped section screenshot: ${sectionId}`)\n }\n\n const isStructuralOnly = ['header', 'footer'].includes(resolvedType)\n\n // Detect text keys\n let textKeys: TextKeyInfo[] = []\n if (!isStructuralOnly) {\n textKeys = await detectTextKeys(page, element, pageInfo.slug, validTextKeys)\n }\n\n const isEditable = !isStructuralOnly && textKeys.length > 0\n\n // Generate display name - prefer data-section-label, then heading, then fallback\n let displayName = basicMetadata.sectionLabel\n if (!displayName) {\n displayName = await generateSectionDisplayName(element, resolvedType, i)\n }\n\n if (verbose) {\n console.log(\n ` Section: ${displayName} (${sectionId}) at y=${Math.round(bounds.y)}${\n basicMetadata.isDynamic ? ' [dynamic]' : ''\n } - ${textKeys.length} text keys`\n )\n }\n\n sections.push({\n sectionId,\n sectionType: resolvedType,\n displayName,\n domPath,\n bounds: {\n x: Math.round(bounds.x),\n y: Math.round(bounds.y),\n width: Math.round(bounds.width),\n height: Math.round(bounds.height),\n },\n textKeys,\n isEditable,\n isStructuralOnly,\n })\n }\n\n // Phase 2: Type-based sections (header, footer, hero, etc.)\n for (const [sectionType, selector] of Object.entries(SECTION_SELECTORS)) {\n if (sectionType === 'explicit') continue\n\n const elements = await page.$$(selector)\n\n for (let i = 0; i < elements.length; i++) {\n const element = elements[i]\n\n const domPath = await getDomPath(element)\n if (seenDomPaths.has(domPath)) continue\n\n // Skip elements that are INSIDE an explicit [data-section]\n // This prevents nested elements from being detected as separate sections\n const isInsideExplicitSection = await element.evaluate((el: Element) => {\n // Walk up the DOM tree to check if any ancestor has data-section\n let current: HTMLElement | null = el.parentElement\n while (current) {\n if (current.dataset?.section) {\n return true\n }\n current = current.parentElement\n }\n return false\n })\n\n if (isInsideExplicitSection) {\n continue // Silently skip - these are intentionally nested inside explicit sections\n }\n\n // Skip elements that are ancestors of already-detected explicit sections\n // This prevents the <main> container from being detected as a \"content\" section\n // when it contains explicit [data-section] children\n const isAncestorOfExplicitSection = await element.evaluate((el: Element) => {\n const explicitSections = el.querySelectorAll('[data-section]')\n return explicitSections.length > 0\n })\n\n if (isAncestorOfExplicitSection) {\n if (verbose) {\n console.log(` Skipping ${sectionType} container (contains explicit sections)`)\n }\n continue\n }\n\n seenDomPaths.add(domPath)\n\n // Use accurate bounds calculation for fixed/sticky elements\n const bounds = await getAccurateBounds(element, sectionType)\n\n if (!bounds || bounds.height < 10) continue\n\n const sectionId = `${sectionType}-${i}`\n const sectionPath = path.join(pageOutputDir, 'sections', `${sectionId}.png`)\n\n try {\n await element.screenshot({ path: sectionPath })\n } catch {\n if (verbose) console.log(` Skipped section screenshot: ${sectionId}`)\n }\n\n const isStructuralOnly = ['header', 'footer'].includes(sectionType)\n const textKeys = isStructuralOnly\n ? []\n : await detectTextKeys(page, element, pageInfo.slug, validTextKeys)\n\n // Skip content sections that have no text keys - they add noise without value\n if (sectionType === 'content' && textKeys.length === 0) {\n continue\n }\n\n const isEditable = !isStructuralOnly && textKeys.length > 0\n const displayName = await generateSectionDisplayName(element, sectionType, i)\n\n if (verbose) {\n console.log(` Section: ${displayName} at y=${Math.round(bounds.y)}${bounds.isFixed ? ' [fixed]' : ''}`)\n }\n\n sections.push({\n sectionId,\n sectionType: sectionType as SectionInfo['sectionType'],\n displayName,\n domPath,\n bounds: {\n x: Math.round(bounds.x),\n y: Math.round(bounds.y),\n width: Math.round(bounds.width),\n height: Math.round(bounds.height),\n },\n textKeys,\n isEditable,\n isStructuralOnly,\n })\n }\n }\n\n // Fallback: If no content sections, try heuristic detection\n const hasContentSection = sections.some((s) => s.sectionType === 'content')\n if (!hasContentSection) {\n if (verbose) console.log(` No content sections found, trying fallback detection...`)\n\n const fallbackElements = await detectFallbackContentSections(page, sections)\n\n for (let i = 0; i < fallbackElements.length; i++) {\n const element = fallbackElements[i]\n\n const domPath = await getDomPath(element)\n if (seenDomPaths.has(domPath)) continue\n seenDomPaths.add(domPath)\n\n const bounds = await element.evaluate((el: Element) => {\n const rect = el.getBoundingClientRect()\n return {\n x: rect.left + window.scrollX,\n y: rect.top + window.scrollY,\n width: rect.width,\n height: rect.height,\n }\n })\n\n if (!bounds || bounds.height < 10) continue\n\n const sectionId = `content-fallback-${i}`\n const sectionPath = path.join(pageOutputDir, 'sections', `${sectionId}.png`)\n\n try {\n await element.screenshot({ path: sectionPath })\n } catch {\n if (verbose) console.log(` Skipped fallback section screenshot: ${sectionId}`)\n }\n\n const textKeys = await detectTextKeys(page, element, pageInfo.slug, validTextKeys)\n const displayName = await generateSectionDisplayName(element, 'content', i)\n\n if (verbose) {\n console.log(\n ` Fallback Section: ${displayName} at y=${Math.round(bounds.y)} (${textKeys.length} text keys)`\n )\n }\n\n sections.push({\n sectionId,\n sectionType: 'content',\n displayName,\n domPath,\n bounds: {\n x: Math.round(bounds.x),\n y: Math.round(bounds.y),\n width: Math.round(bounds.width),\n height: Math.round(bounds.height),\n },\n textKeys,\n isEditable: textKeys.length > 0,\n isStructuralOnly: false,\n })\n }\n }\n\n await context.close()\n\n // Sort sections by vertical position\n sections.sort((a, b) => a.bounds.y - b.bounds.y)\n\n return {\n pageSlug: pageInfo.slug,\n path: pageInfo.path,\n type: pageInfo.config.type,\n deletable: pageInfo.config.deletable,\n title: title || pageInfo.config.title,\n capturedAt: new Date().toISOString(),\n previewUrl: url,\n viewport: snapshotConfig.viewport,\n pageHeight,\n sections,\n }\n}\n\n// =============================================================================\n// Main Command\n// =============================================================================\n\nexport async function captureSnapshotsCommand(options: CaptureSnapshotsOptions): Promise<void> {\n const { target, baseUrl, dryRun, verbose, pages: targetedPages } = options\n const targetDir = path.resolve(target)\n\n console.log(chalk.blue('📸 Page Snapshot Capture'))\n console.log(chalk.gray('========================'))\n console.log(chalk.gray(`Target: ${targetDir}`))\n console.log(chalk.gray(`Base URL: ${baseUrl}`))\n\n // Set base URL for capture\n process.env.SITE_BASE_URL = baseUrl\n\n // Load configuration\n let config: PagesConfig\n try {\n config = loadPagesConfig(targetDir)\n } catch (error) {\n console.error(chalk.red('Error:'), (error as Error).message)\n console.log(chalk.yellow('\\nMake sure .dcs/pages.yaml exists in the target directory.'))\n process.exit(1)\n }\n\n console.log(chalk.gray(`Site: ${config.siteSlug}`))\n\n const outputDir = path.join(targetDir, config.snapshot.outputDir || '.dcs/snapshots')\n\n // Load content configuration (optional)\n const contentConfig = loadContentConfig(targetDir)\n if (contentConfig) {\n const pageCount = Object.keys(contentConfig.pages || {}).length\n const globalCount = Object.keys(contentConfig.global || {}).length\n console.log(chalk.gray(`Content: Loaded ${pageCount} pages, ${globalCount} global keys`))\n }\n\n // Check for targeted snapshots config\n const targetedConfig = loadTargetedSnapshotsConfig(targetDir)\n const isTargetedCapture = Boolean(\n (targetedPages && targetedPages.length > 0) ||\n (targetedConfig?.mode === 'targeted' && targetedConfig.pages.length > 0)\n )\n\n if (targetedConfig?.mode === 'skip') {\n console.log('')\n console.log(chalk.yellow('⏭️ Skip mode activated!'))\n console.log(chalk.gray(` Reason: ${targetedConfig.reason}`))\n console.log(chalk.gray(' No snapshots will be captured.'))\n\n if (!dryRun) {\n fs.mkdirSync(outputDir, { recursive: true })\n const skipMarker = {\n skipped: true,\n reason: targetedConfig.reason,\n triggeredBy: targetedConfig.triggeredBy,\n timestamp: new Date().toISOString(),\n }\n fs.writeFileSync(path.join(outputDir, 'skipped.json'), JSON.stringify(skipMarker, null, 2))\n }\n\n console.log(chalk.green('\\n✅ Snapshot capture skipped (as requested)'))\n return\n }\n\n // Resolve pages\n let pagesToCapture = await resolveAllPages(config)\n console.log(chalk.gray(`Found ${pagesToCapture.length} total pages in configuration`))\n\n // Apply filtering from command-line --pages option\n if (targetedPages && targetedPages.length > 0) {\n const targetedSlugs = new Set(targetedPages)\n const originalCount = pagesToCapture.length\n pagesToCapture = pagesToCapture.filter((p) => targetedSlugs.has(p.slug))\n console.log('')\n console.log(chalk.cyan(`🎯 Targeted mode: Filtering to ${pagesToCapture.length} of ${originalCount} pages`))\n }\n // Apply filtering from targeted-snapshots.yaml\n else if (targetedConfig?.mode === 'targeted' && targetedConfig.pages.length > 0) {\n const targetedSlugs = new Set(targetedConfig.pages)\n const originalCount = pagesToCapture.length\n pagesToCapture = pagesToCapture.filter((p) => targetedSlugs.has(p.slug))\n console.log('')\n console.log(chalk.cyan(`🎯 Targeted mode: Filtering to ${pagesToCapture.length} of ${originalCount} pages`))\n console.log(chalk.gray(` Reason: ${targetedConfig.reason}`))\n }\n\n // List pages to capture\n console.log('')\n console.log('Pages to capture:')\n for (const p of pagesToCapture) {\n console.log(chalk.gray(` - ${p.slug} (${p.config.type}) ${p.path}`))\n }\n\n if (pagesToCapture.length === 0) {\n console.log(chalk.yellow('\\n⚠️ No pages to capture!'))\n return\n }\n\n if (dryRun) {\n console.log(chalk.yellow('\\n--dry-run: Stopping before browser launch'))\n return\n }\n\n // Dynamic import of playwright to avoid requiring it when not using this command\n const { chromium } = await import('playwright')\n\n // Clear and create output directory\n if (fs.existsSync(outputDir)) {\n fs.rmSync(outputDir, { recursive: true })\n }\n fs.mkdirSync(outputDir, { recursive: true })\n\n const spinner = ora('Launching browser...').start()\n const browser = await chromium.launch()\n spinner.succeed('Browser launched')\n\n const snapshots: PageSnapshot[] = []\n const failures: string[] = []\n\n for (const pageInfo of pagesToCapture) {\n const pageSpinner = ora(`Capturing ${pageInfo.slug}...`).start()\n\n try {\n const validTextKeys = getValidTextKeysForPage(contentConfig, pageInfo.slug)\n const snapshot = await capturePageSnapshot(\n browser,\n pageInfo,\n config.snapshot,\n config.siteSlug,\n validTextKeys,\n outputDir,\n verbose || false\n )\n snapshots.push(snapshot)\n\n // Save individual page snapshot JSON\n const snapshotPath = path.join(outputDir, config.siteSlug, pageInfo.slug, 'snapshot.json')\n fs.writeFileSync(snapshotPath, JSON.stringify(snapshot, null, 2))\n\n pageSpinner.succeed(`${pageInfo.slug}: ${snapshot.sections.length} sections captured`)\n } catch (error) {\n const message = `${pageInfo.slug}: ${(error as Error).message}`\n failures.push(message)\n pageSpinner.fail(`${pageInfo.slug}: Failed - ${(error as Error).message}`)\n }\n }\n\n await browser.close()\n\n // Create manifest\n const manifest: SnapshotManifest = {\n siteSlug: config.siteSlug,\n capturedAt: new Date().toISOString(),\n capturedVersion: process.env.GITHUB_SHA,\n deploymentRef: process.env.GITHUB_REF || 'local',\n pagesConfigVersion: config.version,\n pages: snapshots.map((s) => ({\n pageSlug: s.pageSlug,\n pagePath: s.path,\n pageType: s.type,\n deletable: s.deletable,\n title: s.title,\n capturedAt: s.capturedAt,\n sectionCount: s.sections.length,\n hasSnapshot: true,\n })),\n }\n\n const manifestPath = path.join(outputDir, config.siteSlug, 'manifest.json')\n if (isTargetedCapture) {\n console.log(chalk.gray(' Targeted capture: leaving the existing remote manifest intact'))\n } else {\n fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2))\n }\n\n console.log('')\n console.log(chalk.green('✅ Snapshot capture complete!'))\n console.log(chalk.gray(` Captured ${snapshots.length} pages`))\n console.log(chalk.gray(` - Static: ${snapshots.filter((s) => s.type === 'static').length}`))\n console.log(chalk.gray(` - Index: ${snapshots.filter((s) => s.type === 'index').length}`))\n console.log(chalk.gray(` - Dynamic: ${snapshots.filter((s) => s.type === 'dynamic').length}`))\n console.log(chalk.gray(` Output: ${outputDir}`))\n\n if (failures.length > 0) {\n throw new Error(`Snapshot capture failed for ${failures.length} page(s): ${failures.join('; ')}`)\n }\n}\n"],"mappings":"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","names":["chalk","chalk","data","chalk","chalk","DEFAULT_API_URL","path","chalk","chalk","ora","chalk","ora","fs","path","chalk","config","fs","path","chalk","yaml","fs","path","yaml","chalk","ora","config","chalk"]}
1
+ {"version":3,"sources":["../src/index.ts","../package.json","../src/commands/auth.ts","../src/auth/device-flow.ts","../src/auth/credentials.ts","../src/auth/keychain/run.ts","../src/auth/keychain/types.ts","../src/auth/keychain/macos.ts","../src/auth/keychain/libsecret.ts","../src/auth/keychain/windows.ts","../src/auth/keychain/paths.ts","../src/auth/keychain/encrypted-file.ts","../src/auth/keychain/index.ts","../src/commands/sites.ts","../src/api/portal-client.ts","../src/commands/init.ts","../src/templates/site-deploy.yml","../src/commands/validate.ts","../src/commands/plans.ts","../src/commands/capture-snapshots.ts"],"sourcesContent":["/**\n * Main CLI entry point.\n */\n\nimport { Command } from 'commander'\nimport chalk from 'chalk'\nimport { version } from '../package.json'\n\n// Import commands\nimport { loginCommand, logoutCommand, whoamiCommand } from './commands/auth.js'\nimport { listSitesCommand, showSiteCommand } from './commands/sites.js'\nimport { initCommand } from './commands/init.js'\nimport { validateCommand } from './commands/validate.js'\nimport { plansCommand } from './commands/plans.js'\nimport { captureSnapshotsCommand } from './commands/capture-snapshots.js'\n\nconst program = new Command()\n\nprogram\n .name('dcs')\n .description('DCS (Duff Cloud Services) CLI for customer site management')\n .version(version)\n\n// =============================================================================\n// Auth Commands\n// =============================================================================\n\nprogram\n .command('login')\n .description('Authenticate with DCS Portal using Google OAuth')\n .action(async () => {\n await loginCommand()\n })\n\nprogram\n .command('logout')\n .description('Clear stored credentials')\n .action(async () => {\n await logoutCommand()\n })\n\nprogram\n .command('whoami')\n .description('Display current authenticated user')\n .action(async () => {\n await whoamiCommand()\n })\n\n// =============================================================================\n// Site Commands\n// =============================================================================\n\nconst sitesCmd = program\n .command('sites')\n .description('Manage customer sites')\n\nsitesCmd\n .command('list')\n .description('List sites you have access to')\n .action(async () => {\n await listSitesCommand()\n })\n\nsitesCmd\n .command('show <slug>')\n .description('Show details for a specific site')\n .action(async (slug: string) => {\n await showSiteCommand(slug)\n })\n\n// =============================================================================\n// Init Command\n// =============================================================================\n\nprogram\n .command('init')\n .description('Initialize DCS integration for a customer site')\n .requiredOption('-s, --site-slug <slug>', 'Site slug (lowercase, hyphens)')\n .requiredOption('-n, --site-name <name>', 'Human-readable site name')\n .option('-f, --framework <framework>', 'Site framework (vue, astro)', 'vue')\n .option('-t, --target <dir>', 'Target directory', '.')\n .option('--dry-run', 'Show what would be created without writing files')\n .option('--force', 'Overwrite existing files')\n .action(async (options) => {\n await initCommand({\n siteSlug: options.siteSlug,\n siteName: options.siteName,\n framework: options.framework,\n target: options.target,\n dryRun: options.dryRun,\n force: options.force,\n })\n })\n\n// =============================================================================\n// Validate Command\n// =============================================================================\n\nprogram\n .command('validate')\n .description('Validate .dcs configuration files')\n .option('-t, --target <dir>', 'Target directory', '.')\n .option('--fix', 'Attempt to fix common issues')\n .option('-v, --verbose', 'Show detailed output')\n .action(async (options) => {\n await validateCommand({\n target: options.target,\n fix: options.fix,\n verbose: options.verbose,\n })\n })\n\n// =============================================================================\n// Plans Command\n// =============================================================================\n\nprogram\n .command('plans')\n .description('Generate DCS integration plan files for AI-assisted onboarding')\n .option('-t, --target <dir>', 'Target directory', '.')\n .option('--force', 'Overwrite existing plan files')\n .action(async (options) => {\n await plansCommand({\n target: options.target,\n force: options.force,\n })\n })\n\n// =============================================================================\n// Capture Snapshots Command\n// =============================================================================\n\nprogram\n .command('capture-snapshots')\n .description('Capture visual snapshots of site pages for the portal page editor')\n .option('-t, --target <dir>', 'Target directory containing .dcs/pages.yaml', '.')\n .option('-u, --base-url <url>', 'Base URL of the running site', 'http://localhost:5173')\n .option('-p, --pages <slugs>', 'Comma-separated list of page slugs to capture')\n .option('--dry-run', 'Show what would be captured without launching browser')\n .option('-v, --verbose', 'Show detailed output')\n .action(async (options) => {\n const pages = options.pages ? options.pages.split(',').map((s: string) => s.trim()) : undefined\n await captureSnapshotsCommand({\n target: options.target,\n baseUrl: options.baseUrl,\n dryRun: options.dryRun,\n verbose: options.verbose,\n pages,\n })\n })\n\n// =============================================================================\n// Error Handling\n// =============================================================================\n\nprogram.exitOverride()\n\ntry {\n await program.parseAsync(process.argv)\n} catch (error) {\n if (error instanceof Error) {\n // Commander errors (help, version) are expected\n if ((error as { code?: string }).code === 'commander.helpDisplayed' ||\n (error as { code?: string }).code === 'commander.version') {\n process.exit(0)\n }\n\n console.error(chalk.red('Error:'), error.message)\n process.exit(1)\n }\n}\n","{\n \"name\": \"@duffcloudservices/cli\",\n \"version\": \"0.3.3\",\n \"description\": \"CLI for DCS site onboarding and configuration\",\n \"type\": \"module\",\n \"bin\": {\n \"dcs\": \"./dist/index.js\"\n },\n \"main\": \"./dist/index.js\",\n \"files\": [\n \"dist\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"start\": \"node dist/index.js\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"type-check\": \"tsc --noEmit\",\n \"lint\": \"eslint src --ext .ts\",\n \"prepublishOnly\": \"pnpm run build\"\n },\n \"dependencies\": {\n \"chalk\": \"^5.3.0\",\n \"cli-table3\": \"^0.6.5\",\n \"commander\": \"^12.1.0\",\n \"conf\": \"^13.0.1\",\n \"handlebars\": \"^4.7.8\",\n \"inquirer\": \"^9.3.7\",\n \"js-yaml\": \"^4.1.0\",\n \"open\": \"^10.1.0\",\n \"ora\": \"^8.1.1\",\n \"playwright\": \"^1.49.0\"\n },\n \"devDependencies\": {\n \"@types/inquirer\": \"^9.0.7\",\n \"@types/js-yaml\": \"^4.0.9\",\n \"@types/node\": \"^20.11.0\",\n \"tsup\": \"^8.0.0\",\n \"typescript\": \"~5.6.3\",\n \"vitest\": \"^3.2.3\"\n },\n \"keywords\": [\n \"dcs\",\n \"cli\",\n \"cms\",\n \"onboarding\",\n \"duff-cloud-services\"\n ],\n \"author\": \"Duff Cloud Services\",\n \"license\": \"MIT\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/NateDuff/dcs\"\n },\n \"homepage\": \"https://portal.duffcloudservices.com\",\n \"bugs\": {\n \"url\": \"https://github.com/NateDuff/dcs/issues\"\n },\n \"engines\": {\n \"node\": \">=18.0.0\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n }\n}\n","/**\n * Authentication commands for the CLI.\n */\n\nimport chalk from 'chalk'\nimport { login as performLogin, type DeviceFlowOptions } from '../auth/device-flow.js'\nimport { clearCredentials, getCurrentUserEmail, getStorageBackendName } from '../auth/credentials.js'\n\n/**\n * Login command - authenticate with DCS Portal.\n */\nexport async function loginCommand(options: DeviceFlowOptions = {}): Promise<void> {\n try {\n await performLogin(options)\n } catch (error) {\n if (error instanceof Error) {\n console.error(chalk.red('Login failed:'), error.message)\n }\n process.exit(1)\n }\n}\n\n/**\n * Logout command - clear stored credentials.\n */\nexport async function logoutCommand(): Promise<void> {\n clearCredentials()\n console.log(chalk.green('✓'), 'Logged out successfully')\n}\n\n/**\n * Whoami command - display current user.\n */\nexport async function whoamiCommand(): Promise<void> {\n const email = getCurrentUserEmail()\n\n if (!email) {\n console.log(chalk.yellow('Not logged in.'))\n console.log('Run', chalk.cyan('dcs login'), 'to authenticate.')\n return\n }\n\n console.log('Logged in as:', chalk.green(email))\n console.log(chalk.dim('Token storage:'), chalk.dim(getStorageBackendName()))\n}\n","/**\n * Device authorization flow for CLI authentication.\n *\n * Implements RFC 8628 (OAuth 2.0 Device Authorization Grant) style flow\n * for authenticating CLI users via the DCS Portal.\n */\n\nimport open from 'open'\nimport ora from 'ora'\nimport chalk from 'chalk'\nimport { storeCredentials, type StoredCredentials } from './credentials.js'\n\nconst DEFAULT_API_URL = 'https://portal.duffcloudservices.com'\n\nexport interface DeviceAuthResponse {\n deviceCode: string\n userCode: string\n verificationUri: string\n verificationUriComplete: string\n expiresIn: number\n interval: number\n}\n\nexport interface TokenResponse {\n accessToken: string\n refreshToken: string\n tokenType: string\n expiresIn: number\n email: string\n userId: string\n}\n\nexport interface DeviceFlowOptions {\n apiUrl?: string\n clientId?: string\n}\n\n/**\n * Error codes returned during device authorization polling.\n */\ntype DeviceAuthError = 'authorization_pending' | 'slow_down' | 'expired_token' | 'access_denied'\n\n/**\n * Thrown when the server rejects a refresh token (HTTP 401): expired past its\n * 30-day life, or revoked server-side (A-87 gate 1). The stored session is dead\n * and the user must re-authenticate — callers must NOT retry the refresh.\n */\nexport class RefreshTokenRevokedError extends Error {\n constructor(message = 'Refresh token was rejected (expired or revoked).') {\n super(message)\n this.name = 'RefreshTokenRevokedError'\n }\n}\n\n/**\n * Thrown up to the command layer when the CLI must restart the device flow.\n * The dead credentials have already been cleared before this is thrown.\n */\nexport class ReauthRequiredError extends Error {\n constructor(message = 'Your CLI session expired or was revoked. Run `dcs login` to sign in again.') {\n super(message)\n this.name = 'ReauthRequiredError'\n }\n}\n\n/**\n * Sleep for a given number of milliseconds.\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n\n/**\n * Start the device authorization flow.\n */\nexport async function startDeviceAuth(options: DeviceFlowOptions = {}): Promise<DeviceAuthResponse> {\n const apiUrl = options.apiUrl || DEFAULT_API_URL\n const clientId = options.clientId || 'dcs-cli'\n\n const response = await fetch(`${apiUrl}/api/v1/cli/auth/device`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ client_id: clientId }),\n })\n\n if (!response.ok) {\n const error = await response.text()\n throw new Error(`Failed to start device authorization: ${error}`)\n }\n\n const data = await response.json()\n\n return {\n deviceCode: data.device_code,\n userCode: data.user_code,\n verificationUri: data.verification_uri,\n verificationUriComplete: data.verification_uri_complete,\n expiresIn: data.expires_in,\n interval: data.interval,\n }\n}\n\n/**\n * Poll for authorization completion.\n */\nasync function pollForToken(\n deviceCode: string,\n apiUrl: string\n): Promise<TokenResponse | { error: DeviceAuthError }> {\n const response = await fetch(`${apiUrl}/api/v1/cli/auth/token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n device_code: deviceCode,\n grant_type: 'urn:ietf:params:oauth:grant-type:device_code',\n }),\n })\n\n if (!response.ok) {\n const data = await response.json()\n return { error: data.error as DeviceAuthError }\n }\n\n const data = await response.json()\n\n return {\n accessToken: data.access_token,\n refreshToken: data.refresh_token,\n tokenType: data.token_type,\n expiresIn: data.expires_in,\n email: data.email,\n userId: data.user_id,\n }\n}\n\n/**\n * Complete the device authorization flow.\n *\n * This function:\n * 1. Starts the device authorization\n * 2. Opens the browser to the verification URL\n * 3. Polls for completion\n * 4. Stores credentials on success\n */\nexport async function login(options: DeviceFlowOptions = {}): Promise<StoredCredentials> {\n const apiUrl = options.apiUrl || DEFAULT_API_URL\n\n const spinner = ora('Starting authentication...').start()\n\n // Start device auth\n let deviceAuth: DeviceAuthResponse\n try {\n deviceAuth = await startDeviceAuth(options)\n } catch (error) {\n spinner.fail('Failed to start authentication')\n throw error\n }\n\n spinner.stop()\n\n // Display instructions\n console.log()\n console.log(chalk.bold('Please visit:'), chalk.cyan(deviceAuth.verificationUri))\n console.log(chalk.bold('And enter code:'), chalk.yellow.bold(deviceAuth.userCode))\n console.log()\n\n // Try to open browser\n try {\n await open(deviceAuth.verificationUriComplete)\n console.log(chalk.dim('(Browser opened automatically)'))\n } catch {\n console.log(chalk.dim('(Please open the link manually)'))\n }\n\n console.log()\n\n // Poll for completion\n const pollSpinner = ora('Waiting for authentication...').start()\n\n let interval = deviceAuth.interval\n const deadline = Date.now() + deviceAuth.expiresIn * 1000\n\n while (Date.now() < deadline) {\n await sleep(interval * 1000)\n\n try {\n const result = await pollForToken(deviceAuth.deviceCode, apiUrl)\n\n if ('error' in result) {\n if (result.error === 'authorization_pending') {\n continue\n }\n if (result.error === 'slow_down') {\n interval += 5\n continue\n }\n if (result.error === 'expired_token') {\n pollSpinner.fail('Authentication timed out')\n throw new Error('Authentication timed out. Please try again.')\n }\n if (result.error === 'access_denied') {\n pollSpinner.fail('Authentication denied')\n throw new Error('Authentication was denied.')\n }\n } else {\n // Success!\n const credentials: StoredCredentials = {\n accessToken: result.accessToken,\n refreshToken: result.refreshToken,\n expiresAt: Date.now() + result.expiresIn * 1000,\n email: result.email,\n userId: result.userId,\n }\n\n storeCredentials(credentials)\n\n pollSpinner.succeed(`Authenticated as ${chalk.green(result.email)}`)\n return credentials\n }\n } catch (error) {\n // Network error - keep polling\n if (error instanceof Error && error.message.includes('fetch')) {\n continue\n }\n throw error\n }\n }\n\n pollSpinner.fail('Authentication timed out')\n throw new Error('Authentication timed out. Please try again.')\n}\n\n/**\n * Refresh the access token using the refresh token.\n *\n * Distinguishes a definitive rejection (HTTP 401 — expired/revoked refresh\n * token, per A-87 gate 1) from a transient failure (network error, 5xx). A 401\n * throws `RefreshTokenRevokedError` so the caller can drop the dead session and\n * re-authenticate exactly once — never a retry loop. Transient failures throw a\n * plain Error, leaving the stored refresh token in place to retry later.\n */\nexport async function refreshAccessToken(\n refreshToken: string,\n options: DeviceFlowOptions = {}\n): Promise<{ accessToken: string; expiresIn: number }> {\n const apiUrl = options.apiUrl || DEFAULT_API_URL\n\n const response = await fetch(`${apiUrl}/api/v1/cli/auth/refresh`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ refresh_token: refreshToken }),\n })\n\n if (response.status === 401) {\n throw new RefreshTokenRevokedError()\n }\n\n if (!response.ok) {\n throw new Error(`Failed to refresh access token (HTTP ${response.status}).`)\n }\n\n const data = await response.json()\n\n return {\n accessToken: data.access_token,\n expiresIn: data.expires_in,\n }\n}\n","/**\n * Credential storage for CLI authentication.\n *\n * Access tokens, refresh tokens, and user info are stored in the OS keychain\n * (macOS Keychain / libsecret / Windows DPAPI) via the backend resolved in\n * `./keychain`. The secret is a single JSON blob; the plaintext is NEVER written\n * to disk by the CLI.\n *\n * Legacy plaintext `conf` JSON (written by CLI < keychain) is transparently\n * migrated on first read: it is copied into the keychain and then deleted.\n */\nimport Conf from 'conf'\nimport { getKeychain } from './keychain/index.js'\n\nexport interface StoredCredentials {\n accessToken: string\n refreshToken: string\n expiresAt: number\n email: string\n userId: string\n}\n\nconst ACCOUNT = 'auth'\n\n// ---------------------------------------------------------------------------\n// Legacy plaintext store (migration source only — never written to again).\n// ---------------------------------------------------------------------------\ninterface LegacyConfigSchema {\n auth?: StoredCredentials\n}\n\nexport interface LegacyStore {\n read(): StoredCredentials | undefined\n clear(): void\n}\n\nfunction defaultLegacyStore(): LegacyStore {\n const config = new Conf<LegacyConfigSchema>({ projectName: 'dcs-cli' })\n return {\n read: () => config.get('auth'),\n clear: () => {\n // Only touch the file when there is actually a legacy entry, so routine\n // logins don't create an empty config.json.\n if (config.has('auth')) config.delete('auth')\n },\n }\n}\n\nlet legacyStore: LegacyStore | undefined\nfunction getLegacyStore(): LegacyStore {\n if (!legacyStore) legacyStore = defaultLegacyStore()\n return legacyStore\n}\n\n// ---------------------------------------------------------------------------\n// In-process cache so repeated getters don't re-hit the OS keychain (each hit\n// can spawn a subprocess). Invalidated on every write/clear.\n// ---------------------------------------------------------------------------\nlet cache: StoredCredentials | null | undefined // undefined = not loaded yet\n\nfunction load(): StoredCredentials | null {\n if (cache !== undefined) return cache\n\n const raw = getKeychain().get(ACCOUNT)\n if (raw) {\n try {\n cache = JSON.parse(raw) as StoredCredentials\n return cache\n } catch {\n // Corrupt entry — drop it and force re-auth.\n getKeychain().delete(ACCOUNT)\n cache = null\n return cache\n }\n }\n\n // Nothing in the keychain — attempt one-time migration from legacy plaintext.\n const legacy = getLegacyStore().read()\n if (legacy) {\n getKeychain().set(ACCOUNT, JSON.stringify(legacy))\n getLegacyStore().clear()\n cache = legacy\n return cache\n }\n\n cache = null\n return cache\n}\n\nfunction persist(creds: StoredCredentials): void {\n getKeychain().set(ACCOUNT, JSON.stringify(creds))\n cache = creds\n // Belt-and-suspenders: make sure no plaintext copy lingers.\n getLegacyStore().clear()\n}\n\n/**\n * Store credentials after successful authentication.\n */\nexport function storeCredentials(credentials: StoredCredentials): void {\n persist(credentials)\n}\n\n/**\n * Get stored credentials.\n * Returns undefined if not authenticated.\n */\nexport function getCredentials(): StoredCredentials | undefined {\n return load() ?? undefined\n}\n\n/**\n * Clear stored credentials (logout).\n */\nexport function clearCredentials(): void {\n getKeychain().delete(ACCOUNT)\n getLegacyStore().clear()\n cache = null\n}\n\n/**\n * Check if user is authenticated with valid (non-expired) credentials.\n */\nexport function isAuthenticated(): boolean {\n const creds = load()\n if (!creds) return false\n\n // Check if access token is expired (with 5 minute buffer)\n const bufferMs = 5 * 60 * 1000\n return creds.expiresAt > Date.now() + bufferMs\n}\n\n/**\n * Whether a stored session exists at all (regardless of access-token expiry).\n * Use this to gate commands so an expired-but-refreshable session still gets a\n * chance to refresh instead of being told to log in again.\n */\nexport function hasStoredSession(): boolean {\n return load() !== null\n}\n\n/**\n * Get the current user's email if authenticated.\n */\nexport function getCurrentUserEmail(): string | undefined {\n return load()?.email\n}\n\n/**\n * Get the current user's ID if authenticated.\n */\nexport function getCurrentUserId(): string | undefined {\n return load()?.userId\n}\n\n/**\n * Get the access token if available.\n * Returns even if expired — the API client handles refresh.\n */\nexport function getAccessToken(): string | undefined {\n return load()?.accessToken\n}\n\n/**\n * Get the refresh token if available.\n */\nexport function getRefreshToken(): string | undefined {\n return load()?.refreshToken\n}\n\n/**\n * Update the access token after a refresh.\n */\nexport function updateAccessToken(accessToken: string, expiresIn: number): void {\n const creds = load()\n if (!creds) return\n\n persist({\n ...creds,\n accessToken,\n expiresAt: Date.now() + expiresIn * 1000,\n })\n}\n\n/**\n * Name of the active keychain backend (diagnostics).\n */\nexport function getStorageBackendName(): string {\n return getKeychain().name\n}\n\n// ---------------------------------------------------------------------------\n// Test hooks.\n// ---------------------------------------------------------------------------\nexport function __setLegacyStoreForTesting(store: LegacyStore | undefined): void {\n legacyStore = store\n}\n\nexport function __resetCredentialCacheForTesting(): void {\n cache = undefined\n}\n","/**\n * Thin wrapper around spawnSync for invoking OS keychain tools.\n *\n * Secrets are passed via environment variables (never argv) so they do not\n * appear in the process list. stdin is used where a tool reads the secret from\n * a pipe (e.g. `secret-tool store`).\n */\nimport { spawnSync, type SpawnSyncOptions } from 'node:child_process'\n\nexport interface RunResult {\n ok: boolean\n code: number | null\n stdout: string\n stderr: string\n /** True when the binary itself could not be found/launched. */\n spawnError: boolean\n}\n\nexport function run(\n cmd: string,\n args: string[],\n opts: { env?: NodeJS.ProcessEnv; input?: string } = {}\n): RunResult {\n const options: SpawnSyncOptions = {\n encoding: 'utf8',\n env: opts.env ? { ...process.env, ...opts.env } : process.env,\n windowsHide: true,\n }\n if (opts.input !== undefined) options.input = opts.input\n\n const res = spawnSync(cmd, args, options)\n\n if (res.error) {\n return { ok: false, code: null, stdout: '', stderr: String(res.error), spawnError: true }\n }\n return {\n ok: res.status === 0,\n code: res.status,\n stdout: typeof res.stdout === 'string' ? res.stdout : '',\n stderr: typeof res.stderr === 'string' ? res.stderr : '',\n spawnError: false,\n }\n}\n\n/** Whether a command can be launched at all (used for backend availability). */\nexport function commandExists(cmd: string, probeArgs: string[]): boolean {\n const res = run(cmd, probeArgs)\n return !res.spawnError\n}\n","/**\n * Keychain backend contract.\n *\n * A backend stores a single opaque secret string per `account` under a fixed\n * service namespace. Implementations MUST NEVER persist the secret in\n * plaintext: OS keychains (macOS Keychain, libsecret) encrypt at rest, the\n * Windows backend uses DPAPI, and the last-resort file backend uses\n * authenticated encryption with a machine/user-derived key.\n */\nexport interface KeychainBackend {\n /** Stable identifier for diagnostics (`dcs whoami` / reports). */\n readonly name: string\n /** Whether this backend can be used on the current machine. */\n isAvailable(): boolean\n /** Return the stored secret for `account`, or null if none is stored. */\n get(account: string): string | null\n /** Store (or replace) the secret for `account`. */\n set(account: string, secret: string): void\n /** Remove any stored secret for `account`. Must be a no-op if absent. */\n delete(account: string): void\n}\n\n/** Service namespace shared by every backend. */\nexport const SERVICE = 'dcs-cli'\n","/**\n * macOS Keychain backend via the built-in `security` tool.\n *\n * Secrets live in the login keychain as generic passwords, encrypted at rest by\n * the OS. The literal secret is passed to `security` on stdin-free argv only for\n * writes (there is no env-var form of `add-generic-password`); it is never\n * written to disk by the CLI.\n */\nimport { run, commandExists } from './run.js'\nimport { SERVICE, type KeychainBackend } from './types.js'\n\nexport const macosKeychain: KeychainBackend = {\n name: 'macos-keychain',\n\n isAvailable(): boolean {\n return process.platform === 'darwin' && commandExists('security', ['help'])\n },\n\n get(account: string): string | null {\n const res = run('security', [\n 'find-generic-password',\n '-s',\n SERVICE,\n '-a',\n account,\n '-w',\n ])\n if (!res.ok) return null // exit 44 = item not found\n return res.stdout.replace(/\\n$/, '')\n },\n\n set(account: string, secret: string): void {\n // -U updates if present. `security` has no env/stdin path for the secret, so\n // it is supplied as the -w value; it stays out of any file we write.\n const res = run('security', [\n 'add-generic-password',\n '-s',\n SERVICE,\n '-a',\n account,\n '-w',\n secret,\n '-U',\n ])\n if (!res.ok) {\n throw new Error(`macOS Keychain write failed: ${res.stderr.trim() || res.code}`)\n }\n },\n\n delete(account: string): void {\n // Ignore \"not found\" — delete is idempotent.\n run('security', ['delete-generic-password', '-s', SERVICE, '-a', account])\n },\n}\n","/**\n * Linux libsecret backend via the `secret-tool` CLI (GNOME Keyring / KWallet\n * through the Secret Service API). Secrets are encrypted at rest by the daemon.\n *\n * The secret is written to `secret-tool store` on stdin, never argv.\n */\nimport { run, commandExists } from './run.js'\nimport { SERVICE, type KeychainBackend } from './types.js'\n\nexport const libsecretKeychain: KeychainBackend = {\n name: 'libsecret',\n\n isAvailable(): boolean {\n return process.platform === 'linux' && commandExists('secret-tool', ['--version'])\n },\n\n get(account: string): string | null {\n const res = run('secret-tool', ['lookup', 'service', SERVICE, 'account', account])\n if (!res.ok) return null\n // secret-tool prints the secret with no trailing newline; be defensive.\n return res.stdout.replace(/\\n$/, '')\n },\n\n set(account: string, secret: string): void {\n const res = run(\n 'secret-tool',\n ['store', '--label', `${SERVICE}:${account}`, 'service', SERVICE, 'account', account],\n { input: secret }\n )\n if (!res.ok) {\n throw new Error(`libsecret write failed: ${res.stderr.trim() || res.code}`)\n }\n },\n\n delete(account: string): void {\n run('secret-tool', ['clear', 'service', SERVICE, 'account', account])\n },\n}\n","/**\n * Windows backend using DPAPI (Data Protection API) via PowerShell.\n *\n * Windows Credential Manager cannot be read back reliably from a plain\n * PowerShell session (no scriptable CredRead without P/Invoke), so — with no\n * native keychain dependency available in the workspace — we use DPAPI directly\n * through the .NET `System.Security.Cryptography.ProtectedData` API with\n * `CurrentUser` scope. (We deliberately avoid `ConvertTo-/ConvertFrom-\n * SecureString`, which live in the Microsoft.PowerShell.Security module: that\n * module fails to autoload for a `powershell.exe` spawned by node, so it is not\n * a reliable path. `ProtectedData` lives in the always-loadable System.Security\n * assembly.) The resulting ciphertext is bound to the current Windows user\n * account + machine and stored base64 in a sidecar file. The PLAINTEXT is never\n * written to disk: it is passed in and out of PowerShell through environment\n * variables (never argv, so it stays off the process command line).\n *\n * SPAWN BUDGET: availability is a spawn-free platform check — we no longer pay a\n * full DPAPI PROTECT round-trip just to probe. Every credentialed invocation\n * therefore costs exactly ONE `powershell.exe` spawn (the real get/set), not\n * two (probe + real). A machine that is win32 yet genuinely cannot run DPAPI is\n * handled lazily: the first real get/set that proves DPAPI unavailable downgrades\n * to the encrypted-file backend — the SAME fallback the resolver would have\n * chosen had isAvailable() returned false — instead of a pre-emptive probe.\n */\nimport fs from 'node:fs'\nimport { run } from './run.js'\nimport { ensureConfigDir, secretFile } from './paths.js'\nimport { encryptedFile } from './encrypted-file.js'\nimport { type KeychainBackend } from './types.js'\n\nconst EXT = 'dpapi'\n\nconst PROTECT =\n \"$ErrorActionPreference='Stop'; Add-Type -AssemblyName System.Security; \" +\n '$b=[Text.Encoding]::UTF8.GetBytes($env:DCS_KEYCHAIN_SECRET); ' +\n \"$p=[Security.Cryptography.ProtectedData]::Protect($b,$null,'CurrentUser'); \" +\n '[Convert]::ToBase64String($p)'\n\nconst UNPROTECT =\n \"$ErrorActionPreference='Stop'; Add-Type -AssemblyName System.Security; \" +\n '$p=[Convert]::FromBase64String($env:DCS_KEYCHAIN_ENC); ' +\n \"$b=[Security.Cryptography.ProtectedData]::Unprotect($p,$null,'CurrentUser'); \" +\n '[Text.Encoding]::UTF8.GetString($b)'\n\nfunction powershell(script: string, env?: NodeJS.ProcessEnv) {\n return run('powershell', ['-NoProfile', '-NonInteractive', '-Command', script], { env })\n}\n\n// Best-effort removal of an account's DPAPI sidecar. Extracted so both delete()\n// and the set()/downgrade paths can drop a superseded ciphertext without touching\n// the encrypted-file store.\nfunction deleteDpapiSidecar(account: string): void {\n try {\n fs.unlinkSync(secretFile(account, EXT))\n } catch {\n /* already absent */\n }\n}\n\n// Once a real DPAPI operation proves this box cannot run DPAPI, every subsequent\n// operation in the process delegates to the encrypted-file backend — the exact\n// fallback the resolver picks when isAvailable() returns false. This is flipped\n// only by an operation that CANNOT PRODUCE ciphertext (powershell absent, or a\n// PROTECT that returns nothing): those are unambiguous \"DPAPI is broken here\"\n// signals. It is NEVER flipped by a failed UNPROTECT of a specific ciphertext —\n// that is ambiguous (a foreign/tampered blob on a perfectly healthy box) and\n// must not orphan a good stored credential (W18 stale-credential seam).\nlet dpapiUnavailable = false\n\n// Test-only: reset the process-wide downgrade latch so a spec can exercise a\n// fresh backend without a full module reset. No production caller uses it.\nexport function __resetWindowsDpapiForTesting(): void {\n dpapiUnavailable = false\n}\n\nexport const windowsDpapi: KeychainBackend = {\n name: 'windows-dpapi',\n\n isAvailable(): boolean {\n // Spawn-free. DPAPI/ProtectedData ships with every supported Windows build,\n // so platform is a sufficient positive signal and we avoid the cost of a\n // PROTECT round-trip on every resolution. The rare win32-but-DPAPI-broken\n // box is caught by the get/set downgrade below, not by a probe.\n return process.platform === 'win32' && !dpapiUnavailable\n },\n\n get(account: string): string | null {\n if (dpapiUnavailable) return encryptedFile.get(account)\n\n let enc: string\n try {\n enc = fs.readFileSync(secretFile(account, EXT), 'utf8').trim()\n } catch {\n // No DPAPI sidecar. A prior process on a DPAPI-broken box may have\n // downgraded and stored the secret under the encrypted-file backend;\n // consult it (spawn-free) before reporting \"no credentials\".\n return encryptedFile.get(account)\n }\n if (!enc) return null\n\n const res = powershell(UNPROTECT, { DCS_KEYCHAIN_ENC: enc })\n if (!res.ok) {\n if (res.spawnError) {\n // powershell itself could not be launched → DPAPI is unavailable on this\n // box. Downgrade for the rest of the process and answer from the\n // encrypted-file backend.\n dpapiUnavailable = true\n return encryptedFile.get(account)\n }\n // Ciphertext unreadable (copied to another user/machine) or a transient\n // decrypt error. Treat as \"no credentials\" WITHOUT flipping the\n // process-wide downgrade and WITHOUT touching the stored blob — a later\n // successful read still works (W18 seam: transient failure never wipes).\n return null\n }\n return res.stdout.replace(/\\r?\\n$/, '')\n },\n\n set(account: string, secret: string): void {\n if (dpapiUnavailable) {\n encryptedFile.set(account, secret)\n // The encrypted-file backend now holds the authoritative secret. Drop any\n // stale DPAPI sidecar so a FRESH process (downgrade latch reset) cannot read\n // a superseded ciphertext back instead of this value (split-brain / stale\n // shadow). Strictly ordered AFTER the successful enc write, so this only ever\n // removes a copy the new write has already replaced — never the only copy.\n deleteDpapiSidecar(account)\n return\n }\n\n ensureConfigDir()\n const res = powershell(PROTECT, { DCS_KEYCHAIN_SECRET: secret })\n if (!res.ok || !res.stdout.trim()) {\n // PROTECT could not produce ciphertext on a freshly-supplied plaintext —\n // an unambiguous \"DPAPI is broken on this box\" signal (there is no foreign\n // blob to blame). Downgrade and persist via the encrypted-file backend,\n // reproducing exactly what the resolver would have done had isAvailable()\n // returned false.\n dpapiUnavailable = true\n encryptedFile.set(account, secret)\n // Same stale-shadow removal as the already-downgraded branch: now that the\n // new secret is durably in the encrypted-file store, drop the DPAPI sidecar\n // so the two backends stay mutually exclusive and a fresh process reads the\n // NEW value. This is NOT a transient-failure wipe — the delete happens\n // strictly AFTER the fallback write succeeded, so the credential is never\n // left with zero copies.\n deleteDpapiSidecar(account)\n return\n }\n fs.writeFileSync(secretFile(account, EXT), res.stdout.trim(), { encoding: 'utf8', mode: 0o600 })\n // DPAPI now holds the authoritative secret. Drop any encrypted-file copy a\n // prior downgrade may have left so the two backends stay mutually exclusive\n // and a later read cannot resurrect a stale .enc shadow. Ordered AFTER the\n // successful DPAPI write, so it never removes the only copy.\n encryptedFile.delete(account)\n },\n\n delete(account: string): void {\n // Remove both the DPAPI sidecar and any encrypted-file copy a downgrade may\n // have written, so a logout fully clears the credential regardless of which\n // backend ended up storing it.\n deleteDpapiSidecar(account)\n encryptedFile.delete(account)\n },\n}\n","/**\n * Filesystem location for the file-backed keychain backends (Windows DPAPI and\n * the encrypted-file fallback). This mirrors the app config directory used by\n * the `conf` package so a machine has a single DCS CLI state directory.\n *\n * Override with `DCS_CLI_CONFIG_DIR` (used by tests and power users).\n */\nimport os from 'node:os'\nimport path from 'node:path'\nimport fs from 'node:fs'\n\nconst APP_DIR = 'dcs-cli-nodejs'\n\nexport function configDir(): string {\n const override = process.env.DCS_CLI_CONFIG_DIR\n if (override) return override\n\n if (process.platform === 'win32') {\n const base = process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming')\n return path.join(base, APP_DIR)\n }\n if (process.platform === 'darwin') {\n return path.join(os.homedir(), 'Library', 'Application Support', APP_DIR)\n }\n const base = process.env.XDG_CONFIG_HOME || path.join(os.homedir(), '.config')\n return path.join(base, APP_DIR)\n}\n\n/** Absolute path to a per-account secret file with the given extension. */\nexport function secretFile(account: string, ext: string): string {\n const safe = account.replace(/[^a-zA-Z0-9._-]/g, '_')\n return path.join(configDir(), `${safe}.${ext}`)\n}\n\n/** Ensure the config directory exists and return it. */\nexport function ensureConfigDir(): string {\n const dir = configDir()\n fs.mkdirSync(dir, { recursive: true })\n return dir\n}\n","/**\n * Last-resort backend: an AES-256-GCM encrypted file keyed by a machine/user\n * derived key. Used only when no OS keychain (macOS Keychain, libsecret) and no\n * DPAPI is available — e.g. a headless Linux box without a Secret Service\n * daemon.\n *\n * SECURITY TRADEOFF (documented deliberately): the encryption key is derived\n * from the current username + machine hostname, which are discoverable by other\n * software running as the same user. This is stronger than the previous\n * plaintext JSON (it is not human-readable, resists casual disk inspection and\n * accidental commits/log capture) but it is NOT a substitute for an OS keychain\n * against a determined local attacker. It exists so the CLI never has to fall\n * back to writing a plaintext token.\n */\nimport fs from 'node:fs'\nimport os from 'node:os'\nimport crypto from 'node:crypto'\nimport { ensureConfigDir, secretFile } from './paths.js'\nimport { type KeychainBackend } from './types.js'\n\nconst EXT = 'enc'\nconst MAGIC = 'DCSK1' // versioned envelope tag\nconst STATIC_SALT = Buffer.from('dcs-cli-keychain-v1-salt', 'utf8')\n\nfunction deriveKey(): Buffer {\n let username = 'unknown'\n try {\n username = os.userInfo().username\n } catch {\n /* container without a passwd entry */\n }\n // The separators are unicode escapes for NUL (0x00) — written as escapes so\n // git treats this source file as text, not binary. They evaluate to the same\n // raw NUL bytes as before, so existing encrypted files keep decrypting.\n // Do NOT replace them with a visible character.\n const material = `${username}\\u0000${os.hostname()}\\u0000dcs-cli`\n return crypto.scryptSync(material, STATIC_SALT, 32)\n}\n\nexport const encryptedFile: KeychainBackend = {\n name: 'encrypted-file',\n\n isAvailable(): boolean {\n // Always available as the floor; the resolver only reaches it when the OS\n // backends decline.\n return true\n },\n\n get(account: string): string | null {\n let raw: string\n try {\n raw = fs.readFileSync(secretFile(account, EXT), 'utf8').trim()\n } catch {\n return null\n }\n const parts = raw.split(':')\n if (parts.length !== 4 || parts[0] !== MAGIC) return null\n try {\n const iv = Buffer.from(parts[1], 'base64')\n const tag = Buffer.from(parts[2], 'base64')\n const ciphertext = Buffer.from(parts[3], 'base64')\n const decipher = crypto.createDecipheriv('aes-256-gcm', deriveKey(), iv)\n decipher.setAuthTag(tag)\n const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()])\n return plaintext.toString('utf8')\n } catch {\n // Wrong key (moved machine/user) or tampered file → treat as absent.\n return null\n }\n },\n\n set(account: string, secret: string): void {\n ensureConfigDir()\n const iv = crypto.randomBytes(12)\n const cipher = crypto.createCipheriv('aes-256-gcm', deriveKey(), iv)\n const ciphertext = Buffer.concat([cipher.update(secret, 'utf8'), cipher.final()])\n const tag = cipher.getAuthTag()\n const envelope = [\n MAGIC,\n iv.toString('base64'),\n tag.toString('base64'),\n ciphertext.toString('base64'),\n ].join(':')\n fs.writeFileSync(secretFile(account, EXT), envelope, { encoding: 'utf8', mode: 0o600 })\n },\n\n delete(account: string): void {\n try {\n fs.unlinkSync(secretFile(account, EXT))\n } catch {\n /* already absent */\n }\n },\n}\n","/**\n * Keychain resolution.\n *\n * Picks the best available secret store for the current machine:\n * macOS → Keychain (`security`)\n * Linux → libsecret (`secret-tool`) when present\n * Windows→ DPAPI (PowerShell)\n * else → encrypted-file fallback (never plaintext)\n *\n * The chosen backend is memoized. Tests inject a fake via\n * `__setKeychainForTesting`.\n */\nimport { macosKeychain } from './macos.js'\nimport { libsecretKeychain } from './libsecret.js'\nimport { windowsDpapi } from './windows.js'\nimport { encryptedFile } from './encrypted-file.js'\nimport { type KeychainBackend } from './types.js'\n\nexport { type KeychainBackend } from './types.js'\nexport { createInMemoryKeychain } from './memory.js'\n\nconst CANDIDATES: KeychainBackend[] = [macosKeychain, libsecretKeychain, windowsDpapi]\n\nlet resolved: KeychainBackend | undefined\nlet override: KeychainBackend | null = null\n\nfunction resolve(): KeychainBackend {\n for (const backend of CANDIDATES) {\n try {\n if (backend.isAvailable()) return backend\n } catch {\n /* probing failed → try the next candidate */\n }\n }\n return encryptedFile\n}\n\n/** Return the active keychain backend for this process (memoized). */\nexport function getKeychain(): KeychainBackend {\n if (override) return override\n if (!resolved) resolved = resolve()\n return resolved\n}\n\n/** Force a specific backend (tests only). Pass null to restore auto-resolve. */\nexport function __setKeychainForTesting(backend: KeychainBackend | null): void {\n override = backend\n resolved = undefined\n}\n","/**\n * Sites commands for the CLI.\n */\n\nimport chalk from 'chalk'\nimport Table from 'cli-table3'\nimport { getPortalClient, type Site } from '../api/portal-client.js'\nimport { hasStoredSession } from '../auth/credentials.js'\n\n/**\n * List sites command - display sites the user has access to.\n */\nexport async function listSitesCommand(): Promise<void> {\n if (!hasStoredSession()) {\n console.log(chalk.yellow('Not logged in.'))\n console.log('Run', chalk.cyan('dcs login'), 'to authenticate.')\n process.exit(1)\n }\n\n const client = getPortalClient()\n\n try {\n const { sites } = await client.listSites()\n\n if (sites.length === 0) {\n console.log(chalk.yellow('No sites found.'))\n console.log('Ask your company admin to grant you access to sites.')\n return\n }\n\n console.log(chalk.bold('\\nYour Sites:\\n'))\n\n const table = new Table({\n head: [\n chalk.cyan('Slug'),\n chalk.cyan('Name'),\n chalk.cyan('Role'),\n chalk.cyan('Company'),\n chalk.cyan('URL'),\n ],\n style: {\n head: [],\n border: [],\n },\n })\n\n for (const site of sites) {\n table.push([\n site.slug,\n site.name,\n formatRole(site.role),\n site.companyName,\n site.productionUrl || chalk.dim('(not deployed)'),\n ])\n }\n\n console.log(table.toString())\n console.log()\n console.log(chalk.dim(`Total: ${sites.length} site(s)`))\n } catch (error) {\n if (error instanceof Error) {\n console.error(chalk.red('Error:'), error.message)\n }\n process.exit(1)\n }\n}\n\n/**\n * Show site details command.\n */\nexport async function showSiteCommand(slug: string): Promise<void> {\n if (!hasStoredSession()) {\n console.log(chalk.yellow('Not logged in.'))\n console.log('Run', chalk.cyan('dcs login'), 'to authenticate.')\n process.exit(1)\n }\n\n const client = getPortalClient()\n\n try {\n const site = await client.getSite(slug)\n displaySiteDetails(site)\n } catch (error) {\n if (error instanceof Error) {\n console.error(chalk.red('Error:'), error.message)\n }\n process.exit(1)\n }\n}\n\nfunction formatRole(role: string): string {\n switch (role) {\n case 'owner':\n return chalk.magenta(role)\n case 'admin':\n return chalk.red(role)\n case 'editor':\n return chalk.yellow(role)\n case 'viewer':\n return chalk.blue(role)\n default:\n return role\n }\n}\n\nfunction displaySiteDetails(site: Site): void {\n console.log()\n console.log(chalk.bold('Site Details'))\n console.log()\n console.log(' Slug: ', chalk.cyan(site.slug))\n console.log(' Name: ', site.name)\n console.log(' Role: ', formatRole(site.role))\n console.log(' Company: ', site.companyName)\n console.log(' Company ID: ', chalk.dim(site.companyId))\n if (site.productionUrl) {\n console.log(' URL: ', chalk.green(site.productionUrl))\n } else {\n console.log(' URL: ', chalk.dim('(not deployed)'))\n }\n console.log()\n}\n","/**\n * Portal API client for CLI operations.\n */\n\nimport { getAccessToken, getRefreshToken, updateAccessToken, isAuthenticated, clearCredentials } from '../auth/credentials.js'\nimport { refreshAccessToken, RefreshTokenRevokedError, ReauthRequiredError } from '../auth/device-flow.js'\n\nconst DEFAULT_API_URL = 'https://portal.duffcloudservices.com'\n\nexport interface Site {\n slug: string\n name: string\n role: 'owner' | 'admin' | 'editor' | 'viewer'\n companyId: string\n companyName: string\n productionUrl?: string\n}\n\nexport interface PortalClientOptions {\n apiUrl?: string\n}\n\nexport class PortalClient {\n private apiUrl: string\n\n constructor(options: PortalClientOptions = {}) {\n this.apiUrl = options.apiUrl || process.env.DCS_API_URL || DEFAULT_API_URL\n }\n\n /**\n * Refresh the session using the stored refresh token. Exactly one attempt —\n * never a retry loop. On a revoked/expired refresh token the dead local\n * session is dropped and a clean re-auth is demanded; transient failures\n * (network/5xx/429) keep the session and surface a retryable error.\n */\n private async refreshSession(): Promise<string> {\n const refreshToken = getRefreshToken()!\n try {\n const { accessToken: newToken, expiresIn } = await refreshAccessToken(refreshToken, {\n apiUrl: this.apiUrl,\n })\n updateAccessToken(newToken, expiresIn)\n return newToken\n } catch (error) {\n if (error instanceof RefreshTokenRevokedError) {\n // The refresh token is dead server-side (expired/revoked). Drop the\n // stale local session and prompt a clean re-auth.\n clearCredentials()\n throw new ReauthRequiredError()\n }\n // Transient failure (network/5xx) — keep the session, surface a\n // retryable error rather than forcing re-login.\n throw new Error(\n `Could not reach the DCS Portal to refresh your session: ${\n error instanceof Error ? error.message : String(error)\n }`\n )\n }\n }\n\n /**\n * Make an authenticated API request.\n */\n private async request<T>(\n path: string,\n options: RequestInit = {}\n ): Promise<T> {\n // Get access token\n let accessToken = getAccessToken()\n\n // Refresh a stale access token before spending a doomed request on it.\n let refreshedThisRequest = false\n if (!isAuthenticated() && getRefreshToken()) {\n accessToken = await this.refreshSession()\n refreshedThisRequest = true\n }\n\n if (!accessToken) {\n throw new ReauthRequiredError('Not authenticated. Run `dcs login` to sign in.')\n }\n\n const send = (token: string) =>\n fetch(`${this.apiUrl}${path}`, {\n ...options,\n headers: {\n ...options.headers,\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n })\n\n let response = await send(accessToken)\n\n // A 401 on a token we believed was valid usually means it was revoked or\n // clock-skewed server-side. Try ONE refresh-then-retry before giving up —\n // but only if we did not already refresh for this request (no loops).\n if (response.status === 401 && !refreshedThisRequest && getRefreshToken()) {\n const newToken = await this.refreshSession()\n response = await send(newToken)\n }\n\n if (response.status === 401) {\n // Rejected even after any refresh above — force re-auth.\n clearCredentials()\n throw new ReauthRequiredError()\n }\n\n if (!response.ok) {\n const error = await response.text()\n throw new Error(`API error (${response.status}): ${error}`)\n }\n\n return response.json()\n }\n\n /**\n * List sites accessible to the authenticated user.\n */\n async listSites(): Promise<{ sites: Site[] }> {\n return this.request('/api/v1/cli/sites')\n }\n\n /**\n * Get details for a specific site.\n */\n async getSite(slug: string): Promise<Site> {\n return this.request(`/api/v1/cli/sites/${slug}`)\n }\n\n /**\n * Validate that the user can create/modify a site.\n */\n async validateSiteAccess(\n slug: string,\n operation: 'read' | 'edit' | 'create'\n ): Promise<{ valid: boolean; role?: string; message?: string }> {\n try {\n const { sites } = await this.listSites()\n const site = sites.find((s) => s.slug === slug)\n\n if (!site) {\n if (operation === 'create') {\n // Check if user has any admin access to create new sites\n const canCreate = sites.some((s) => ['owner', 'admin'].includes(s.role))\n return canCreate\n ? { valid: true, message: 'New site will be created' }\n : { valid: false, message: 'You need admin access in a company to create new sites' }\n }\n return { valid: false, message: `Site '${slug}' not found or you don't have access` }\n }\n\n if (operation === 'read') {\n return { valid: true, role: site.role }\n }\n\n if (operation === 'edit') {\n const canEdit = ['owner', 'admin', 'editor'].includes(site.role)\n return canEdit\n ? { valid: true, role: site.role }\n : { valid: false, role: site.role, message: 'You need editor access to modify this site' }\n }\n\n if (operation === 'create') {\n return { valid: false, message: 'Site already exists' }\n }\n\n return { valid: false }\n } catch (error) {\n if (error instanceof Error) {\n return { valid: false, message: error.message }\n }\n return { valid: false, message: 'Unknown error' }\n }\n }\n}\n\n// Singleton instance\nlet clientInstance: PortalClient | null = null\n\nexport function getPortalClient(options?: PortalClientOptions): PortalClient {\n if (!clientInstance) {\n clientInstance = new PortalClient(options)\n }\n return clientInstance\n}\n","/**\n * Init command - initialize DCS integration for a customer site.\n */\n\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\nimport chalk from 'chalk'\nimport ora from 'ora'\nimport { getPortalClient } from '../api/portal-client.js'\nimport { hasStoredSession } from '../auth/credentials.js'\nimport siteDeployTemplate from '../templates/site-deploy.yml'\n\nexport interface InitOptions {\n siteSlug: string\n siteName: string\n framework?: 'vue' | 'astro'\n target?: string\n dryRun?: boolean\n force?: boolean\n}\n\n/**\n * Initialize DCS integration for a customer site.\n */\nexport async function initCommand(options: InitOptions): Promise<void> {\n const { siteSlug, siteName, framework = 'vue', target = '.', dryRun = false, force = false } = options\n\n // 1. Check authentication\n if (!hasStoredSession()) {\n console.log(chalk.yellow('Not logged in.'))\n console.log('Run', chalk.cyan('dcs login'), 'to authenticate.')\n process.exit(1)\n }\n\n // 2. Validate site access\n const spinner = ora('Validating site access...').start()\n const client = getPortalClient()\n\n try {\n const { sites } = await client.listSites()\n const existingSite = sites.find((s) => s.slug === siteSlug)\n\n if (existingSite) {\n if (!['owner', 'admin', 'editor'].includes(existingSite.role)) {\n spinner.fail(`You don't have edit access to site '${siteSlug}'`)\n process.exit(1)\n }\n spinner.succeed(`Site '${siteSlug}' found (role: ${existingSite.role})`)\n } else {\n // Check if user can create new sites\n const canCreate = sites.some((s) => ['owner', 'admin'].includes(s.role))\n if (!canCreate) {\n spinner.fail('You need admin access in a company to create new sites')\n process.exit(1)\n }\n spinner.info(`Creating new site: ${siteSlug}`)\n }\n } catch (error) {\n spinner.fail('Failed to validate site access')\n if (error instanceof Error) {\n console.error(chalk.red('Error:'), error.message)\n }\n process.exit(1)\n }\n\n // 3. Resolve target directory\n const targetDir = path.resolve(target)\n console.log(chalk.dim(`Target directory: ${targetDir}`))\n\n // 4. Generate configuration files\n const generatedFiles: string[] = []\n const timestamp = new Date().toISOString()\n\n try {\n // .dcs/site.yaml\n const siteYaml = generateSiteYaml({ siteSlug, siteName, timestamp, framework })\n generatedFiles.push(await writeFile(targetDir, '.dcs/site.yaml', siteYaml, { dryRun, force }))\n\n // .dcs/pages.yaml\n const pagesYaml = generatePagesYaml({ siteSlug, timestamp })\n generatedFiles.push(await writeFile(targetDir, '.dcs/pages.yaml', pagesYaml, { dryRun, force }))\n\n // .dcs/content.yaml\n const contentYaml = generateContentYaml({ timestamp })\n generatedFiles.push(await writeFile(targetDir, '.dcs/content.yaml', contentYaml, { dryRun, force }))\n\n // .dcs/seo.yaml\n const seoYaml = generateSeoYaml({ siteName, timestamp })\n generatedFiles.push(await writeFile(targetDir, '.dcs/seo.yaml', seoYaml, { dryRun, force }))\n\n // .dcs/SECTION-CONVENTIONS.md\n const sectionConventions = generateSectionConventions()\n generatedFiles.push(\n await writeFile(targetDir, '.dcs/SECTION-CONVENTIONS.md', sectionConventions, { dryRun, force })\n )\n\n // .github/copilot-instructions.md\n const copilotInstructions = generateCopilotInstructions({ siteName })\n generatedFiles.push(\n await writeFile(targetDir, '.github/copilot-instructions.md', copilotInstructions, { dryRun, force })\n )\n\n // .github/workflows/site-deploy.yml\n const deployWorkflow = generateDeployWorkflow()\n generatedFiles.push(\n await writeFile(targetDir, '.github/workflows/site-deploy.yml', deployWorkflow, { dryRun, force })\n )\n\n // .plans/README.md\n const plansReadme = generatePlansReadme({ siteName, framework })\n generatedFiles.push(await writeFile(targetDir, '.plans/README.md', plansReadme, { dryRun, force }))\n\n // .plans/00-audit-site.md\n const plan00 = generatePlan00AuditSite({ framework })\n generatedFiles.push(await writeFile(targetDir, '.plans/00-audit-site.md', plan00, { dryRun, force }))\n\n // .plans/01-integrate-content.md\n const plan01 = generatePlan01IntegrateContent()\n generatedFiles.push(await writeFile(targetDir, '.plans/01-integrate-content.md', plan01, { dryRun, force }))\n\n // .plans/02-configure-seo.md\n const plan02 = generatePlan02ConfigureSeo()\n generatedFiles.push(await writeFile(targetDir, '.plans/02-configure-seo.md', plan02, { dryRun, force }))\n\n } catch (error) {\n if (error instanceof Error) {\n console.error(chalk.red('Error:'), error.message)\n }\n process.exit(1)\n }\n\n // 5. Display results\n console.log()\n console.log(chalk.bold('Generated files:'))\n for (const file of generatedFiles) {\n console.log(` ${chalk.green('✓')} ${file}`)\n }\n\n // 6. Post-init instructions\n console.log()\n console.log(chalk.bold('Next steps:'))\n console.log(' 1. Install CMS package:', chalk.cyan('pnpm add @duffcloudservices/cms'))\n console.log(' 2. Configure Vite plugins in your build config')\n console.log(' 3. Follow the plans in', chalk.cyan('.plans/'), 'directory')\n console.log()\n}\n\n// =============================================================================\n// File Generation Helpers\n// =============================================================================\n\ninterface WriteOptions {\n dryRun: boolean\n force: boolean\n}\n\nasync function writeFile(\n targetDir: string,\n relativePath: string,\n content: string,\n options: WriteOptions\n): Promise<string> {\n const fullPath = path.join(targetDir, relativePath)\n\n if (options.dryRun) {\n console.log(chalk.dim(`Would create: ${relativePath}`))\n return relativePath\n }\n\n // Check if file exists\n try {\n await fs.access(fullPath)\n if (!options.force) {\n console.log(chalk.yellow(`Skipped (exists): ${relativePath}`))\n return relativePath\n }\n } catch {\n // File doesn't exist, continue\n }\n\n // Create directory if needed\n await fs.mkdir(path.dirname(fullPath), { recursive: true })\n\n // Write file\n await fs.writeFile(fullPath, content, 'utf8')\n\n return relativePath\n}\n\n// =============================================================================\n// Template Generators\n// =============================================================================\n\nfunction generateSiteYaml(data: {\n siteSlug: string\n siteName: string\n timestamp: string\n framework: string\n}): string {\n return `# DCS Site Configuration\n# This file defines the site identity and Azure resource bindings.\n# Generated by: @duffcloudservices/cli\n\nsite_name: ${data.siteName}\nsite_slug: ${data.siteSlug}\n\n# Azure Static Web App resource ID\n# Set this after provisioning the SWA resource in Azure\nswa_resource_id: \"\"\n\n# URLs are populated after first deployment\nproduction_url: \"\"\npreview_url: \"\"\n\n# Google Analytics Configuration\ngoogle_analytics_id: \"\"\n\n# Azure Authentication (Managed Identity)\n# These values are used by GitHub Actions for OIDC authentication\nazure:\n client_id: \"\"\n tenant_id: \"\"\n subscription_id: \"\"\n\n# Portal API Configuration\nportal_api_url: \"https://portal.duffcloudservices.com\"\n\n# Site metadata for portal\nmetadata:\n framework: ${data.framework}\n managed_by: dcs\n created_at: \"${data.timestamp}\"\n`\n}\n\nfunction generatePagesYaml(data: { siteSlug: string; timestamp: string }): string {\n return `# .dcs/pages.yaml\n# Page registry for DCS CMS integration and snapshot capture.\n# Generated by: @duffcloudservices/cli\n#\n# This file defines page metadata only. Text content is stored in content.yaml\n# and text keys are auto-discovered during snapshot capture.\n\nversion: 3\nsiteSlug: ${data.siteSlug}\nlastUpdated: \"${data.timestamp}\"\ngeneratedBy: dcs-cli\n\npages:\n # Home page - always protected\n - slug: home\n path: /\n type: static\n title: Home\n deletable: false\n\n# Excluded paths - never captured\nexcluded:\n - /404\n - /dev-*\n - /_*\n\n# Snapshot configuration\nsnapshot:\n viewport:\n width: 1280\n height: 720\n waitAfterLoad: 3000\n captureFullPage: true\n`\n}\n\nfunction generateContentYaml(data: { timestamp: string }): string {\n return `# .dcs/content.yaml\n# Managed by DCS Portal - Do not edit manually\n# This file contains all text content for the site.\n#\n# Content is organized by page slug, with each page containing\n# key-value pairs for editable text. The \\`global\\` section contains\n# text shared across all pages (navigation, footer, etc.).\n\nversion: 1\nlastUpdated: \"${data.timestamp}\"\nupdatedBy: \"dcs-cli\"\n\n# Global text content (shared across all pages)\nglobal: {}\n\n# Page-specific text content\npages: {}\n`\n}\n\nfunction generateSeoYaml(data: { siteName: string; timestamp: string }): string {\n return `# .dcs/seo.yaml\n# Managed by DCS Portal - Do not edit manually\n# SEO configuration for all pages and global defaults.\n\nversion: 1\nlastUpdated: \"${data.timestamp}\"\nupdatedBy: \"dcs-cli\"\n\n# Global/site-wide SEO defaults\nglobal:\n siteName: \"${data.siteName}\"\n siteUrl: \"\" # Set your production URL\n locale: en_US\n defaultTitle: \"${data.siteName}\"\n defaultDescription: \"\"\n titleTemplate: \"%s | ${data.siteName}\"\n\n # Social media handles\n social:\n twitter: \"\"\n linkedin: \"\"\n\n # Default images for social sharing\n images:\n logo: \"\"\n ogDefault: \"\"\n twitterDefault: \"\"\n\n # Default robots directive\n robots: \"index, follow\"\n\n# Page-specific SEO configurations\npages:\n home:\n title: Home\n description: \"\"\n noTitleTemplate: true\n openGraph:\n type: website\n twitter:\n card: summary_large_image\n`\n}\n\nfunction generateSectionConventions(): string {\n return `# Section Conventions for DCS CMS Integration\n\nThis document describes the HTML attributes used by the DCS visual page editor\nfor section identification and snapshot capture.\n\n## Required Attributes\n\n### \\`data-dcs-section\\`\n\nMarks an element as an editable section. The value should match the section's\nidentifier in content.yaml.\n\n\\`\\`\\`html\n<section data-dcs-section=\"hero\">\n <!-- Section content -->\n</section>\n\\`\\`\\`\n\n### \\`data-dcs-text\\`\n\nMarks an element's text as editable via the portal. The value should be the\ntext key from content.yaml.\n\n\\`\\`\\`html\n<h1 data-dcs-text=\"hero.title\">{{ t('hero.title') }}</h1>\n\\`\\`\\`\n\n## Best Practices\n\n1. Use semantic section names that describe the content\n2. Keep text keys hierarchical (section.element)\n3. Ensure all user-editable text has a data-dcs-text attribute\n4. Don't nest sections unnecessarily\n`\n}\n\nfunction generateCopilotInstructions(data: { siteName: string }): string {\n return `# GitHub Copilot Instructions for ${data.siteName}\n\n## Project Overview\n\nThis is a **DCS-managed customer site**. Content and feature changes are managed\nthrough the DCS Portal at https://portal.duffcloudservices.com.\n\n## Technology Stack\n\n- Vue 3 + Composition API\n- VitePress (or Vite)\n- @duffcloudservices/cms for content management\n- Azure Static Web Apps for hosting\n\n## Content Management\n\n### Text Content\n\nUse the \\`useTextContent\\` composable for all editable text:\n\n\\`\\`\\`vue\n<script setup lang=\"ts\">\nimport { useTextContent } from '@duffcloudservices/cms'\n\nconst { t } = useTextContent({\n pageSlug: 'home',\n defaults: {\n 'hero.title': 'Default Title',\n 'hero.subtitle': 'Default subtitle'\n }\n})\n</script>\n\n<template>\n <h1 data-dcs-text=\"hero.title\">{{ t('hero.title') }}</h1>\n <p data-dcs-text=\"hero.subtitle\">{{ t('hero.subtitle') }}</p>\n</template>\n\\`\\`\\`\n\n### SEO\n\nUse the \\`useSEO\\` composable for page SEO:\n\n\\`\\`\\`vue\n<script setup lang=\"ts\">\nimport { useSEO } from '@duffcloudservices/cms'\n\nconst { applyHead } = useSEO('home')\napplyHead()\n</script>\n\\`\\`\\`\n\n## Key Conventions\n\n1. All user-editable text must use \\`useTextContent\\`\n2. All pages must have SEO configured via \\`useSEO\\`\n3. Section elements need \\`data-dcs-section\\` attributes\n4. Text elements need \\`data-dcs-text\\` attributes\n5. See \\`.dcs/SECTION-CONVENTIONS.md\\` for details\n`\n}\n\nfunction generatePlansReadme(data: { siteName: string; framework: string }): string {\n return `# DCS Integration Plans for ${data.siteName}\n\nThis directory contains step-by-step plans for integrating this site with the\nDCS content management system.\n\n## Overview\n\nThese plans guide AI assistants (like GitHub Copilot) through the integration\nprocess. Follow them in order for best results.\n\n## Plans\n\n1. **00-audit-site.md** - Analyze current site structure\n2. **01-create-use-text-content.md** - Set up the text content composable\n3. **02-integrate-home-page.md** - Add CMS integration to home page\n4. **03-integrate-remaining-pages.md** - Complete other pages\n5. **04-capture-snapshots.md** - Configure visual editor snapshots\n6. **05-verify-deployment.md** - Test and verify deployment\n\n## Framework\n\nThis site uses: **${data.framework}**\n\n## Getting Started\n\n1. Open plan 00 and ask Copilot to execute it\n2. Review the output and make any needed adjustments\n3. Continue with subsequent plans\n\n## Configuration Files\n\n- \\`.dcs/site.yaml\\` - Site identity and Azure config\n- \\`.dcs/pages.yaml\\` - Page registry for CMS\n- \\`.dcs/content.yaml\\` - Text content (managed by Portal)\n- \\`.dcs/seo.yaml\\` - SEO configuration (managed by Portal)\n`\n}\n\nfunction generateDeployWorkflow(): string {\n return siteDeployTemplate\n}\n\nfunction generatePlan00AuditSite(data: { framework: string }): string {\n return `# Plan 00: Audit Site Structure\n\n## Objective\n\nAnalyze the current site structure to understand what pages exist, their routes,\nand identify all text content that should be managed via DCS.\n\n## Framework\n\nThis site uses: **${data.framework}**\n\n## Tasks\n\n### 1. List All Pages\n\nIdentify all page components/routes in the project:\n\n- For Vue/VitePress: Look in \\`src/views/\\`, \\`src/pages/\\`, or \\`.vitepress/\\`\n- For Astro: Look in \\`src/pages/\\`\n\nCreate a list of:\n- Route path (e.g., \\`/\\`, \\`/about\\`, \\`/services\\`)\n- Component file path\n- Page title (from existing meta or content)\n\n### 2. Identify Text Content\n\nFor each page, list all user-facing text that should be editable:\n\n- Headings (h1, h2, etc.)\n- Paragraphs and body text\n- Button labels\n- Navigation links\n- Footer content\n\nUse this naming convention for text keys:\n\\`\\`\\`\n{pageSlug}.{section}.{element}\n\nExamples:\n- home.hero.title\n- home.hero.subtitle\n- home.cta.buttonText\n- about.intro.heading\n\\`\\`\\`\n\n### 3. Update pages.yaml\n\nAdd all discovered pages to \\`.dcs/pages.yaml\\`:\n\n\\`\\`\\`yaml\npages:\n - slug: home\n path: /\n type: static\n title: Home\n deletable: false\n \n - slug: about\n path: /about\n type: static\n title: About Us\n deletable: true\n\\`\\`\\`\n\n### 4. Document Findings\n\nCreate a brief summary of:\n- Total number of pages\n- Estimated number of text keys per page\n- Any complex sections (carousels, tabs, etc.)\n- Shared components that contain text (header, footer)\n\n## Output\n\nAfter completing this plan, you should have:\n1. Updated \\`.dcs/pages.yaml\\` with all pages\n2. A mental map of text content to migrate\n3. Understanding of component structure\n`\n}\n\nfunction generatePlan01IntegrateContent(): string {\n return `# Plan 01: Integrate Text Content\n\n## Objective\n\nSet up the \\`useTextContent\\` composable and migrate all static text to the\nDCS content management system.\n\n## Prerequisites\n\n- Completed Plan 00 (site audit)\n- \\`@duffcloudservices/cms\\` package installed\n\n## Tasks\n\n### 1. Install DCS CMS Package\n\n\\`\\`\\`bash\npnpm add @duffcloudservices/cms\n\\`\\`\\`\n\n### 2. Configure Vite Plugin\n\nAdd the DCS content plugin to your Vite config:\n\n\\`\\`\\`typescript\n// vite.config.ts\nimport { dcsContentPlugin } from '@duffcloudservices/cms/vite'\n\nexport default defineConfig({\n plugins: [\n vue(),\n dcsContentPlugin({\n contentPath: '.dcs/content.yaml',\n }),\n ],\n})\n\\`\\`\\`\n\n### 3. Integrate Home Page\n\nStart with the home page as a template:\n\n\\`\\`\\`vue\n<script setup lang=\"ts\">\nimport { useTextContent } from '@duffcloudservices/cms'\n\nconst { t } = useTextContent({\n pageSlug: 'home',\n defaults: {\n 'hero.title': 'Your Default Title',\n 'hero.subtitle': 'Your default subtitle text',\n 'hero.cta': 'Get Started',\n }\n})\n</script>\n\n<template>\n <section data-dcs-section=\"hero\">\n <h1 data-dcs-text=\"hero.title\">{{ t('hero.title') }}</h1>\n <p data-dcs-text=\"hero.subtitle\">{{ t('hero.subtitle') }}</p>\n <button data-dcs-text=\"hero.cta\">{{ t('hero.cta') }}</button>\n </section>\n</template>\n\\`\\`\\`\n\n### 4. Key Patterns\n\n**Text Keys**: Use hierarchical naming\n- \\`{section}.{element}\\` for page-specific content\n- Prefix with \\`global.\\` for shared content\n\n**Default Values**: Always provide defaults that match current content\n- This ensures the site works before CMS content is loaded\n- Defaults serve as fallback if content fetch fails\n\n**Data Attributes**: Required for visual editor\n- \\`data-dcs-section\\` on section containers\n- \\`data-dcs-text\\` on text elements (value = text key)\n\n### 5. Migrate Remaining Pages\n\nApply the same pattern to all pages identified in Plan 00.\n\nFor each page:\n1. Import \\`useTextContent\\`\n2. Define defaults for all text content\n3. Replace static text with \\`{{ t('key') }}\\`\n4. Add \\`data-dcs-text\\` attributes\n\n### 6. Handle Global Content\n\nCreate a shared composable for navigation/footer:\n\n\\`\\`\\`typescript\n// composables/useGlobalContent.ts\nimport { useTextContent } from '@duffcloudservices/cms'\n\nexport function useGlobalContent() {\n return useTextContent({\n pageSlug: 'global',\n defaults: {\n 'nav.home': 'Home',\n 'nav.about': 'About',\n 'footer.copyright': '© 2025 Company Name',\n }\n })\n}\n\\`\\`\\`\n\n## Verification\n\nAfter completing this plan:\n1. Run \\`pnpm dev\\` - site should display normally with defaults\n2. Check browser console for any missing text key warnings\n3. Verify all text elements have \\`data-dcs-text\\` attributes\n`\n}\n\nfunction generatePlan02ConfigureSeo(): string {\n return `# Plan 02: Configure SEO\n\n## Objective\n\nSet up the \\`useSEO\\` composable to manage page metadata through DCS Portal.\n\n## Prerequisites\n\n- Completed Plan 01 (content integration)\n- \\`@duffcloudservices/cms\\` package installed\n\n## Tasks\n\n### 1. Configure Vite Plugin\n\nAdd the DCS SEO plugin to your Vite config:\n\n\\`\\`\\`typescript\n// vite.config.ts\nimport { dcsContentPlugin, dcsSeoPlugin } from '@duffcloudservices/cms/vite'\n\nexport default defineConfig({\n plugins: [\n vue(),\n dcsContentPlugin({ contentPath: '.dcs/content.yaml' }),\n dcsSeoPlugin({ seoPath: '.dcs/seo.yaml' }),\n ],\n})\n\\`\\`\\`\n\n### 2. Add SEO to Pages\n\nFor each page, add the \\`useSEO\\` composable:\n\n\\`\\`\\`vue\n<script setup lang=\"ts\">\nimport { useTextContent, useSEO } from '@duffcloudservices/cms'\n\n// Content setup\nconst { t } = useTextContent({ pageSlug: 'home', defaults: { ... } })\n\n// SEO setup - applies head tags automatically\nconst { applyHead } = useSEO('home')\napplyHead()\n</script>\n\\`\\`\\`\n\n### 3. Configure Default SEO\n\nUpdate \\`.dcs/seo.yaml\\` with site-wide defaults:\n\n\\`\\`\\`yaml\nglobal:\n siteName: \"Your Site Name\"\n siteUrl: \"https://yoursite.com\"\n locale: en_US\n defaultTitle: \"Your Site Name\"\n defaultDescription: \"Your site description for search engines\"\n titleTemplate: \"%s | Your Site Name\"\n \n social:\n twitter: \"@yourhandle\"\n linkedin: \"company/yourcompany\"\n \n images:\n logo: \"/images/logo.png\"\n ogDefault: \"/images/og-default.png\"\n\npages:\n home:\n title: Home\n description: \"Welcome to our site\"\n noTitleTemplate: true # Home page often skips \" | Site Name\"\n openGraph:\n type: website\n twitter:\n card: summary_large_image\n \n about:\n title: About Us\n description: \"Learn more about our company\"\n\\`\\`\\`\n\n### 4. Page-Specific Overrides\n\nEach page can have custom SEO settings that override globals:\n\n\\`\\`\\`yaml\npages:\n services:\n title: Our Services\n description: \"Explore our professional services\"\n canonical: \"https://yoursite.com/services\"\n openGraph:\n title: \"Services - Custom OG Title\"\n description: \"Custom description for social sharing\"\n image: \"/images/services-og.png\"\n\\`\\`\\`\n\n### 5. Dynamic Pages (Blogs, etc.)\n\nFor dynamic pages like blog posts, you'll pass SEO data dynamically:\n\n\\`\\`\\`vue\n<script setup lang=\"ts\">\nimport { useSEO } from '@duffcloudservices/cms'\n\nconst props = defineProps<{ post: BlogPost }>()\n\nconst { applyHead } = useSEO('blog-post', {\n title: props.post.title,\n description: props.post.excerpt,\n openGraph: {\n type: 'article',\n image: props.post.featuredImage,\n }\n})\napplyHead()\n</script>\n\\`\\`\\`\n\n## Verification\n\nAfter completing this plan:\n1. View page source - check for meta tags\n2. Use browser dev tools to inspect \\`<head>\\`\n3. Test with social sharing debuggers (Facebook, Twitter, LinkedIn)\n4. Validate with Google's Rich Results Test\n`\n}\n","# Site Deployment Workflow\n# Generated by @duffcloudservices/cli\n# This workflow deploys the site to Azure Static Web Apps using OIDC authentication.\n\nname: Site Deployment\n\non:\n push:\n branches:\n - 'release/**'\n - 'master'\n workflow_dispatch:\n inputs:\n trigger_reason:\n description: 'Reason for manual deployment'\n required: false\n type: string\n default: 'Manual deployment'\n\n# Prevent concurrent deployments for the same branch\nconcurrency:\n group: site-deploy-${{ github.ref }}\n cancel-in-progress: false\n\npermissions:\n contents: read\n issues: write\n pull-requests: read\n id-token: write # Required for Azure OIDC authentication\n\njobs:\n deploy:\n name: Deploy to Azure Static Web App\n runs-on: ubuntu-latest\n # Skip deployment on branch creation events\n if: github.event.before != '0000000000000000000000000000000000000000'\n\n steps:\n - name: Determine deployment environment\n id: environment\n run: |\n BRANCH=\"${{ github.ref_name }}\"\n echo \"Branch: $BRANCH\"\n \n if [ \"$BRANCH\" == \"master\" ]; then\n echo \"environment=Production\" >> $GITHUB_OUTPUT\n echo \"environment_name=Production\" >> $GITHUB_OUTPUT\n echo \"swa_environment=\" >> $GITHUB_OUTPUT\n else\n echo \"environment=preview\" >> $GITHUB_OUTPUT\n echo \"environment_name=Preview\" >> $GITHUB_OUTPUT\n echo \"swa_environment=preview\" >> $GITHUB_OUTPUT\n fi\n\n - name: Checkout repository\n uses: actions/checkout@v4\n\n - name: Read site configuration\n id: config\n run: |\n CONFIG_FILE=\".dcs/site.yaml\"\n \n if [ ! -f \"$CONFIG_FILE\" ]; then\n echo \"::error::Missing DCS site configuration file: $CONFIG_FILE\"\n exit 1\n fi\n \n SITE_NAME=$(grep -E '^site_name:' \"$CONFIG_FILE\" | sed 's/site_name:\\s*//' | tr -d '\"' | tr -d \"'\")\n SITE_SLUG=$(grep -E '^site_slug:' \"$CONFIG_FILE\" | sed 's/site_slug:\\s*//' | tr -d '\"' | tr -d \"'\")\n SWA_RESOURCE_ID=$(grep -E '^swa_resource_id:' \"$CONFIG_FILE\" | sed 's/swa_resource_id:\\s*//' | tr -d '\"' | tr -d \"'\")\n PORTAL_API_URL=$(grep -E '^portal_api_url:' \"$CONFIG_FILE\" | sed 's/portal_api_url:\\s*//' | tr -d '\"' | tr -d \"'\")\n PORTAL_API_URL=\"${PORTAL_API_URL:-https://portal.duffcloudservices.com}\"\n GOOGLE_ANALYTICS_ID=$(grep -E '^google_analytics_id:' \"$CONFIG_FILE\" | sed 's/google_analytics_id:\\s*//' | tr -d '\"' | tr -d \"'\")\n PROD_APPINSIGHTS_CONNECTION_STRING=$(grep -E '^prod_appinsights_connection_string:' \"$CONFIG_FILE\" | sed 's/prod_appinsights_connection_string:\\s*//' | tr -d '\"' | tr -d \"'\" || true)\n DEV_APPINSIGHTS_CONNECTION_STRING=$(grep -E '^dev_appinsights_connection_string:' \"$CONFIG_FILE\" | sed 's/dev_appinsights_connection_string:\\s*//' | tr -d '\"' | tr -d \"'\" || true)\n \n # Azure Authentication\n AZURE_CLIENT_ID=$(grep -E '^\\s*client_id:' \"$CONFIG_FILE\" | sed 's/.*client_id:\\s*//' | tr -d '\"' | tr -d \"'\")\n AZURE_TENANT_ID=$(grep -E '^\\s*tenant_id:' \"$CONFIG_FILE\" | sed 's/.*tenant_id:\\s*//' | tr -d '\"' | tr -d \"'\")\n AZURE_SUBSCRIPTION_ID=$(grep -E '^\\s*subscription_id:' \"$CONFIG_FILE\" | sed 's/.*subscription_id:\\s*//' | tr -d '\"' | tr -d \"'\")\n \n # Build Configuration\n APP_LOCATION=$(grep -E '^\\s*app_location:' \"$CONFIG_FILE\" | sed 's/.*app_location:\\s*//' | tr -d '\"' | tr -d \"'\")\n OUTPUT_LOCATION=$(grep -E '^\\s*output_location:' \"$CONFIG_FILE\" | sed 's/.*output_location:\\s*//' | tr -d '\"' | tr -d \"'\")\n \n APP_LOCATION=\"${APP_LOCATION:-dist}\"\n OUTPUT_LOCATION=\"${OUTPUT_LOCATION:-.}\"\n\n if [ -z \"$SWA_RESOURCE_ID\" ]; then\n echo \"::error::Missing swa_resource_id in $CONFIG_FILE\"\n exit 1\n fi\n \n echo \"site_name=$SITE_NAME\" >> $GITHUB_OUTPUT\n echo \"site_slug=$SITE_SLUG\" >> $GITHUB_OUTPUT\n echo \"swa_resource_id=$SWA_RESOURCE_ID\" >> $GITHUB_OUTPUT\n echo \"app_location=$APP_LOCATION\" >> $GITHUB_OUTPUT\n echo \"output_location=$OUTPUT_LOCATION\" >> $GITHUB_OUTPUT\n echo \"azure_client_id=$AZURE_CLIENT_ID\" >> $GITHUB_OUTPUT\n echo \"azure_tenant_id=$AZURE_TENANT_ID\" >> $GITHUB_OUTPUT\n echo \"azure_subscription_id=$AZURE_SUBSCRIPTION_ID\" >> $GITHUB_OUTPUT\n echo \"portal_api_url=$PORTAL_API_URL\" >> $GITHUB_OUTPUT\n echo \"google_analytics_id=$GOOGLE_ANALYTICS_ID\" >> $GITHUB_OUTPUT\n echo \"prod_appinsights_connection_string=$PROD_APPINSIGHTS_CONNECTION_STRING\" >> $GITHUB_OUTPUT\n echo \"dev_appinsights_connection_string=$DEV_APPINSIGHTS_CONNECTION_STRING\" >> $GITHUB_OUTPUT\n\n - name: Azure login via OIDC\n uses: azure/login@v2\n with:\n client-id: ${{ steps.config.outputs.azure_client_id || vars.AZURE_CLIENT_ID }}\n tenant-id: ${{ steps.config.outputs.azure_tenant_id || vars.AZURE_TENANT_ID }}\n subscription-id: ${{ steps.config.outputs.azure_subscription_id || vars.AZURE_SUBSCRIPTION_ID }}\n\n - name: Get deployment tokens\n id: tokens\n run: |\n # Get access token for DCS API\n ACCESS_TOKEN=$(az account get-access-token --resource \"api://304711b9-8532-4659-beb7-c85726de9ae5\" --query accessToken -o tsv)\n \n SITE_NAME=\"${{ steps.config.outputs.site_name }}\"\n SITE_SLUG=\"${{ steps.config.outputs.site_slug }}\"\n PORTAL_API_URL=\"${{ steps.config.outputs.portal_api_url }}\"\n \n RESPONSE=$(curl -s -X POST \"${PORTAL_API_URL}/api/v1/sites/deployment-tokens\" \\\n -H \"Authorization: Bearer $ACCESS_TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d \"{\\\"siteName\\\": \\\"$SITE_NAME\\\", \\\"siteSlug\\\": \\\"$SITE_SLUG\\\"}\")\n \n SWA_TOKEN=$(echo $RESPONSE | jq -r .swaToken)\n \n if [ -z \"$SWA_TOKEN\" ] || [ \"$SWA_TOKEN\" == \"null\" ]; then\n echo \"::error::Failed to retrieve SWA token from DCS API\"\n echo \"Response: $RESPONSE\"\n exit 1\n fi\n \n echo \"::add-mask::$SWA_TOKEN\"\n echo \"swa_token=$SWA_TOKEN\" >> $GITHUB_OUTPUT\n\n - name: Setup Node.js\n uses: actions/setup-node@v4\n with:\n node-version: \"22\"\n\n - name: Install pnpm\n uses: pnpm/action-setup@v4\n\n - name: Setup pnpm cache\n uses: actions/cache@v4\n with:\n path: ~/.pnpm-store\n key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}\n restore-keys: |\n ${{ runner.os }}-pnpm-\n\n - name: Install dependencies\n run: pnpm install --frozen-lockfile\n\n - name: Create environment file\n run: |\n cat > .env << EOF\n VITE_APP_VERSION=\"${{ github.sha }}\"\n VITE_BACKEND_URI=\"${{ steps.config.outputs.portal_api_url }}\"\n VITE_SITE_SLUG=\"${{ steps.config.outputs.site_slug }}\"\n VITE_GOOGLE_ANALYTICS_ID=\"${{ steps.config.outputs.google_analytics_id }}\"\n VITE_PROD_APPINSIGHTS_CONNECTION_STRING=\"${{ steps.config.outputs.prod_appinsights_connection_string }}\"\n VITE_DEV_APPINSIGHTS_CONNECTION_STRING=\"${{ steps.config.outputs.dev_appinsights_connection_string }}\"\n EOF\n\n - name: Build site\n run: pnpm run build\n\n - name: Inject Google Analytics\n if: steps.config.outputs.google_analytics_id != ''\n run: |\n GA_ID=\"${{ steps.config.outputs.google_analytics_id }}\"\n OUTPUT_DIR=\"${{ steps.config.outputs.app_location }}\"\n echo \"Injecting Google Analytics tag ${GA_ID} into ${OUTPUT_DIR}...\"\n find \"$OUTPUT_DIR\" -name \"*.html\" -exec sed -i \"s|</head>|<script async src=\\\"https://www.googletagmanager.com/gtag/js?id=${GA_ID}\\\"></script><script>window.dataLayer=window.dataLayer\\|\\|[];function gtag(){dataLayer.push(arguments)}gtag('js',new Date());gtag('config','${GA_ID}')</script>\\n</head>|\" {} +\n INJECTED=$(grep -rl \"googletagmanager\" \"$OUTPUT_DIR\" --include=\"*.html\" | wc -l)\n echo \"Injected GA tag into ${INJECTED} HTML file(s)\"\n\n - name: Deploy to Azure Static Web Apps\n uses: Azure/static-web-apps-deploy@v1\n with:\n azure_static_web_apps_api_token: ${{ steps.tokens.outputs.swa_token }}\n repo_token: ${{ secrets.GITHUB_TOKEN }}\n action: \"upload\"\n app_location: ${{ steps.config.outputs.app_location }}\n output_location: ${{ steps.config.outputs.output_location }}\n skip_app_build: true\n deployment_environment: ${{ steps.environment.outputs.swa_environment != '' && steps.environment.outputs.swa_environment || '' }}\n","/**\n * Validate command - validate existing .dcs configuration files.\n */\n\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\nimport chalk from 'chalk'\nimport yaml from 'js-yaml'\n\nexport interface ValidateOptions {\n target?: string\n fix?: boolean\n verbose?: boolean\n}\n\ninterface ValidationResult {\n file: string\n valid: boolean\n errors: string[]\n warnings: string[]\n}\n\n/**\n * Validate .dcs configuration files for a customer site.\n */\nexport async function validateCommand(options: ValidateOptions): Promise<void> {\n const { target = '.', fix = false, verbose = false } = options\n\n const targetDir = path.resolve(target)\n const dcsDir = path.join(targetDir, '.dcs')\n\n console.log(chalk.bold('Validating DCS configuration...'))\n console.log(chalk.dim(`Directory: ${targetDir}`))\n console.log()\n\n // Check if .dcs directory exists\n try {\n await fs.access(dcsDir)\n } catch {\n console.log(chalk.red('No .dcs directory found.'))\n console.log('Run', chalk.cyan('dcs init'), 'to create DCS configuration.')\n process.exit(1)\n }\n\n const results: ValidationResult[] = []\n\n // Validate each config file\n results.push(await validateSiteYaml(dcsDir, verbose))\n results.push(await validatePagesYaml(dcsDir, verbose))\n results.push(await validateContentYaml(dcsDir, verbose))\n results.push(await validateSeoYaml(dcsDir, verbose))\n\n // Display results\n console.log(chalk.bold('Validation Results:'))\n console.log()\n\n let hasErrors = false\n let hasWarnings = false\n\n for (const result of results) {\n if (!result.valid || result.errors.length > 0) {\n hasErrors = true\n console.log(`${chalk.red('✗')} ${result.file}`)\n for (const error of result.errors) {\n console.log(` ${chalk.red('error:')} ${error}`)\n }\n } else if (result.warnings.length > 0) {\n hasWarnings = true\n console.log(`${chalk.yellow('!')} ${result.file}`)\n } else {\n console.log(`${chalk.green('✓')} ${result.file}`)\n }\n\n for (const warning of result.warnings) {\n console.log(` ${chalk.yellow('warning:')} ${warning}`)\n }\n }\n\n console.log()\n\n if (hasErrors) {\n console.log(chalk.red('Validation failed with errors.'))\n if (fix) {\n console.log(chalk.yellow('Some errors can be fixed automatically. Run with --fix flag.'))\n }\n process.exit(1)\n } else if (hasWarnings) {\n console.log(chalk.yellow('Validation passed with warnings.'))\n } else {\n console.log(chalk.green('All configuration files are valid.'))\n }\n}\n\n// =============================================================================\n// File Validators\n// =============================================================================\n\nasync function validateSiteYaml(dcsDir: string, verbose: boolean): Promise<ValidationResult> {\n const filePath = path.join(dcsDir, 'site.yaml')\n const result: ValidationResult = {\n file: '.dcs/site.yaml',\n valid: true,\n errors: [],\n warnings: [],\n }\n\n try {\n const content = await fs.readFile(filePath, 'utf8')\n const config = yaml.load(content) as Record<string, unknown>\n\n // Required fields\n if (!config.site_name) {\n result.errors.push('Missing required field: site_name')\n result.valid = false\n }\n\n if (!config.site_slug) {\n result.errors.push('Missing required field: site_slug')\n result.valid = false\n } else if (!/^[a-z0-9-]+$/.test(config.site_slug as string)) {\n result.errors.push('site_slug must be lowercase alphanumeric with hyphens only')\n result.valid = false\n }\n\n // Warnings for empty optional fields\n if (!config.swa_resource_id) {\n result.warnings.push('swa_resource_id is not set (required for deployment)')\n }\n\n if (!config.production_url) {\n result.warnings.push('production_url is not set')\n }\n\n if (verbose) {\n console.log(chalk.dim(` site_name: ${config.site_name}`))\n console.log(chalk.dim(` site_slug: ${config.site_slug}`))\n }\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n result.errors.push('File not found')\n } else {\n result.errors.push(`Parse error: ${(error as Error).message}`)\n }\n result.valid = false\n }\n\n return result\n}\n\nasync function validatePagesYaml(dcsDir: string, verbose: boolean): Promise<ValidationResult> {\n const filePath = path.join(dcsDir, 'pages.yaml')\n const result: ValidationResult = {\n file: '.dcs/pages.yaml',\n valid: true,\n errors: [],\n warnings: [],\n }\n\n try {\n const content = await fs.readFile(filePath, 'utf8')\n const config = yaml.load(content) as Record<string, unknown>\n\n // Required fields\n if (config.version === undefined) {\n result.errors.push('Missing required field: version')\n result.valid = false\n }\n\n if (!config.siteSlug) {\n result.errors.push('Missing required field: siteSlug')\n result.valid = false\n }\n\n // Validate pages array\n const pages = config.pages as Array<Record<string, unknown>> | undefined\n if (!pages || !Array.isArray(pages)) {\n result.errors.push('Missing or invalid pages array')\n result.valid = false\n } else {\n // Check for home page\n const hasHome = pages.some((p) => p.slug === 'home')\n if (!hasHome) {\n result.warnings.push('No home page defined (slug: home)')\n }\n\n // Validate each page\n for (let i = 0; i < pages.length; i++) {\n const page = pages[i]\n if (!page.slug) {\n result.errors.push(`Page ${i}: missing slug`)\n result.valid = false\n }\n if (!page.path) {\n result.errors.push(`Page ${i}: missing path`)\n result.valid = false\n }\n if (!page.type) {\n result.warnings.push(`Page '${page.slug}': missing type (defaulting to static)`)\n }\n }\n\n if (verbose) {\n console.log(chalk.dim(` ${pages.length} pages defined`))\n }\n }\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n result.errors.push('File not found')\n } else {\n result.errors.push(`Parse error: ${(error as Error).message}`)\n }\n result.valid = false\n }\n\n return result\n}\n\nasync function validateContentYaml(dcsDir: string, verbose: boolean): Promise<ValidationResult> {\n const filePath = path.join(dcsDir, 'content.yaml')\n const result: ValidationResult = {\n file: '.dcs/content.yaml',\n valid: true,\n errors: [],\n warnings: [],\n }\n\n try {\n const content = await fs.readFile(filePath, 'utf8')\n const config = yaml.load(content) as Record<string, unknown>\n\n // Required fields\n if (config.version === undefined) {\n result.errors.push('Missing required field: version')\n result.valid = false\n }\n\n // Validate structure\n if (config.global !== undefined && typeof config.global !== 'object') {\n result.errors.push('global must be an object')\n result.valid = false\n }\n\n if (config.pages !== undefined && typeof config.pages !== 'object') {\n result.errors.push('pages must be an object')\n result.valid = false\n }\n\n if (verbose) {\n const globalKeys = config.global ? Object.keys(config.global as object).length : 0\n const pageCount = config.pages ? Object.keys(config.pages as object).length : 0\n console.log(chalk.dim(` ${globalKeys} global keys, ${pageCount} page sections`))\n }\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n result.errors.push('File not found')\n } else {\n result.errors.push(`Parse error: ${(error as Error).message}`)\n }\n result.valid = false\n }\n\n return result\n}\n\nasync function validateSeoYaml(dcsDir: string, verbose: boolean): Promise<ValidationResult> {\n const filePath = path.join(dcsDir, 'seo.yaml')\n const result: ValidationResult = {\n file: '.dcs/seo.yaml',\n valid: true,\n errors: [],\n warnings: [],\n }\n\n try {\n const content = await fs.readFile(filePath, 'utf8')\n const config = yaml.load(content) as Record<string, unknown>\n\n // Required fields\n if (config.version === undefined) {\n result.errors.push('Missing required field: version')\n result.valid = false\n }\n\n // Validate global section\n const global = config.global as Record<string, unknown> | undefined\n if (!global) {\n result.errors.push('Missing required section: global')\n result.valid = false\n } else {\n if (!global.siteName) {\n result.warnings.push('global.siteName is not set')\n }\n if (!global.siteUrl) {\n result.warnings.push('global.siteUrl is not set (required for SEO)')\n }\n if (!global.defaultDescription) {\n result.warnings.push('global.defaultDescription is not set')\n }\n }\n\n // Validate pages section\n const pages = config.pages as Record<string, unknown> | undefined\n if (pages && typeof pages === 'object') {\n const pageKeys = Object.keys(pages)\n if (!pageKeys.includes('home')) {\n result.warnings.push('No SEO configuration for home page')\n }\n\n if (verbose) {\n console.log(chalk.dim(` ${pageKeys.length} page SEO configs`))\n }\n }\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n result.errors.push('File not found')\n } else {\n result.errors.push(`Parse error: ${(error as Error).message}`)\n }\n result.valid = false\n }\n\n return result\n}\n","/**\n * Plans command - generate/regenerate Copilot prompt plans for DCS integration.\n */\n\nimport fs from 'node:fs/promises'\nimport path from 'node:path'\nimport chalk from 'chalk'\nimport yaml from 'js-yaml'\n\nexport interface PlansOptions {\n target?: string\n force?: boolean\n}\n\n/**\n * Generate DCS integration plan files for AI-assisted onboarding.\n */\nexport async function plansCommand(options: PlansOptions): Promise<void> {\n const { target = '.', force = false } = options\n\n const targetDir = path.resolve(target)\n const dcsDir = path.join(targetDir, '.dcs')\n const plansDir = path.join(targetDir, '.plans')\n\n console.log(chalk.bold('Generating DCS integration plans...'))\n console.log(chalk.dim(`Directory: ${targetDir}`))\n console.log()\n\n // Check if .dcs directory exists\n try {\n await fs.access(dcsDir)\n } catch {\n console.log(chalk.red('No .dcs directory found.'))\n console.log('Run', chalk.cyan('dcs init'), 'first to create DCS configuration.')\n process.exit(1)\n }\n\n // Read site config for context\n let siteName = 'Customer Site'\n let framework = 'vue'\n try {\n const siteYaml = await fs.readFile(path.join(dcsDir, 'site.yaml'), 'utf8')\n const siteConfig = yaml.load(siteYaml) as Record<string, unknown>\n siteName = (siteConfig.site_name as string) || siteName\n const metadata = siteConfig.metadata as Record<string, unknown> | undefined\n framework = (metadata?.framework as string) || framework\n } catch {\n console.log(chalk.yellow('Warning: Could not read site.yaml'))\n }\n\n // Generate plan files\n const plans: Array<{ name: string; content: string }> = [\n { name: '00-audit-site.md', content: generateAuditPlan(siteName) },\n { name: '01-create-use-text-content.md', content: generateTextContentPlan(siteName, framework) },\n { name: '02-integrate-home-page.md', content: generateHomePagePlan(siteName) },\n { name: '03-integrate-remaining-pages.md', content: generateRemainingPagesPlan(siteName) },\n { name: '04-capture-snapshots.md', content: generateSnapshotsPlan(siteName) },\n { name: '05-verify-deployment.md', content: generateVerifyPlan(siteName) },\n ]\n\n // Create plans directory\n await fs.mkdir(plansDir, { recursive: true })\n\n // Write plan files\n const generatedFiles: string[] = []\n for (const plan of plans) {\n const filePath = path.join(plansDir, plan.name)\n\n // Check if file exists\n try {\n await fs.access(filePath)\n if (!force) {\n console.log(chalk.yellow(`Skipped (exists): .plans/${plan.name}`))\n continue\n }\n } catch {\n // File doesn't exist, continue\n }\n\n await fs.writeFile(filePath, plan.content, 'utf8')\n generatedFiles.push(plan.name)\n console.log(`${chalk.green('✓')} .plans/${plan.name}`)\n }\n\n console.log()\n if (generatedFiles.length > 0) {\n console.log(chalk.green(`Generated ${generatedFiles.length} plan files.`))\n } else {\n console.log(chalk.yellow('No new files generated. Use --force to overwrite existing files.'))\n }\n}\n\n// =============================================================================\n// Plan Templates\n// =============================================================================\n\nfunction generateAuditPlan(siteName: string): string {\n return `# Plan 00: Audit ${siteName} Site Structure\n\n## Objective\n\nAnalyze the current site structure to understand:\n- What pages exist and their routing structure\n- Current component organization\n- Existing content patterns\n- Build/deploy configuration\n\n## Steps\n\n### Step 1: List All Pages\n\nScan the project for page components:\n\n\\`\\`\\`\n# For VitePress\nls docs/**/*.md\n\n# For Vue/Vite\nls src/pages/**/*.vue\nls src/views/**/*.vue\n\\`\\`\\`\n\n### Step 2: Identify Content Patterns\n\nLook for:\n- Hardcoded text strings in templates\n- Existing i18n or content management\n- Props-based content passing\n- API-driven content\n\n### Step 3: Document Current Structure\n\nCreate a list of:\n1. All page routes\n2. Reusable components with text content\n3. Layout components (header, footer)\n4. Any existing SEO implementation\n\n### Step 4: Update pages.yaml\n\nUpdate \\`.dcs/pages.yaml\\` with all discovered pages:\n\n\\`\\`\\`yaml\npages:\n - slug: home\n path: /\n type: static\n title: Home\n deletable: false\n # Add discovered pages here...\n\\`\\`\\`\n\n## Output\n\n- Updated \\`.dcs/pages.yaml\\` with all pages\n- List of components requiring content integration\n- Notes on any existing content management to migrate\n`\n}\n\nfunction generateTextContentPlan(siteName: string, framework: string): string {\n return `# Plan 01: Create useTextContent Composable for ${siteName}\n\n## Objective\n\nSet up the text content composable for runtime/build-time content management.\n\n## Prerequisites\n\n- \\`@duffcloudservices/cms\\` package installed\n- Vite/VitePress project configured\n\n## Steps\n\n### Step 1: Install CMS Package\n\n\\`\\`\\`bash\npnpm add @duffcloudservices/cms\n\\`\\`\\`\n\n### Step 2: Configure Vite Plugin\n\n${framework === 'vue' ? `\nIn \\`vite.config.ts\\`:\n\n\\`\\`\\`typescript\nimport { defineConfig } from 'vite'\nimport vue from '@vitejs/plugin-vue'\nimport { dcsContentPlugin } from '@duffcloudservices/cms/plugins'\n\nexport default defineConfig({\n plugins: [\n vue(),\n dcsContentPlugin({ path: '.dcs/content.yaml' }),\n ],\n})\n\\`\\`\\`\n` : `\nIn \\`.vitepress/config.ts\\`:\n\n\\`\\`\\`typescript\nimport { defineConfig } from 'vitepress'\nimport { dcsContentPlugin } from '@duffcloudservices/cms/plugins'\n\nexport default defineConfig({\n vite: {\n plugins: [\n dcsContentPlugin({ path: '.dcs/content.yaml' }),\n ],\n },\n})\n\\`\\`\\`\n`}\n\n### Step 3: Create Site Composable Wrapper (Optional)\n\nCreate a site-specific wrapper for consistent defaults:\n\n\\`\\`\\`typescript\n// src/composables/useContent.ts\nimport { useTextContent as baseuseTextContent } from '@duffcloudservices/cms'\n\nexport function useContent(pageSlug: string, defaults: Record<string, string> = {}) {\n return baseuseTextContent({ pageSlug, defaults })\n}\n\\`\\`\\`\n\n## Verification\n\n1. Run \\`pnpm dev\\` - should start without errors\n2. Import and use composable in a test component\n3. Verify content.yaml values are accessible via \\`t()\\` function\n`\n}\n\nfunction generateHomePagePlan(siteName: string): string {\n return `# Plan 02: Integrate Home Page for ${siteName}\n\n## Objective\n\nAdd DCS CMS integration to the home page, including:\n- Text content management\n- SEO configuration\n- Data attributes for visual editor\n\n## Steps\n\n### Step 1: Identify Home Page Component\n\nFind the main home page component:\n- VitePress: \\`docs/index.md\\` or \\`docs/.vitepress/theme/components/Home.vue\\`\n- Vue: \\`src/pages/index.vue\\` or \\`src/views/Home.vue\\`\n\n### Step 2: Add Text Content Integration\n\nReplace hardcoded text with \\`t()\\` function calls:\n\n\\`\\`\\`vue\n<script setup lang=\"ts\">\nimport { useTextContent, useSEO } from '@duffcloudservices/cms'\n\nconst { t } = useTextContent({\n pageSlug: 'home',\n defaults: {\n 'hero.title': 'Welcome to ${siteName}',\n 'hero.subtitle': 'Your subtitle here',\n 'hero.cta': 'Get Started',\n // Add all text content...\n }\n})\n\n// Apply SEO\nconst { applyHead } = useSEO('home')\napplyHead()\n</script>\n\n<template>\n <section data-dcs-section=\"hero\">\n <h1 data-dcs-text=\"hero.title\">{{ t('hero.title') }}</h1>\n <p data-dcs-text=\"hero.subtitle\">{{ t('hero.subtitle') }}</p>\n <button data-dcs-text=\"hero.cta\">{{ t('hero.cta') }}</button>\n </section>\n</template>\n\\`\\`\\`\n\n### Step 3: Update content.yaml\n\nAdd all discovered text keys to \\`.dcs/content.yaml\\`:\n\n\\`\\`\\`yaml\npages:\n home:\n hero.title: \"Welcome to ${siteName}\"\n hero.subtitle: \"Your subtitle here\"\n hero.cta: \"Get Started\"\n\\`\\`\\`\n\n### Step 4: Update seo.yaml\n\nConfigure SEO for the home page in \\`.dcs/seo.yaml\\`:\n\n\\`\\`\\`yaml\npages:\n home:\n title: Home\n description: \"Description for ${siteName}\"\n noTitleTemplate: true\n openGraph:\n type: website\n\\`\\`\\`\n\n## Verification\n\n1. Run \\`pnpm dev\\` and verify home page renders correctly\n2. Check browser console for any errors\n3. Verify SEO meta tags in page source\n4. Test that data-dcs-* attributes are present\n`\n}\n\nfunction generateRemainingPagesPlan(siteName: string): string {\n return `# Plan 03: Integrate Remaining Pages for ${siteName}\n\n## Objective\n\nApply the same integration pattern to all remaining pages identified in the audit.\n\n## Steps\n\n### For Each Page\n\n1. **Open the page component**\n\n2. **Add imports and composable setup**:\n\n\\`\\`\\`vue\n<script setup lang=\"ts\">\nimport { useTextContent, useSEO } from '@duffcloudservices/cms'\n\nconst { t } = useTextContent({\n pageSlug: 'PAGE_SLUG', // Match pages.yaml slug\n defaults: {\n // Add all text content with sensible defaults\n }\n})\n\nconst { applyHead } = useSEO('PAGE_SLUG')\napplyHead()\n</script>\n\\`\\`\\`\n\n3. **Replace all hardcoded text** with \\`t()\\` calls\n\n4. **Add data-dcs-* attributes** to sections and text elements\n\n5. **Update content.yaml** with the page's text content\n\n6. **Update seo.yaml** with SEO configuration\n\n### Global Components\n\nDon't forget shared components like:\n- Header/Navigation\n- Footer\n- Sidebar\n\nThese should use \\`pageSlug: 'global'\\` for shared content.\n\n## Checklist\n\n- [ ] About page\n- [ ] Contact page\n- [ ] Services/Products pages\n- [ ] Blog index (if applicable)\n- [ ] Header component\n- [ ] Footer component\n- [ ] Any other pages...\n\n## Verification\n\nFor each page:\n1. Page renders correctly\n2. No console errors\n3. SEO meta tags present\n4. Data attributes present\n`\n}\n\nfunction generateSnapshotsPlan(siteName: string): string {\n return `# Plan 04: Configure Snapshot Capture for ${siteName}\n\n## Objective\n\nEnsure the visual page editor can capture accurate snapshots of all pages.\n\n## Prerequisites\n\n- All pages integrated with \\`useTextContent\\`\n- All pages have \\`data-dcs-section\\` attributes\n- All editable text has \\`data-dcs-text\\` attributes\n\n## Steps\n\n### Step 1: Verify pages.yaml\n\nEnsure all pages are listed in \\`.dcs/pages.yaml\\`:\n\n\\`\\`\\`yaml\npages:\n - slug: home\n path: /\n type: static\n title: Home\n deletable: false\n # All other pages...\n\nexcluded:\n - /404\n - /dev-*\n - /_*\n\\`\\`\\`\n\n### Step 2: Configure Snapshot Settings\n\nReview snapshot configuration:\n\n\\`\\`\\`yaml\nsnapshot:\n viewport:\n width: 1280\n height: 720\n waitAfterLoad: 3000 # Adjust if page has animations\n captureFullPage: true\n\\`\\`\\`\n\n### Step 3: Test Local Snapshots\n\nBuild and preview the site:\n\n\\`\\`\\`bash\npnpm build\npnpm preview\n\\`\\`\\`\n\nManually verify each page:\n1. Opens correctly\n2. All content is visible\n3. No loading spinners or placeholders\n\n### Step 4: Verify Data Attributes\n\nUse browser DevTools to check:\n- Each section has \\`data-dcs-section\\`\n- Each editable text element has \\`data-dcs-text\\`\n- Attribute values match content.yaml keys\n\n## Verification\n\n1. Site builds without errors\n2. All pages accessible at documented paths\n3. Data attributes present in rendered HTML\n4. No dynamic content that would prevent snapshot capture\n`\n}\n\nfunction generateVerifyPlan(siteName: string): string {\n return `# Plan 05: Verify Deployment for ${siteName}\n\n## Objective\n\nTest the complete integration before handing off to the DCS Portal.\n\n## Steps\n\n### Step 1: Run Validation\n\n\\`\\`\\`bash\nnpx @duffcloudservices/cli validate\n\\`\\`\\`\n\nFix any reported errors or warnings.\n\n### Step 2: Build Test\n\n\\`\\`\\`bash\npnpm build\n\\`\\`\\`\n\nEnsure no build errors related to:\n- Missing content keys\n- SEO configuration\n- Vite plugin issues\n\n### Step 3: Local Preview\n\n\\`\\`\\`bash\npnpm preview\n\\`\\`\\`\n\nVerify:\n- [ ] Home page loads correctly\n- [ ] All pages accessible\n- [ ] SEO meta tags correct\n- [ ] No console errors\n- [ ] Content displays correctly\n\n### Step 4: Deploy to Staging\n\nDeploy to Azure Static Web Apps staging environment:\n\n\\`\\`\\`bash\n# If using GitHub Actions, push to a PR branch\ngit push origin feature/dcs-integration\n\\`\\`\\`\n\n### Step 5: Portal Registration\n\n1. Log in to DCS Portal\n2. Navigate to Sites > Add Site\n3. Enter site slug and GitHub repo\n4. Trigger initial snapshot capture\n\n### Step 6: Visual Editor Test\n\n1. Open the site in DCS Portal\n2. Navigate to Pages\n3. Select a page\n4. Verify visual editor loads correctly\n5. Test editing a text field\n6. Save and verify changes deploy\n\n## Success Criteria\n\n- [ ] All validation passes\n- [ ] Site builds and deploys\n- [ ] Portal can capture snapshots\n- [ ] Visual editor works\n- [ ] Content changes deploy correctly\n\n## Handoff\n\nOnce verified:\n1. Merge the integration PR\n2. Notify the site owner\n3. Provide portal access credentials\n`\n}\n","/**\n * Capture visual snapshots of customer site pages for the portal's visual page editor.\n *\n * This command:\n * 1. Reads page configuration from .dcs/pages.yaml\n * 2. Launches a headless browser via Playwright\n * 3. Captures full-page screenshots and section bounds\n * 4. Detects text keys from data-text-key attributes\n * 5. Outputs JSON manifests and PNG screenshots\n *\n * The output is consumed by the DCS portal to render the visual page editor.\n */\n\nimport fs from 'fs'\nimport path from 'path'\nimport yaml from 'js-yaml'\nimport chalk from 'chalk'\nimport ora from 'ora'\nimport type { Browser, ElementHandle, Page } from 'playwright'\n\n// =============================================================================\n// Types\n// =============================================================================\n\ninterface PagesConfig {\n siteSlug: string\n version: number | string\n pages: PageConfig[]\n snapshot: {\n viewport: { width: number; height: number }\n waitAfterLoad: number\n captureFullPage: boolean\n outputDir?: string\n }\n}\n\ninterface PageConfig {\n slug: string\n path: string\n type: string\n title: string\n deletable: boolean\n textKeys?: string[]\n /** For dynamic pages: template pattern like /topics/:topic */\n pathTemplate?: string\n /** For dynamic pages: list of actual slugs to capture */\n instances?: string[]\n}\n\ninterface ContentConfig {\n global?: Record<string, unknown>\n pages?: Record<string, Record<string, unknown>>\n}\n\ninterface TextKeyInfo {\n key: string\n currentValue: string\n elementType: string\n bounds: {\n x: number\n y: number\n width: number\n height: number\n }\n domPath?: string\n}\n\ninterface SectionInfo {\n sectionId: string\n sectionType: 'header' | 'footer' | 'hero' | 'content' | 'gallery' | 'cta' | 'custom'\n displayName: string\n domPath: string\n bounds: {\n x: number\n y: number\n width: number\n height: number\n }\n textKeys: TextKeyInfo[]\n isEditable: boolean\n isStructuralOnly: boolean\n}\n\ninterface PageSnapshot {\n pageSlug: string\n path: string\n type: string\n deletable: boolean\n title: string\n capturedAt: string\n previewUrl: string\n viewport: { width: number; height: number }\n pageHeight: number\n sections: SectionInfo[]\n}\n\ninterface SnapshotManifest {\n siteSlug: string\n capturedAt: string\n capturedVersion?: string\n deploymentRef?: string\n pagesConfigVersion: number | string\n pages: Array<{\n pageSlug: string\n pagePath: string\n pageType: string\n deletable: boolean\n title: string\n capturedAt: string\n sectionCount: number\n hasSnapshot: boolean\n }>\n}\n\ninterface TargetedSnapshotsConfig {\n mode: 'skip' | 'targeted' | 'full'\n pages: string[]\n reason: string\n triggeredBy?: string\n}\n\ninterface CaptureSnapshotsOptions {\n target: string\n baseUrl: string\n dryRun?: boolean\n verbose?: boolean\n pages?: string[]\n}\n\nconst CARD_THUMBNAIL_ASPECT_RATIO = 16 / 9\nconst ASSET_WAIT_TIMEOUT_MS = 5000\n\nasync function disableCaptureMotion(page: Page): Promise<void> {\n await page.addStyleTag({\n content: `\n *, *::before, *::after {\n animation-delay: -1ms !important;\n animation-duration: 1ms !important;\n animation-iteration-count: 1 !important;\n scroll-behavior: auto !important;\n transition-delay: 0s !important;\n transition-duration: 0s !important;\n }\n `,\n }).catch(() => {\n // Some pages can navigate before style injection completes; capture can continue without this stabilization.\n })\n}\n\nasync function waitForAboveFoldAssets(page: Page): Promise<void> {\n await page.evaluate(async (timeoutMs) => {\n const waitForFonts = typeof document.fonts?.ready?.then === 'function'\n ? document.fonts.ready.catch(() => undefined)\n : Promise.resolve()\n\n const visibleImages = Array.from(document.images).filter((img) => {\n const rect = img.getBoundingClientRect()\n return rect.bottom >= -100 && rect.top <= window.innerHeight * 1.5\n })\n\n const waitForImages = Promise.all(visibleImages.map((img) => {\n if (img.complete && img.naturalWidth > 0) {\n return Promise.resolve()\n }\n return new Promise<void>((resolve) => {\n const done = () => resolve()\n img.addEventListener('load', done, { once: true })\n img.addEventListener('error', done, { once: true })\n })\n }))\n\n await Promise.race([\n Promise.all([waitForFonts, waitForImages]),\n new Promise((resolve) => window.setTimeout(resolve, timeoutMs)),\n ])\n }, ASSET_WAIT_TIMEOUT_MS).catch((error) => {\n console.warn(` Warning: could not verify above-fold assets: ${error instanceof Error ? error.message : String(error)}`)\n })\n}\n\nasync function lazyLoadPage(page: Page, viewportHeight: number): Promise<number> {\n let pageHeight = await page.evaluate(() => document.documentElement.scrollHeight)\n const scrollStep = Math.max(viewportHeight, 1)\n\n for (let scrollY = 0; scrollY < pageHeight; scrollY += scrollStep) {\n await page.evaluate((y) => window.scrollTo(0, y), scrollY)\n await page.waitForTimeout(100)\n }\n\n await page.waitForTimeout(200)\n pageHeight = await page.evaluate(() => document.documentElement.scrollHeight)\n\n return pageHeight\n}\n\nasync function resetScrollForFullPageCapture(page: Page): Promise<void> {\n await page.evaluate(() => {\n window.scrollTo(0, 0)\n window.dispatchEvent(new Event('scroll'))\n })\n await page.waitForFunction(() => Math.abs(window.scrollY) < 1, undefined, { timeout: 1000 }).catch(() => undefined)\n await page.evaluate(() => new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve)))).catch(() => undefined)\n await page.waitForTimeout(350)\n await waitForAboveFoldAssets(page)\n}\n\nasync function withFullPageCaptureLayout<T>(page: Page, capture: () => Promise<T>): Promise<T> {\n const styleHandle = await page.addStyleTag({\n content: `\n html[data-dcs-full-page-snapshot=\"true\"] header[data-section=\"header\"],\n html[data-dcs-full-page-snapshot=\"true\"] [data-section-type=\"header\"],\n html[data-dcs-full-page-snapshot=\"true\"] [role=\"banner\"] {\n position: static !important;\n transform: none !important;\n }\n\n html[data-dcs-full-page-snapshot=\"true\"] [data-dcs-pin-full-page-nav=\"true\"] {\n position: absolute !important;\n top: 0 !important;\n right: 0 !important;\n bottom: auto !important;\n left: 0 !important;\n transform: none !important;\n }\n\n html[data-dcs-full-page-snapshot=\"true\"] .VPNavBar {\n transform: none !important;\n }\n `,\n }).catch(() => null)\n\n await page.evaluate(() => {\n const candidates = Array.from(document.querySelectorAll(\n 'header[data-section=\"header\"] > nav, [data-section-type=\"header\"] > nav, [role=\"banner\"] > nav, .VPNav'\n ))\n candidates.forEach((element) => {\n const style = window.getComputedStyle(element)\n const className = element instanceof HTMLElement && typeof element.className === 'string' ? element.className : ''\n if (style.position === 'fixed' || style.position === 'sticky' || /\\b(fixed|sticky)\\b/.test(className) || element.classList.contains('VPNav')) {\n element.setAttribute('data-dcs-pin-full-page-nav', 'true')\n }\n })\n document.documentElement.setAttribute('data-dcs-full-page-snapshot', 'true')\n window.scrollTo(0, 0)\n window.dispatchEvent(new Event('scroll'))\n })\n await page.evaluate(() => new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve)))).catch(() => undefined)\n\n try {\n return await capture()\n } finally {\n await styleHandle?.evaluate((el) => el.parentNode?.removeChild(el)).catch(() => undefined)\n await page.evaluate(() => {\n document.documentElement.removeAttribute('data-dcs-full-page-snapshot')\n document.querySelectorAll('[data-dcs-pin-full-page-nav]').forEach((element) => {\n element.removeAttribute('data-dcs-pin-full-page-nav')\n })\n }).catch(() => undefined)\n }\n}\n\nasync function captureHeaderThumbnail(\n page: Page,\n outputPath: string,\n viewport: { width: number; height: number }\n): Promise<void> {\n await page.setViewportSize(viewport)\n await page.evaluate(() => window.scrollTo(0, 0))\n await page.waitForTimeout(200)\n await waitForAboveFoldAssets(page)\n\n const clip = await page.evaluate((aspectRatio) => {\n const width = Math.max(document.documentElement.clientWidth, window.innerWidth, 1)\n const pageHeight = Math.max(document.documentElement.scrollHeight, document.body?.scrollHeight ?? 0, 1)\n const height = Math.min(Math.round(width / aspectRatio), pageHeight)\n return { x: 0, y: 0, width, height }\n }, CARD_THUMBNAIL_ASPECT_RATIO)\n\n await page.screenshot({\n path: outputPath,\n clip,\n })\n}\n\n// =============================================================================\n// Section Detection Selectors\n// =============================================================================\n\nconst SECTION_SELECTORS = {\n explicit: '[data-section]',\n header: 'header, [role=\"banner\"], .header, #header',\n footer: 'footer, [role=\"contentinfo\"], .footer, #footer',\n hero: '[class*=\"hero\"], [data-section-type=\"hero\"], .hero-section, #hero',\n content: 'main, [role=\"main\"], article, .content, .main-content',\n gallery: '[class*=\"gallery\"], [data-section-type=\"gallery\"]',\n cta: '[class*=\"cta\"], [data-section-type=\"cta\"], .call-to-action',\n} as const\n\n// =============================================================================\n// Configuration Loaders\n// =============================================================================\n\nfunction loadPagesConfig(targetDir: string): PagesConfig {\n const configPath = path.join(targetDir, '.dcs', 'pages.yaml')\n if (!fs.existsSync(configPath)) {\n throw new Error(`Configuration file not found: ${configPath}`)\n }\n const content = fs.readFileSync(configPath, 'utf-8')\n return yaml.load(content) as PagesConfig\n}\n\nfunction loadContentConfig(targetDir: string): ContentConfig | null {\n const contentPath = path.join(targetDir, '.dcs', 'content.yaml')\n if (!fs.existsSync(contentPath)) {\n return null\n }\n const content = fs.readFileSync(contentPath, 'utf-8')\n return yaml.load(content) as ContentConfig\n}\n\nfunction loadTargetedSnapshotsConfig(targetDir: string): TargetedSnapshotsConfig | null {\n const targetedPath = path.join(targetDir, '.dcs', 'targeted-snapshots.yaml')\n if (!fs.existsSync(targetedPath)) {\n return null\n }\n const content = fs.readFileSync(targetedPath, 'utf-8')\n return yaml.load(content) as TargetedSnapshotsConfig\n}\n\nfunction getValidTextKeysForPage(\n contentConfig: ContentConfig | null,\n pageSlug: string\n): Map<string, string> {\n const keys = new Map<string, string>()\n if (!contentConfig) return keys\n\n // Add global keys\n if (contentConfig.global) {\n for (const [key, value] of Object.entries(contentConfig.global)) {\n keys.set(key, typeof value === 'string' ? value : JSON.stringify(value))\n }\n }\n\n // Add page-specific keys\n const pageContent = contentConfig.pages?.[pageSlug]\n if (pageContent) {\n for (const [key, value] of Object.entries(pageContent)) {\n const fullKey = `${pageSlug}.${key}`\n keys.set(fullKey, typeof value === 'string' ? value : JSON.stringify(value))\n }\n }\n\n return keys\n}\n\n// =============================================================================\n// Page Resolution\n// =============================================================================\n\ninterface ResolvedPage {\n slug: string\n path: string\n config: PageConfig\n}\n\nasync function resolveAllPages(config: PagesConfig): Promise<ResolvedPage[]> {\n const resolved: ResolvedPage[] = []\n\n for (const page of config.pages) {\n if (page.type === 'dynamic' && page.instances) {\n // Expand dynamic pages into individual instances\n for (const instance of page.instances) {\n const instancePath = page.pathTemplate\n ? page.pathTemplate.replace(/:[\\w]+/, instance)\n : `${page.path}/${instance}`\n\n resolved.push({\n slug: `${page.slug}-${instance}`,\n path: instancePath,\n config: { ...page, slug: `${page.slug}-${instance}` },\n })\n }\n } else {\n resolved.push({\n slug: page.slug,\n path: page.path,\n config: page,\n })\n }\n }\n\n return resolved\n}\n\n// =============================================================================\n// DOM Helpers\n// =============================================================================\n\nasync function getDomPath(element: ElementHandle): Promise<string> {\n return element.evaluate((el: Element) => {\n const parts: string[] = []\n let current: Element | null = el\n\n while (current && current !== document.body) {\n let selector = current.tagName.toLowerCase()\n\n if (current.id) {\n selector += `#${current.id}`\n } else {\n const classes = Array.from(current.classList)\n .filter((c) => !c.match(/^(v-|nuxt-|vue-|react-|ng-|_)/))\n .slice(0, 2)\n .join('.')\n if (classes) {\n selector += `.${classes}`\n }\n }\n\n parts.unshift(selector)\n current = current.parentElement\n }\n\n return parts.join(' > ')\n })\n}\n\nasync function generateSectionDisplayName(\n element: ElementHandle,\n sectionType: string,\n index: number\n): Promise<string> {\n // Try to find a heading within the section\n const headingText = await element.evaluate((el: Element) => {\n const heading = el.querySelector('h1, h2, h3, h4, [class*=\"title\"], [class*=\"heading\"]')\n if (heading && heading.textContent) {\n return heading.textContent.trim().slice(0, 50)\n }\n return null\n })\n\n if (headingText) return headingText\n\n // Fall back to section type + index\n const typeLabels: Record<string, string> = {\n header: 'Header',\n footer: 'Footer',\n hero: 'Hero Section',\n content: 'Content Section',\n gallery: 'Gallery',\n cta: 'Call to Action',\n }\n\n return typeLabels[sectionType] || `Section ${index + 1}`\n}\n\n// =============================================================================\n// Text Key Detection\n// =============================================================================\n\nasync function detectTextKeys(\n _page: Page,\n sectionElement: ElementHandle,\n _pageSlug: string,\n _validTextKeys: Map<string, string>\n): Promise<TextKeyInfo[]> {\n const textKeys: TextKeyInfo[] = []\n\n // Find editable text and managed image markers within this section.\n const textKeyElements = await sectionElement.$$('[data-text-key], [data-dcs-text], [data-dcs-image-key]')\n\n for (const element of textKeyElements) {\n const keyInfo = await element.evaluate((el: Element) => {\n const rect = el.getBoundingClientRect()\n const explicitImageUrl = el.getAttribute('data-dcs-image-url')\n const imageUrl = el instanceof HTMLImageElement ? el.currentSrc || el.src : ''\n return {\n key: el.getAttribute('data-text-key') || el.getAttribute('data-dcs-text') || el.getAttribute('data-dcs-image-key') || '',\n elementType: el.tagName.toLowerCase(),\n currentValue: (explicitImageUrl || imageUrl || el.textContent?.trim() || '').slice(0, 500),\n bounds: {\n x: rect.left + window.scrollX,\n y: rect.top + window.scrollY,\n width: rect.width,\n height: rect.height,\n },\n }\n })\n\n if (keyInfo.key && !textKeys.some((textKey) => textKey.key === keyInfo.key)) {\n const domPath = await getDomPath(element)\n textKeys.push({\n key: keyInfo.key,\n currentValue: keyInfo.currentValue,\n elementType: keyInfo.elementType,\n domPath,\n bounds: {\n x: Math.round(keyInfo.bounds.x),\n y: Math.round(keyInfo.bounds.y),\n width: Math.round(keyInfo.bounds.width),\n height: Math.round(keyInfo.bounds.height),\n },\n })\n }\n }\n\n return textKeys\n}\n\n// =============================================================================\n// Fallback Section Detection\n// =============================================================================\n\nasync function detectFallbackContentSections(\n page: Page,\n existingSections: SectionInfo[]\n): Promise<ElementHandle[]> {\n // Find large content blocks that might be sections\n const candidates = await page.$$('section, article, div[class*=\"section\"], div[class*=\"container\"]')\n\n const results: ElementHandle[] = []\n const existingBounds = existingSections.map((s) => s.bounds)\n\n for (const candidate of candidates) {\n const bounds = await candidate.evaluate((el: Element) => {\n const rect = el.getBoundingClientRect()\n return {\n y: rect.top + window.scrollY,\n height: rect.height,\n }\n })\n\n // Skip if too small or overlaps with existing sections\n if (bounds.height < 100) continue\n\n const overlaps = existingBounds.some(\n (eb) =>\n bounds.y < eb.y + eb.height && bounds.y + bounds.height > eb.y\n )\n\n if (!overlaps) {\n results.push(candidate)\n }\n }\n\n return results\n}\n\n// =============================================================================\n// Page Capture\n// =============================================================================\n\nasync function capturePageSnapshot(\n browser: Browser,\n pageInfo: ResolvedPage,\n snapshotConfig: PagesConfig['snapshot'],\n siteSlug: string,\n validTextKeys: Map<string, string>,\n outputDir: string,\n verbose: boolean\n): Promise<PageSnapshot> {\n const context = await browser.newContext({\n viewport: snapshotConfig.viewport,\n ignoreHTTPSErrors: true,\n reducedMotion: 'reduce',\n })\n const page = await context.newPage()\n\n // Build page URL and append dcs-hide-ribbon to suppress preview ribbon in screenshots\n const rawUrl = pageInfo.path.startsWith('http')\n ? pageInfo.path\n : `${process.env.SITE_BASE_URL || 'http://localhost:5173'}${pageInfo.path}`\n const pageUrl = new URL(rawUrl)\n pageUrl.searchParams.set('dcs-hide-ribbon', '')\n const url = pageUrl.toString()\n\n if (verbose) {\n console.log(` Capturing: ${pageInfo.slug} (${url})`)\n }\n\n try {\n await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30000 })\n await disableCaptureMotion(page)\n await page.waitForLoadState('networkidle', { timeout: 10000 }).catch(() => {\n if (verbose) console.log(` Note: networkidle timeout, continuing anyway`)\n })\n } catch (error) {\n console.error(` Failed to load: ${error}`)\n await context.close()\n throw error\n }\n\n await page.waitForTimeout(snapshotConfig.waitAfterLoad)\n await waitForAboveFoldAssets(page)\n\n // Create output directory for this page\n const pageOutputDir = path.join(outputDir, siteSlug, pageInfo.slug)\n fs.mkdirSync(path.join(pageOutputDir, 'sections'), { recursive: true })\n\n const title = await page.title()\n\n // Scroll through the page once so lazy-loaded content is present before the full-page capture.\n let pageHeight = await lazyLoadPage(page, snapshotConfig.viewport.height)\n await resetScrollForFullPageCapture(page)\n\n // Capture full page screenshot with fixed nav pinned to the document origin.\n const fullPagePath = path.join(pageOutputDir, 'full-page.png')\n await withFullPageCaptureLayout(page, () =>\n page.screenshot({\n path: fullPagePath,\n fullPage: snapshotConfig.captureFullPage,\n })\n )\n\n // Capture the header/hero-focused card image at the configured desktop viewport.\n const thumbnailPath = path.join(pageOutputDir, 'thumbnail.png')\n await captureHeaderThumbnail(page, thumbnailPath, snapshotConfig.viewport)\n\n // Ensure we're at scroll position 0 before capturing bounds.\n await page.evaluate(() => window.scrollTo(0, 0))\n await page.waitForTimeout(100)\n\n // Detect sections\n const sections: SectionInfo[] = []\n const seenDomPaths = new Set<string>()\n\n // Helper to get accurate bounds for fixed/sticky elements\n // For fixed/sticky elements, we need to calculate their \"document position\"\n // based on their role (header = top, footer = bottom)\n const getAccurateBounds = async (el: ElementHandle, sectionType?: string) => {\n return el.evaluate((element: Element, type: string | undefined) => {\n const rect = element.getBoundingClientRect()\n const style = window.getComputedStyle(element)\n const position = style.position\n const isFixed = position === 'fixed' || position === 'sticky'\n \n let y = rect.top + window.scrollY\n \n // For fixed/sticky headers, they should be at y=0 (top of page)\n if (isFixed && (type === 'header' || element.tagName.toLowerCase() === 'header')) {\n y = 0\n }\n // For fixed/sticky footers, they should be at the bottom of the page\n else if (isFixed && (type === 'footer' || element.tagName.toLowerCase() === 'footer')) {\n y = document.documentElement.scrollHeight - rect.height\n }\n \n return {\n x: rect.left + window.scrollX,\n y,\n width: rect.width,\n height: rect.height,\n isFixed,\n }\n }, sectionType)\n }\n\n // Phase 1: Explicit [data-section] elements\n const explicitElements = await page.$$(SECTION_SELECTORS.explicit)\n\n for (let i = 0; i < explicitElements.length; i++) {\n const element = explicitElements[i]\n\n const domPath = await getDomPath(element)\n if (seenDomPaths.has(domPath)) continue\n seenDomPaths.add(domPath)\n\n // First, get basic metadata\n const basicMetadata = await element.evaluate((el: Element) => {\n return {\n sectionId: el.getAttribute('data-section') || '',\n sectionLabel: el.getAttribute('data-section-label') || '',\n sectionType: el.getAttribute('data-section-type') || '',\n isDynamic: el.hasAttribute('data-dynamic'),\n }\n })\n\n // Determine section type early so we can pass it to bounds calculation\n let resolvedType: SectionInfo['sectionType'] = 'content'\n if (basicMetadata.sectionType) {\n resolvedType = basicMetadata.sectionType as SectionInfo['sectionType']\n } else if (['header', 'footer'].includes(basicMetadata.sectionId)) {\n resolvedType = basicMetadata.sectionId as SectionInfo['sectionType']\n } else if (basicMetadata.sectionId.includes('hero')) {\n resolvedType = 'hero'\n } else if (basicMetadata.sectionId.includes('gallery')) {\n resolvedType = 'gallery'\n }\n\n // Get accurate bounds with fixed/sticky handling\n const bounds = await getAccurateBounds(element, resolvedType)\n\n if (!bounds || bounds.height < 10) continue\n\n const sectionId = basicMetadata.sectionId || `section-${i}`\n const sectionPath = path.join(pageOutputDir, 'sections', `${sectionId}.png`)\n\n try {\n await element.screenshot({ path: sectionPath })\n } catch {\n if (verbose) console.log(` Skipped section screenshot: ${sectionId}`)\n }\n\n const isStructuralOnly = ['header', 'footer'].includes(resolvedType)\n\n // Detect text keys\n let textKeys: TextKeyInfo[] = []\n if (!isStructuralOnly) {\n textKeys = await detectTextKeys(page, element, pageInfo.slug, validTextKeys)\n }\n\n const isEditable = !isStructuralOnly && textKeys.length > 0\n\n // Generate display name - prefer data-section-label, then heading, then fallback\n let displayName = basicMetadata.sectionLabel\n if (!displayName) {\n displayName = await generateSectionDisplayName(element, resolvedType, i)\n }\n\n if (verbose) {\n console.log(\n ` Section: ${displayName} (${sectionId}) at y=${Math.round(bounds.y)}${\n basicMetadata.isDynamic ? ' [dynamic]' : ''\n } - ${textKeys.length} text keys`\n )\n }\n\n sections.push({\n sectionId,\n sectionType: resolvedType,\n displayName,\n domPath,\n bounds: {\n x: Math.round(bounds.x),\n y: Math.round(bounds.y),\n width: Math.round(bounds.width),\n height: Math.round(bounds.height),\n },\n textKeys,\n isEditable,\n isStructuralOnly,\n })\n }\n\n // Phase 2: Type-based sections (header, footer, hero, etc.)\n for (const [sectionType, selector] of Object.entries(SECTION_SELECTORS)) {\n if (sectionType === 'explicit') continue\n\n const elements = await page.$$(selector)\n\n for (let i = 0; i < elements.length; i++) {\n const element = elements[i]\n\n const domPath = await getDomPath(element)\n if (seenDomPaths.has(domPath)) continue\n\n // Skip elements that are INSIDE an explicit [data-section]\n // This prevents nested elements from being detected as separate sections\n const isInsideExplicitSection = await element.evaluate((el: Element) => {\n // Walk up the DOM tree to check if any ancestor has data-section\n let current: HTMLElement | null = el.parentElement\n while (current) {\n if (current.dataset?.section) {\n return true\n }\n current = current.parentElement\n }\n return false\n })\n\n if (isInsideExplicitSection) {\n continue // Silently skip - these are intentionally nested inside explicit sections\n }\n\n // Skip elements that are ancestors of already-detected explicit sections\n // This prevents the <main> container from being detected as a \"content\" section\n // when it contains explicit [data-section] children\n const isAncestorOfExplicitSection = await element.evaluate((el: Element) => {\n const explicitSections = el.querySelectorAll('[data-section]')\n return explicitSections.length > 0\n })\n\n if (isAncestorOfExplicitSection) {\n if (verbose) {\n console.log(` Skipping ${sectionType} container (contains explicit sections)`)\n }\n continue\n }\n\n seenDomPaths.add(domPath)\n\n // Use accurate bounds calculation for fixed/sticky elements\n const bounds = await getAccurateBounds(element, sectionType)\n\n if (!bounds || bounds.height < 10) continue\n\n const sectionId = `${sectionType}-${i}`\n const sectionPath = path.join(pageOutputDir, 'sections', `${sectionId}.png`)\n\n try {\n await element.screenshot({ path: sectionPath })\n } catch {\n if (verbose) console.log(` Skipped section screenshot: ${sectionId}`)\n }\n\n const isStructuralOnly = ['header', 'footer'].includes(sectionType)\n const textKeys = isStructuralOnly\n ? []\n : await detectTextKeys(page, element, pageInfo.slug, validTextKeys)\n\n // Skip content sections that have no text keys - they add noise without value\n if (sectionType === 'content' && textKeys.length === 0) {\n continue\n }\n\n const isEditable = !isStructuralOnly && textKeys.length > 0\n const displayName = await generateSectionDisplayName(element, sectionType, i)\n\n if (verbose) {\n console.log(` Section: ${displayName} at y=${Math.round(bounds.y)}${bounds.isFixed ? ' [fixed]' : ''}`)\n }\n\n sections.push({\n sectionId,\n sectionType: sectionType as SectionInfo['sectionType'],\n displayName,\n domPath,\n bounds: {\n x: Math.round(bounds.x),\n y: Math.round(bounds.y),\n width: Math.round(bounds.width),\n height: Math.round(bounds.height),\n },\n textKeys,\n isEditable,\n isStructuralOnly,\n })\n }\n }\n\n // Fallback: If no content sections, try heuristic detection\n const hasContentSection = sections.some((s) => s.sectionType === 'content')\n if (!hasContentSection) {\n if (verbose) console.log(` No content sections found, trying fallback detection...`)\n\n const fallbackElements = await detectFallbackContentSections(page, sections)\n\n for (let i = 0; i < fallbackElements.length; i++) {\n const element = fallbackElements[i]\n\n const domPath = await getDomPath(element)\n if (seenDomPaths.has(domPath)) continue\n seenDomPaths.add(domPath)\n\n const bounds = await element.evaluate((el: Element) => {\n const rect = el.getBoundingClientRect()\n return {\n x: rect.left + window.scrollX,\n y: rect.top + window.scrollY,\n width: rect.width,\n height: rect.height,\n }\n })\n\n if (!bounds || bounds.height < 10) continue\n\n const sectionId = `content-fallback-${i}`\n const sectionPath = path.join(pageOutputDir, 'sections', `${sectionId}.png`)\n\n try {\n await element.screenshot({ path: sectionPath })\n } catch {\n if (verbose) console.log(` Skipped fallback section screenshot: ${sectionId}`)\n }\n\n const textKeys = await detectTextKeys(page, element, pageInfo.slug, validTextKeys)\n const displayName = await generateSectionDisplayName(element, 'content', i)\n\n if (verbose) {\n console.log(\n ` Fallback Section: ${displayName} at y=${Math.round(bounds.y)} (${textKeys.length} text keys)`\n )\n }\n\n sections.push({\n sectionId,\n sectionType: 'content',\n displayName,\n domPath,\n bounds: {\n x: Math.round(bounds.x),\n y: Math.round(bounds.y),\n width: Math.round(bounds.width),\n height: Math.round(bounds.height),\n },\n textKeys,\n isEditable: textKeys.length > 0,\n isStructuralOnly: false,\n })\n }\n }\n\n await context.close()\n\n // Sort sections by vertical position\n sections.sort((a, b) => a.bounds.y - b.bounds.y)\n\n return {\n pageSlug: pageInfo.slug,\n path: pageInfo.path,\n type: pageInfo.config.type,\n deletable: pageInfo.config.deletable,\n title: title || pageInfo.config.title,\n capturedAt: new Date().toISOString(),\n previewUrl: url,\n viewport: snapshotConfig.viewport,\n pageHeight,\n sections,\n }\n}\n\n// =============================================================================\n// Main Command\n// =============================================================================\n\nexport async function captureSnapshotsCommand(options: CaptureSnapshotsOptions): Promise<void> {\n const { target, baseUrl, dryRun, verbose, pages: targetedPages } = options\n const targetDir = path.resolve(target)\n\n console.log(chalk.blue('📸 Page Snapshot Capture'))\n console.log(chalk.gray('========================'))\n console.log(chalk.gray(`Target: ${targetDir}`))\n console.log(chalk.gray(`Base URL: ${baseUrl}`))\n\n // Set base URL for capture\n process.env.SITE_BASE_URL = baseUrl\n\n // Load configuration\n let config: PagesConfig\n try {\n config = loadPagesConfig(targetDir)\n } catch (error) {\n console.error(chalk.red('Error:'), (error as Error).message)\n console.log(chalk.yellow('\\nMake sure .dcs/pages.yaml exists in the target directory.'))\n process.exit(1)\n }\n\n console.log(chalk.gray(`Site: ${config.siteSlug}`))\n\n const outputDir = path.join(targetDir, config.snapshot.outputDir || '.dcs/snapshots')\n\n // Load content configuration (optional)\n const contentConfig = loadContentConfig(targetDir)\n if (contentConfig) {\n const pageCount = Object.keys(contentConfig.pages || {}).length\n const globalCount = Object.keys(contentConfig.global || {}).length\n console.log(chalk.gray(`Content: Loaded ${pageCount} pages, ${globalCount} global keys`))\n }\n\n // Check for targeted snapshots config\n const targetedConfig = loadTargetedSnapshotsConfig(targetDir)\n const isTargetedCapture = Boolean(\n (targetedPages && targetedPages.length > 0) ||\n (targetedConfig?.mode === 'targeted' && targetedConfig.pages.length > 0)\n )\n\n if (targetedConfig?.mode === 'skip') {\n console.log('')\n console.log(chalk.yellow('⏭️ Skip mode activated!'))\n console.log(chalk.gray(` Reason: ${targetedConfig.reason}`))\n console.log(chalk.gray(' No snapshots will be captured.'))\n\n if (!dryRun) {\n fs.mkdirSync(outputDir, { recursive: true })\n const skipMarker = {\n skipped: true,\n reason: targetedConfig.reason,\n triggeredBy: targetedConfig.triggeredBy,\n timestamp: new Date().toISOString(),\n }\n fs.writeFileSync(path.join(outputDir, 'skipped.json'), JSON.stringify(skipMarker, null, 2))\n }\n\n console.log(chalk.green('\\n✅ Snapshot capture skipped (as requested)'))\n return\n }\n\n // Resolve pages\n let pagesToCapture = await resolveAllPages(config)\n console.log(chalk.gray(`Found ${pagesToCapture.length} total pages in configuration`))\n\n // Apply filtering from command-line --pages option\n if (targetedPages && targetedPages.length > 0) {\n const targetedSlugs = new Set(targetedPages)\n const originalCount = pagesToCapture.length\n pagesToCapture = pagesToCapture.filter((p) => targetedSlugs.has(p.slug))\n console.log('')\n console.log(chalk.cyan(`🎯 Targeted mode: Filtering to ${pagesToCapture.length} of ${originalCount} pages`))\n }\n // Apply filtering from targeted-snapshots.yaml\n else if (targetedConfig?.mode === 'targeted' && targetedConfig.pages.length > 0) {\n const targetedSlugs = new Set(targetedConfig.pages)\n const originalCount = pagesToCapture.length\n pagesToCapture = pagesToCapture.filter((p) => targetedSlugs.has(p.slug))\n console.log('')\n console.log(chalk.cyan(`🎯 Targeted mode: Filtering to ${pagesToCapture.length} of ${originalCount} pages`))\n console.log(chalk.gray(` Reason: ${targetedConfig.reason}`))\n }\n\n // List pages to capture\n console.log('')\n console.log('Pages to capture:')\n for (const p of pagesToCapture) {\n console.log(chalk.gray(` - ${p.slug} (${p.config.type}) ${p.path}`))\n }\n\n if (pagesToCapture.length === 0) {\n console.log(chalk.yellow('\\n⚠️ No pages to capture!'))\n return\n }\n\n if (dryRun) {\n console.log(chalk.yellow('\\n--dry-run: Stopping before browser launch'))\n return\n }\n\n // Dynamic import of playwright to avoid requiring it when not using this command\n const { chromium } = await import('playwright')\n\n // Clear and create output directory\n if (fs.existsSync(outputDir)) {\n fs.rmSync(outputDir, { recursive: true })\n }\n fs.mkdirSync(outputDir, { recursive: true })\n\n const spinner = ora('Launching browser...').start()\n const browser = await chromium.launch()\n spinner.succeed('Browser launched')\n\n const snapshots: PageSnapshot[] = []\n const failures: string[] = []\n\n for (const pageInfo of pagesToCapture) {\n const pageSpinner = ora(`Capturing ${pageInfo.slug}...`).start()\n\n try {\n const validTextKeys = getValidTextKeysForPage(contentConfig, pageInfo.slug)\n const snapshot = await capturePageSnapshot(\n browser,\n pageInfo,\n config.snapshot,\n config.siteSlug,\n validTextKeys,\n outputDir,\n verbose || false\n )\n snapshots.push(snapshot)\n\n // Save individual page snapshot JSON\n const snapshotPath = path.join(outputDir, config.siteSlug, pageInfo.slug, 'snapshot.json')\n fs.writeFileSync(snapshotPath, JSON.stringify(snapshot, null, 2))\n\n pageSpinner.succeed(`${pageInfo.slug}: ${snapshot.sections.length} sections captured`)\n } catch (error) {\n const message = `${pageInfo.slug}: ${(error as Error).message}`\n failures.push(message)\n pageSpinner.fail(`${pageInfo.slug}: Failed - ${(error as Error).message}`)\n }\n }\n\n await browser.close()\n\n // Create manifest\n const manifest: SnapshotManifest = {\n siteSlug: config.siteSlug,\n capturedAt: new Date().toISOString(),\n capturedVersion: process.env.GITHUB_SHA,\n deploymentRef: process.env.GITHUB_REF || 'local',\n pagesConfigVersion: config.version,\n pages: snapshots.map((s) => ({\n pageSlug: s.pageSlug,\n pagePath: s.path,\n pageType: s.type,\n deletable: s.deletable,\n title: s.title,\n capturedAt: s.capturedAt,\n sectionCount: s.sections.length,\n hasSnapshot: true,\n })),\n }\n\n const manifestPath = path.join(outputDir, config.siteSlug, 'manifest.json')\n if (isTargetedCapture) {\n console.log(chalk.gray(' Targeted capture: leaving the existing remote manifest intact'))\n } else {\n fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2))\n }\n\n console.log('')\n console.log(chalk.green('✅ Snapshot capture complete!'))\n console.log(chalk.gray(` Captured ${snapshots.length} pages`))\n console.log(chalk.gray(` - Static: ${snapshots.filter((s) => s.type === 'static').length}`))\n console.log(chalk.gray(` - Index: ${snapshots.filter((s) => s.type === 'index').length}`))\n console.log(chalk.gray(` - Dynamic: ${snapshots.filter((s) => s.type === 'dynamic').length}`))\n console.log(chalk.gray(` Output: ${outputDir}`))\n\n if (failures.length > 0) {\n throw new Error(`Snapshot capture failed for ${failures.length} page(s): ${failures.join('; ')}`)\n }\n}\n"],"mappings":";;;AAIA,SAAS,eAAe;AACxB,OAAOA,YAAW;;;ACHhB,cAAW;;;ACEb,OAAOC,YAAW;;;ACGlB,OAAO,UAAU;AACjB,OAAO,SAAS;AAChB,OAAO,WAAW;;;ACElB,OAAO,UAAU;;;ACJjB,SAAS,iBAAwC;AAW1C,SAAS,IACd,KACA,MACA,OAAoD,CAAC,GAC1C;AACX,QAAM,UAA4B;AAAA,IAChC,UAAU;AAAA,IACV,KAAK,KAAK,MAAM,EAAE,GAAG,QAAQ,KAAK,GAAG,KAAK,IAAI,IAAI,QAAQ;AAAA,IAC1D,aAAa;AAAA,EACf;AACA,MAAI,KAAK,UAAU,OAAW,SAAQ,QAAQ,KAAK;AAEnD,QAAM,MAAM,UAAU,KAAK,MAAM,OAAO;AAExC,MAAI,IAAI,OAAO;AACb,WAAO,EAAE,IAAI,OAAO,MAAM,MAAM,QAAQ,IAAI,QAAQ,OAAO,IAAI,KAAK,GAAG,YAAY,KAAK;AAAA,EAC1F;AACA,SAAO;AAAA,IACL,IAAI,IAAI,WAAW;AAAA,IACnB,MAAM,IAAI;AAAA,IACV,QAAQ,OAAO,IAAI,WAAW,WAAW,IAAI,SAAS;AAAA,IACtD,QAAQ,OAAO,IAAI,WAAW,WAAW,IAAI,SAAS;AAAA,IACtD,YAAY;AAAA,EACd;AACF;AAGO,SAAS,cAAc,KAAa,WAA8B;AACvE,QAAM,MAAM,IAAI,KAAK,SAAS;AAC9B,SAAO,CAAC,IAAI;AACd;;;ACzBO,IAAM,UAAU;;;ACZhB,IAAM,gBAAiC;AAAA,EAC5C,MAAM;AAAA,EAEN,cAAuB;AACrB,WAAO,QAAQ,aAAa,YAAY,cAAc,YAAY,CAAC,MAAM,CAAC;AAAA,EAC5E;AAAA,EAEA,IAAI,SAAgC;AAClC,UAAM,MAAM,IAAI,YAAY;AAAA,MAC1B;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AACD,QAAI,CAAC,IAAI,GAAI,QAAO;AACpB,WAAO,IAAI,OAAO,QAAQ,OAAO,EAAE;AAAA,EACrC;AAAA,EAEA,IAAI,SAAiB,QAAsB;AAGzC,UAAM,MAAM,IAAI,YAAY;AAAA,MAC1B;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AACD,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,IAAI,MAAM,gCAAgC,IAAI,OAAO,KAAK,KAAK,IAAI,IAAI,EAAE;AAAA,IACjF;AAAA,EACF;AAAA,EAEA,OAAO,SAAuB;AAE5B,QAAI,YAAY,CAAC,2BAA2B,MAAM,SAAS,MAAM,OAAO,CAAC;AAAA,EAC3E;AACF;;;AC5CO,IAAM,oBAAqC;AAAA,EAChD,MAAM;AAAA,EAEN,cAAuB;AACrB,WAAO,QAAQ,aAAa,WAAW,cAAc,eAAe,CAAC,WAAW,CAAC;AAAA,EACnF;AAAA,EAEA,IAAI,SAAgC;AAClC,UAAM,MAAM,IAAI,eAAe,CAAC,UAAU,WAAW,SAAS,WAAW,OAAO,CAAC;AACjF,QAAI,CAAC,IAAI,GAAI,QAAO;AAEpB,WAAO,IAAI,OAAO,QAAQ,OAAO,EAAE;AAAA,EACrC;AAAA,EAEA,IAAI,SAAiB,QAAsB;AACzC,UAAM,MAAM;AAAA,MACV;AAAA,MACA,CAAC,SAAS,WAAW,GAAG,OAAO,IAAI,OAAO,IAAI,WAAW,SAAS,WAAW,OAAO;AAAA,MACpF,EAAE,OAAO,OAAO;AAAA,IAClB;AACA,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,IAAI,MAAM,2BAA2B,IAAI,OAAO,KAAK,KAAK,IAAI,IAAI,EAAE;AAAA,IAC5E;AAAA,EACF;AAAA,EAEA,OAAO,SAAuB;AAC5B,QAAI,eAAe,CAAC,SAAS,WAAW,SAAS,WAAW,OAAO,CAAC;AAAA,EACtE;AACF;;;ACbA,OAAOC,SAAQ;;;ACjBf,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,OAAO,QAAQ;AAEf,IAAM,UAAU;AAET,SAAS,YAAoB;AAClC,QAAMC,YAAW,QAAQ,IAAI;AAC7B,MAAIA,UAAU,QAAOA;AAErB,MAAI,QAAQ,aAAa,SAAS;AAChC,UAAMC,QAAO,QAAQ,IAAI,WAAW,KAAK,KAAK,GAAG,QAAQ,GAAG,WAAW,SAAS;AAChF,WAAO,KAAK,KAAKA,OAAM,OAAO;AAAA,EAChC;AACA,MAAI,QAAQ,aAAa,UAAU;AACjC,WAAO,KAAK,KAAK,GAAG,QAAQ,GAAG,WAAW,uBAAuB,OAAO;AAAA,EAC1E;AACA,QAAM,OAAO,QAAQ,IAAI,mBAAmB,KAAK,KAAK,GAAG,QAAQ,GAAG,SAAS;AAC7E,SAAO,KAAK,KAAK,MAAM,OAAO;AAChC;AAGO,SAAS,WAAW,SAAiB,KAAqB;AAC/D,QAAM,OAAO,QAAQ,QAAQ,oBAAoB,GAAG;AACpD,SAAO,KAAK,KAAK,UAAU,GAAG,GAAG,IAAI,IAAI,GAAG,EAAE;AAChD;AAGO,SAAS,kBAA0B;AACxC,QAAM,MAAM,UAAU;AACtB,KAAG,UAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AACrC,SAAO;AACT;;;ACzBA,OAAOC,SAAQ;AACf,OAAOC,SAAQ;AACf,OAAO,YAAY;AAInB,IAAM,MAAM;AACZ,IAAM,QAAQ;AACd,IAAM,cAAc,OAAO,KAAK,4BAA4B,MAAM;AAElE,SAAS,YAAoB;AAC3B,MAAI,WAAW;AACf,MAAI;AACF,eAAWC,IAAG,SAAS,EAAE;AAAA,EAC3B,QAAQ;AAAA,EAER;AAKA,QAAM,WAAW,GAAG,QAAQ,KAASA,IAAG,SAAS,CAAC;AAClD,SAAO,OAAO,WAAW,UAAU,aAAa,EAAE;AACpD;AAEO,IAAM,gBAAiC;AAAA,EAC5C,MAAM;AAAA,EAEN,cAAuB;AAGrB,WAAO;AAAA,EACT;AAAA,EAEA,IAAI,SAAgC;AAClC,QAAI;AACJ,QAAI;AACF,YAAMC,IAAG,aAAa,WAAW,SAAS,GAAG,GAAG,MAAM,EAAE,KAAK;AAAA,IAC/D,QAAQ;AACN,aAAO;AAAA,IACT;AACA,UAAM,QAAQ,IAAI,MAAM,GAAG;AAC3B,QAAI,MAAM,WAAW,KAAK,MAAM,CAAC,MAAM,MAAO,QAAO;AACrD,QAAI;AACF,YAAM,KAAK,OAAO,KAAK,MAAM,CAAC,GAAG,QAAQ;AACzC,YAAM,MAAM,OAAO,KAAK,MAAM,CAAC,GAAG,QAAQ;AAC1C,YAAM,aAAa,OAAO,KAAK,MAAM,CAAC,GAAG,QAAQ;AACjD,YAAM,WAAW,OAAO,iBAAiB,eAAe,UAAU,GAAG,EAAE;AACvE,eAAS,WAAW,GAAG;AACvB,YAAM,YAAY,OAAO,OAAO,CAAC,SAAS,OAAO,UAAU,GAAG,SAAS,MAAM,CAAC,CAAC;AAC/E,aAAO,UAAU,SAAS,MAAM;AAAA,IAClC,QAAQ;AAEN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,IAAI,SAAiB,QAAsB;AACzC,oBAAgB;AAChB,UAAM,KAAK,OAAO,YAAY,EAAE;AAChC,UAAM,SAAS,OAAO,eAAe,eAAe,UAAU,GAAG,EAAE;AACnE,UAAM,aAAa,OAAO,OAAO,CAAC,OAAO,OAAO,QAAQ,MAAM,GAAG,OAAO,MAAM,CAAC,CAAC;AAChF,UAAM,MAAM,OAAO,WAAW;AAC9B,UAAM,WAAW;AAAA,MACf;AAAA,MACA,GAAG,SAAS,QAAQ;AAAA,MACpB,IAAI,SAAS,QAAQ;AAAA,MACrB,WAAW,SAAS,QAAQ;AAAA,IAC9B,EAAE,KAAK,GAAG;AACV,IAAAA,IAAG,cAAc,WAAW,SAAS,GAAG,GAAG,UAAU,EAAE,UAAU,QAAQ,MAAM,IAAM,CAAC;AAAA,EACxF;AAAA,EAEA,OAAO,SAAuB;AAC5B,QAAI;AACF,MAAAA,IAAG,WAAW,WAAW,SAAS,GAAG,CAAC;AAAA,IACxC,QAAQ;AAAA,IAER;AAAA,EACF;AACF;;;AF/DA,IAAMC,OAAM;AAEZ,IAAM,UACJ;AAKF,IAAM,YACJ;AAKF,SAAS,WAAW,QAAgB,KAAyB;AAC3D,SAAO,IAAI,cAAc,CAAC,cAAc,mBAAmB,YAAY,MAAM,GAAG,EAAE,IAAI,CAAC;AACzF;AAKA,SAAS,mBAAmB,SAAuB;AACjD,MAAI;AACF,IAAAC,IAAG,WAAW,WAAW,SAASD,IAAG,CAAC;AAAA,EACxC,QAAQ;AAAA,EAER;AACF;AAUA,IAAI,mBAAmB;AAQhB,IAAM,eAAgC;AAAA,EAC3C,MAAM;AAAA,EAEN,cAAuB;AAKrB,WAAO,QAAQ,aAAa,WAAW,CAAC;AAAA,EAC1C;AAAA,EAEA,IAAI,SAAgC;AAClC,QAAI,iBAAkB,QAAO,cAAc,IAAI,OAAO;AAEtD,QAAI;AACJ,QAAI;AACF,YAAME,IAAG,aAAa,WAAW,SAASC,IAAG,GAAG,MAAM,EAAE,KAAK;AAAA,IAC/D,QAAQ;AAIN,aAAO,cAAc,IAAI,OAAO;AAAA,IAClC;AACA,QAAI,CAAC,IAAK,QAAO;AAEjB,UAAM,MAAM,WAAW,WAAW,EAAE,kBAAkB,IAAI,CAAC;AAC3D,QAAI,CAAC,IAAI,IAAI;AACX,UAAI,IAAI,YAAY;AAIlB,2BAAmB;AACnB,eAAO,cAAc,IAAI,OAAO;AAAA,MAClC;AAKA,aAAO;AAAA,IACT;AACA,WAAO,IAAI,OAAO,QAAQ,UAAU,EAAE;AAAA,EACxC;AAAA,EAEA,IAAI,SAAiB,QAAsB;AACzC,QAAI,kBAAkB;AACpB,oBAAc,IAAI,SAAS,MAAM;AAMjC,yBAAmB,OAAO;AAC1B;AAAA,IACF;AAEA,oBAAgB;AAChB,UAAM,MAAM,WAAW,SAAS,EAAE,qBAAqB,OAAO,CAAC;AAC/D,QAAI,CAAC,IAAI,MAAM,CAAC,IAAI,OAAO,KAAK,GAAG;AAMjC,yBAAmB;AACnB,oBAAc,IAAI,SAAS,MAAM;AAOjC,yBAAmB,OAAO;AAC1B;AAAA,IACF;AACA,IAAAD,IAAG,cAAc,WAAW,SAASC,IAAG,GAAG,IAAI,OAAO,KAAK,GAAG,EAAE,UAAU,QAAQ,MAAM,IAAM,CAAC;AAK/F,kBAAc,OAAO,OAAO;AAAA,EAC9B;AAAA,EAEA,OAAO,SAAuB;AAI5B,uBAAmB,OAAO;AAC1B,kBAAc,OAAO,OAAO;AAAA,EAC9B;AACF;;;AG/IA,IAAM,aAAgC,CAAC,eAAe,mBAAmB,YAAY;AAErF,IAAI;AACJ,IAAI,WAAmC;AAEvC,SAAS,UAA2B;AAClC,aAAW,WAAW,YAAY;AAChC,QAAI;AACF,UAAI,QAAQ,YAAY,EAAG,QAAO;AAAA,IACpC,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO;AACT;AAGO,SAAS,cAA+B;AAC7C,MAAI,SAAU,QAAO;AACrB,MAAI,CAAC,SAAU,YAAW,QAAQ;AAClC,SAAO;AACT;;;ARpBA,IAAM,UAAU;AAchB,SAAS,qBAAkC;AACzC,QAAM,SAAS,IAAI,KAAyB,EAAE,aAAa,UAAU,CAAC;AACtE,SAAO;AAAA,IACL,MAAM,MAAM,OAAO,IAAI,MAAM;AAAA,IAC7B,OAAO,MAAM;AAGX,UAAI,OAAO,IAAI,MAAM,EAAG,QAAO,OAAO,MAAM;AAAA,IAC9C;AAAA,EACF;AACF;AAEA,IAAI;AACJ,SAAS,iBAA8B;AACrC,MAAI,CAAC,YAAa,eAAc,mBAAmB;AACnD,SAAO;AACT;AAMA,IAAI;AAEJ,SAAS,OAAiC;AACxC,MAAI,UAAU,OAAW,QAAO;AAEhC,QAAM,MAAM,YAAY,EAAE,IAAI,OAAO;AACrC,MAAI,KAAK;AACP,QAAI;AACF,cAAQ,KAAK,MAAM,GAAG;AACtB,aAAO;AAAA,IACT,QAAQ;AAEN,kBAAY,EAAE,OAAO,OAAO;AAC5B,cAAQ;AACR,aAAO;AAAA,IACT;AAAA,EACF;AAGA,QAAM,SAAS,eAAe,EAAE,KAAK;AACrC,MAAI,QAAQ;AACV,gBAAY,EAAE,IAAI,SAAS,KAAK,UAAU,MAAM,CAAC;AACjD,mBAAe,EAAE,MAAM;AACvB,YAAQ;AACR,WAAO;AAAA,EACT;AAEA,UAAQ;AACR,SAAO;AACT;AAEA,SAAS,QAAQ,OAAgC;AAC/C,cAAY,EAAE,IAAI,SAAS,KAAK,UAAU,KAAK,CAAC;AAChD,UAAQ;AAER,iBAAe,EAAE,MAAM;AACzB;AAKO,SAAS,iBAAiB,aAAsC;AACrE,UAAQ,WAAW;AACrB;AAaO,SAAS,mBAAyB;AACvC,cAAY,EAAE,OAAO,OAAO;AAC5B,iBAAe,EAAE,MAAM;AACvB,UAAQ;AACV;AAKO,SAAS,kBAA2B;AACzC,QAAM,QAAQ,KAAK;AACnB,MAAI,CAAC,MAAO,QAAO;AAGnB,QAAM,WAAW,IAAI,KAAK;AAC1B,SAAO,MAAM,YAAY,KAAK,IAAI,IAAI;AACxC;AAOO,SAAS,mBAA4B;AAC1C,SAAO,KAAK,MAAM;AACpB;AAKO,SAAS,sBAA0C;AACxD,SAAO,KAAK,GAAG;AACjB;AAaO,SAAS,iBAAqC;AACnD,SAAO,KAAK,GAAG;AACjB;AAKO,SAAS,kBAAsC;AACpD,SAAO,KAAK,GAAG;AACjB;AAKO,SAAS,kBAAkB,aAAqB,WAAyB;AAC9E,QAAM,QAAQ,KAAK;AACnB,MAAI,CAAC,MAAO;AAEZ,UAAQ;AAAA,IACN,GAAG;AAAA,IACH;AAAA,IACA,WAAW,KAAK,IAAI,IAAI,YAAY;AAAA,EACtC,CAAC;AACH;AAKO,SAAS,wBAAgC;AAC9C,SAAO,YAAY,EAAE;AACvB;;;ADjLA,IAAM,kBAAkB;AAmCjB,IAAM,2BAAN,cAAuC,MAAM;AAAA,EAClD,YAAY,UAAU,oDAAoD;AACxE,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAMO,IAAM,sBAAN,cAAkC,MAAM;AAAA,EAC7C,YAAY,UAAU,8EAA8E;AAClG,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAKA,SAAS,MAAM,IAA2B;AACxC,SAAO,IAAI,QAAQ,CAACC,aAAY,WAAWA,UAAS,EAAE,CAAC;AACzD;AAKA,eAAsB,gBAAgB,UAA6B,CAAC,GAAgC;AAClG,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,WAAW,QAAQ,YAAY;AAErC,QAAM,WAAW,MAAM,MAAM,GAAG,MAAM,2BAA2B;AAAA,IAC/D,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,gBAAgB;AAAA,IAClB;AAAA,IACA,MAAM,KAAK,UAAU,EAAE,WAAW,SAAS,CAAC;AAAA,EAC9C,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,QAAQ,MAAM,SAAS,KAAK;AAClC,UAAM,IAAI,MAAM,yCAAyC,KAAK,EAAE;AAAA,EAClE;AAEA,QAAM,OAAO,MAAM,SAAS,KAAK;AAEjC,SAAO;AAAA,IACL,YAAY,KAAK;AAAA,IACjB,UAAU,KAAK;AAAA,IACf,iBAAiB,KAAK;AAAA,IACtB,yBAAyB,KAAK;AAAA,IAC9B,WAAW,KAAK;AAAA,IAChB,UAAU,KAAK;AAAA,EACjB;AACF;AAKA,eAAe,aACb,YACA,QACqD;AACrD,QAAM,WAAW,MAAM,MAAM,GAAG,MAAM,0BAA0B;AAAA,IAC9D,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,gBAAgB;AAAA,IAClB;AAAA,IACA,MAAM,KAAK,UAAU;AAAA,MACnB,aAAa;AAAA,MACb,YAAY;AAAA,IACd,CAAC;AAAA,EACH,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,UAAMC,QAAO,MAAM,SAAS,KAAK;AACjC,WAAO,EAAE,OAAOA,MAAK,MAAyB;AAAA,EAChD;AAEA,QAAM,OAAO,MAAM,SAAS,KAAK;AAEjC,SAAO;AAAA,IACL,aAAa,KAAK;AAAA,IAClB,cAAc,KAAK;AAAA,IACnB,WAAW,KAAK;AAAA,IAChB,WAAW,KAAK;AAAA,IAChB,OAAO,KAAK;AAAA,IACZ,QAAQ,KAAK;AAAA,EACf;AACF;AAWA,eAAsB,MAAM,UAA6B,CAAC,GAA+B;AACvF,QAAM,SAAS,QAAQ,UAAU;AAEjC,QAAM,UAAU,IAAI,4BAA4B,EAAE,MAAM;AAGxD,MAAI;AACJ,MAAI;AACF,iBAAa,MAAM,gBAAgB,OAAO;AAAA,EAC5C,SAAS,OAAO;AACd,YAAQ,KAAK,gCAAgC;AAC7C,UAAM;AAAA,EACR;AAEA,UAAQ,KAAK;AAGb,UAAQ,IAAI;AACZ,UAAQ,IAAI,MAAM,KAAK,eAAe,GAAG,MAAM,KAAK,WAAW,eAAe,CAAC;AAC/E,UAAQ,IAAI,MAAM,KAAK,iBAAiB,GAAG,MAAM,OAAO,KAAK,WAAW,QAAQ,CAAC;AACjF,UAAQ,IAAI;AAGZ,MAAI;AACF,UAAM,KAAK,WAAW,uBAAuB;AAC7C,YAAQ,IAAI,MAAM,IAAI,gCAAgC,CAAC;AAAA,EACzD,QAAQ;AACN,YAAQ,IAAI,MAAM,IAAI,iCAAiC,CAAC;AAAA,EAC1D;AAEA,UAAQ,IAAI;AAGZ,QAAM,cAAc,IAAI,+BAA+B,EAAE,MAAM;AAE/D,MAAI,WAAW,WAAW;AAC1B,QAAM,WAAW,KAAK,IAAI,IAAI,WAAW,YAAY;AAErD,SAAO,KAAK,IAAI,IAAI,UAAU;AAC5B,UAAM,MAAM,WAAW,GAAI;AAE3B,QAAI;AACF,YAAM,SAAS,MAAM,aAAa,WAAW,YAAY,MAAM;AAE/D,UAAI,WAAW,QAAQ;AACrB,YAAI,OAAO,UAAU,yBAAyB;AAC5C;AAAA,QACF;AACA,YAAI,OAAO,UAAU,aAAa;AAChC,sBAAY;AACZ;AAAA,QACF;AACA,YAAI,OAAO,UAAU,iBAAiB;AACpC,sBAAY,KAAK,0BAA0B;AAC3C,gBAAM,IAAI,MAAM,6CAA6C;AAAA,QAC/D;AACA,YAAI,OAAO,UAAU,iBAAiB;AACpC,sBAAY,KAAK,uBAAuB;AACxC,gBAAM,IAAI,MAAM,4BAA4B;AAAA,QAC9C;AAAA,MACF,OAAO;AAEL,cAAM,cAAiC;AAAA,UACrC,aAAa,OAAO;AAAA,UACpB,cAAc,OAAO;AAAA,UACrB,WAAW,KAAK,IAAI,IAAI,OAAO,YAAY;AAAA,UAC3C,OAAO,OAAO;AAAA,UACd,QAAQ,OAAO;AAAA,QACjB;AAEA,yBAAiB,WAAW;AAE5B,oBAAY,QAAQ,oBAAoB,MAAM,MAAM,OAAO,KAAK,CAAC,EAAE;AACnE,eAAO;AAAA,MACT;AAAA,IACF,SAAS,OAAO;AAEd,UAAI,iBAAiB,SAAS,MAAM,QAAQ,SAAS,OAAO,GAAG;AAC7D;AAAA,MACF;AACA,YAAM;AAAA,IACR;AAAA,EACF;AAEA,cAAY,KAAK,0BAA0B;AAC3C,QAAM,IAAI,MAAM,6CAA6C;AAC/D;AAWA,eAAsB,mBACpB,cACA,UAA6B,CAAC,GACuB;AACrD,QAAM,SAAS,QAAQ,UAAU;AAEjC,QAAM,WAAW,MAAM,MAAM,GAAG,MAAM,4BAA4B;AAAA,IAChE,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,gBAAgB;AAAA,IAClB;AAAA,IACA,MAAM,KAAK,UAAU,EAAE,eAAe,aAAa,CAAC;AAAA,EACtD,CAAC;AAED,MAAI,SAAS,WAAW,KAAK;AAC3B,UAAM,IAAI,yBAAyB;AAAA,EACrC;AAEA,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,IAAI,MAAM,wCAAwC,SAAS,MAAM,IAAI;AAAA,EAC7E;AAEA,QAAM,OAAO,MAAM,SAAS,KAAK;AAEjC,SAAO;AAAA,IACL,aAAa,KAAK;AAAA,IAClB,WAAW,KAAK;AAAA,EAClB;AACF;;;ADtQA,eAAsB,aAAa,UAA6B,CAAC,GAAkB;AACjF,MAAI;AACF,UAAM,MAAa,OAAO;AAAA,EAC5B,SAAS,OAAO;AACd,QAAI,iBAAiB,OAAO;AAC1B,cAAQ,MAAMC,OAAM,IAAI,eAAe,GAAG,MAAM,OAAO;AAAA,IACzD;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAKA,eAAsB,gBAA+B;AACnD,mBAAiB;AACjB,UAAQ,IAAIA,OAAM,MAAM,QAAG,GAAG,yBAAyB;AACzD;AAKA,eAAsB,gBAA+B;AACnD,QAAM,QAAQ,oBAAoB;AAElC,MAAI,CAAC,OAAO;AACV,YAAQ,IAAIA,OAAM,OAAO,gBAAgB,CAAC;AAC1C,YAAQ,IAAI,OAAOA,OAAM,KAAK,WAAW,GAAG,kBAAkB;AAC9D;AAAA,EACF;AAEA,UAAQ,IAAI,iBAAiBA,OAAM,MAAM,KAAK,CAAC;AAC/C,UAAQ,IAAIA,OAAM,IAAI,gBAAgB,GAAGA,OAAM,IAAI,sBAAsB,CAAC,CAAC;AAC7E;;;AWxCA,OAAOC,YAAW;AAClB,OAAO,WAAW;;;ACElB,IAAMC,mBAAkB;AAejB,IAAM,eAAN,MAAmB;AAAA,EAChB;AAAA,EAER,YAAY,UAA+B,CAAC,GAAG;AAC7C,SAAK,SAAS,QAAQ,UAAU,QAAQ,IAAI,eAAeA;AAAA,EAC7D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,iBAAkC;AAC9C,UAAM,eAAe,gBAAgB;AACrC,QAAI;AACF,YAAM,EAAE,aAAa,UAAU,UAAU,IAAI,MAAM,mBAAmB,cAAc;AAAA,QAClF,QAAQ,KAAK;AAAA,MACf,CAAC;AACD,wBAAkB,UAAU,SAAS;AACrC,aAAO;AAAA,IACT,SAAS,OAAO;AACd,UAAI,iBAAiB,0BAA0B;AAG7C,yBAAiB;AACjB,cAAM,IAAI,oBAAoB;AAAA,MAChC;AAGA,YAAM,IAAI;AAAA,QACR,2DACE,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CACvD;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,QACZC,OACA,UAAuB,CAAC,GACZ;AAEZ,QAAI,cAAc,eAAe;AAGjC,QAAI,uBAAuB;AAC3B,QAAI,CAAC,gBAAgB,KAAK,gBAAgB,GAAG;AAC3C,oBAAc,MAAM,KAAK,eAAe;AACxC,6BAAuB;AAAA,IACzB;AAEA,QAAI,CAAC,aAAa;AAChB,YAAM,IAAI,oBAAoB,gDAAgD;AAAA,IAChF;AAEA,UAAM,OAAO,CAAC,UACZ,MAAM,GAAG,KAAK,MAAM,GAAGA,KAAI,IAAI;AAAA,MAC7B,GAAG;AAAA,MACH,SAAS;AAAA,QACP,GAAG,QAAQ;AAAA,QACX,eAAe,UAAU,KAAK;AAAA,QAC9B,gBAAgB;AAAA,MAClB;AAAA,IACF,CAAC;AAEH,QAAI,WAAW,MAAM,KAAK,WAAW;AAKrC,QAAI,SAAS,WAAW,OAAO,CAAC,wBAAwB,gBAAgB,GAAG;AACzE,YAAM,WAAW,MAAM,KAAK,eAAe;AAC3C,iBAAW,MAAM,KAAK,QAAQ;AAAA,IAChC;AAEA,QAAI,SAAS,WAAW,KAAK;AAE3B,uBAAiB;AACjB,YAAM,IAAI,oBAAoB;AAAA,IAChC;AAEA,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,QAAQ,MAAM,SAAS,KAAK;AAClC,YAAM,IAAI,MAAM,cAAc,SAAS,MAAM,MAAM,KAAK,EAAE;AAAA,IAC5D;AAEA,WAAO,SAAS,KAAK;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,YAAwC;AAC5C,WAAO,KAAK,QAAQ,mBAAmB;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,QAAQ,MAA6B;AACzC,WAAO,KAAK,QAAQ,qBAAqB,IAAI,EAAE;AAAA,EACjD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,mBACJ,MACA,WAC8D;AAC9D,QAAI;AACF,YAAM,EAAE,MAAM,IAAI,MAAM,KAAK,UAAU;AACvC,YAAM,OAAO,MAAM,KAAK,CAAC,MAAM,EAAE,SAAS,IAAI;AAE9C,UAAI,CAAC,MAAM;AACT,YAAI,cAAc,UAAU;AAE1B,gBAAM,YAAY,MAAM,KAAK,CAAC,MAAM,CAAC,SAAS,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC;AACvE,iBAAO,YACH,EAAE,OAAO,MAAM,SAAS,2BAA2B,IACnD,EAAE,OAAO,OAAO,SAAS,yDAAyD;AAAA,QACxF;AACA,eAAO,EAAE,OAAO,OAAO,SAAS,SAAS,IAAI,uCAAuC;AAAA,MACtF;AAEA,UAAI,cAAc,QAAQ;AACxB,eAAO,EAAE,OAAO,MAAM,MAAM,KAAK,KAAK;AAAA,MACxC;AAEA,UAAI,cAAc,QAAQ;AACxB,cAAM,UAAU,CAAC,SAAS,SAAS,QAAQ,EAAE,SAAS,KAAK,IAAI;AAC/D,eAAO,UACH,EAAE,OAAO,MAAM,MAAM,KAAK,KAAK,IAC/B,EAAE,OAAO,OAAO,MAAM,KAAK,MAAM,SAAS,6CAA6C;AAAA,MAC7F;AAEA,UAAI,cAAc,UAAU;AAC1B,eAAO,EAAE,OAAO,OAAO,SAAS,sBAAsB;AAAA,MACxD;AAEA,aAAO,EAAE,OAAO,MAAM;AAAA,IACxB,SAAS,OAAO;AACd,UAAI,iBAAiB,OAAO;AAC1B,eAAO,EAAE,OAAO,OAAO,SAAS,MAAM,QAAQ;AAAA,MAChD;AACA,aAAO,EAAE,OAAO,OAAO,SAAS,gBAAgB;AAAA,IAClD;AAAA,EACF;AACF;AAGA,IAAI,iBAAsC;AAEnC,SAAS,gBAAgB,SAA6C;AAC3E,MAAI,CAAC,gBAAgB;AACnB,qBAAiB,IAAI,aAAa,OAAO;AAAA,EAC3C;AACA,SAAO;AACT;;;AD5KA,eAAsB,mBAAkC;AACtD,MAAI,CAAC,iBAAiB,GAAG;AACvB,YAAQ,IAAIC,OAAM,OAAO,gBAAgB,CAAC;AAC1C,YAAQ,IAAI,OAAOA,OAAM,KAAK,WAAW,GAAG,kBAAkB;AAC9D,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,SAAS,gBAAgB;AAE/B,MAAI;AACF,UAAM,EAAE,MAAM,IAAI,MAAM,OAAO,UAAU;AAEzC,QAAI,MAAM,WAAW,GAAG;AACtB,cAAQ,IAAIA,OAAM,OAAO,iBAAiB,CAAC;AAC3C,cAAQ,IAAI,sDAAsD;AAClE;AAAA,IACF;AAEA,YAAQ,IAAIA,OAAM,KAAK,iBAAiB,CAAC;AAEzC,UAAM,QAAQ,IAAI,MAAM;AAAA,MACtB,MAAM;AAAA,QACJA,OAAM,KAAK,MAAM;AAAA,QACjBA,OAAM,KAAK,MAAM;AAAA,QACjBA,OAAM,KAAK,MAAM;AAAA,QACjBA,OAAM,KAAK,SAAS;AAAA,QACpBA,OAAM,KAAK,KAAK;AAAA,MAClB;AAAA,MACA,OAAO;AAAA,QACL,MAAM,CAAC;AAAA,QACP,QAAQ,CAAC;AAAA,MACX;AAAA,IACF,CAAC;AAED,eAAW,QAAQ,OAAO;AACxB,YAAM,KAAK;AAAA,QACT,KAAK;AAAA,QACL,KAAK;AAAA,QACL,WAAW,KAAK,IAAI;AAAA,QACpB,KAAK;AAAA,QACL,KAAK,iBAAiBA,OAAM,IAAI,gBAAgB;AAAA,MAClD,CAAC;AAAA,IACH;AAEA,YAAQ,IAAI,MAAM,SAAS,CAAC;AAC5B,YAAQ,IAAI;AACZ,YAAQ,IAAIA,OAAM,IAAI,UAAU,MAAM,MAAM,UAAU,CAAC;AAAA,EACzD,SAAS,OAAO;AACd,QAAI,iBAAiB,OAAO;AAC1B,cAAQ,MAAMA,OAAM,IAAI,QAAQ,GAAG,MAAM,OAAO;AAAA,IAClD;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAKA,eAAsB,gBAAgB,MAA6B;AACjE,MAAI,CAAC,iBAAiB,GAAG;AACvB,YAAQ,IAAIA,OAAM,OAAO,gBAAgB,CAAC;AAC1C,YAAQ,IAAI,OAAOA,OAAM,KAAK,WAAW,GAAG,kBAAkB;AAC9D,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,SAAS,gBAAgB;AAE/B,MAAI;AACF,UAAM,OAAO,MAAM,OAAO,QAAQ,IAAI;AACtC,uBAAmB,IAAI;AAAA,EACzB,SAAS,OAAO;AACd,QAAI,iBAAiB,OAAO;AAC1B,cAAQ,MAAMA,OAAM,IAAI,QAAQ,GAAG,MAAM,OAAO;AAAA,IAClD;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAEA,SAAS,WAAW,MAAsB;AACxC,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,aAAOA,OAAM,QAAQ,IAAI;AAAA,IAC3B,KAAK;AACH,aAAOA,OAAM,IAAI,IAAI;AAAA,IACvB,KAAK;AACH,aAAOA,OAAM,OAAO,IAAI;AAAA,IAC1B,KAAK;AACH,aAAOA,OAAM,KAAK,IAAI;AAAA,IACxB;AACE,aAAO;AAAA,EACX;AACF;AAEA,SAAS,mBAAmB,MAAkB;AAC5C,UAAQ,IAAI;AACZ,UAAQ,IAAIA,OAAM,KAAK,cAAc,CAAC;AACtC,UAAQ,IAAI;AACZ,UAAQ,IAAI,mBAAmBA,OAAM,KAAK,KAAK,IAAI,CAAC;AACpD,UAAQ,IAAI,mBAAmB,KAAK,IAAI;AACxC,UAAQ,IAAI,mBAAmB,WAAW,KAAK,IAAI,CAAC;AACpD,UAAQ,IAAI,mBAAmB,KAAK,WAAW;AAC/C,UAAQ,IAAI,mBAAmBA,OAAM,IAAI,KAAK,SAAS,CAAC;AACxD,MAAI,KAAK,eAAe;AACtB,YAAQ,IAAI,mBAAmBA,OAAM,MAAM,KAAK,aAAa,CAAC;AAAA,EAChE,OAAO;AACL,YAAQ,IAAI,mBAAmBA,OAAM,IAAI,gBAAgB,CAAC;AAAA,EAC5D;AACA,UAAQ,IAAI;AACd;;;AEpHA,OAAOC,SAAQ;AACf,OAAOC,WAAU;AACjB,OAAOC,YAAW;AAClB,OAAOC,UAAS;;;ACPhB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ADwBA,eAAsB,YAAY,SAAqC;AACrE,QAAM,EAAE,UAAU,UAAU,YAAY,OAAO,SAAS,KAAK,SAAS,OAAO,QAAQ,MAAM,IAAI;AAG/F,MAAI,CAAC,iBAAiB,GAAG;AACvB,YAAQ,IAAIC,OAAM,OAAO,gBAAgB,CAAC;AAC1C,YAAQ,IAAI,OAAOA,OAAM,KAAK,WAAW,GAAG,kBAAkB;AAC9D,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,QAAM,UAAUC,KAAI,2BAA2B,EAAE,MAAM;AACvD,QAAM,SAAS,gBAAgB;AAE/B,MAAI;AACF,UAAM,EAAE,MAAM,IAAI,MAAM,OAAO,UAAU;AACzC,UAAM,eAAe,MAAM,KAAK,CAAC,MAAM,EAAE,SAAS,QAAQ;AAE1D,QAAI,cAAc;AAChB,UAAI,CAAC,CAAC,SAAS,SAAS,QAAQ,EAAE,SAAS,aAAa,IAAI,GAAG;AAC7D,gBAAQ,KAAK,uCAAuC,QAAQ,GAAG;AAC/D,gBAAQ,KAAK,CAAC;AAAA,MAChB;AACA,cAAQ,QAAQ,SAAS,QAAQ,kBAAkB,aAAa,IAAI,GAAG;AAAA,IACzE,OAAO;AAEL,YAAM,YAAY,MAAM,KAAK,CAAC,MAAM,CAAC,SAAS,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC;AACvE,UAAI,CAAC,WAAW;AACd,gBAAQ,KAAK,wDAAwD;AACrE,gBAAQ,KAAK,CAAC;AAAA,MAChB;AACA,cAAQ,KAAK,sBAAsB,QAAQ,EAAE;AAAA,IAC/C;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,KAAK,gCAAgC;AAC7C,QAAI,iBAAiB,OAAO;AAC1B,cAAQ,MAAMD,OAAM,IAAI,QAAQ,GAAG,MAAM,OAAO;AAAA,IAClD;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,QAAM,YAAYE,MAAK,QAAQ,MAAM;AACrC,UAAQ,IAAIF,OAAM,IAAI,qBAAqB,SAAS,EAAE,CAAC;AAGvD,QAAM,iBAA2B,CAAC;AAClC,QAAM,aAAY,oBAAI,KAAK,GAAE,YAAY;AAEzC,MAAI;AAEF,UAAM,WAAW,iBAAiB,EAAE,UAAU,UAAU,WAAW,UAAU,CAAC;AAC9E,mBAAe,KAAK,MAAM,UAAU,WAAW,kBAAkB,UAAU,EAAE,QAAQ,MAAM,CAAC,CAAC;AAG7F,UAAM,YAAY,kBAAkB,EAAE,UAAU,UAAU,CAAC;AAC3D,mBAAe,KAAK,MAAM,UAAU,WAAW,mBAAmB,WAAW,EAAE,QAAQ,MAAM,CAAC,CAAC;AAG/F,UAAM,cAAc,oBAAoB,EAAE,UAAU,CAAC;AACrD,mBAAe,KAAK,MAAM,UAAU,WAAW,qBAAqB,aAAa,EAAE,QAAQ,MAAM,CAAC,CAAC;AAGnG,UAAM,UAAU,gBAAgB,EAAE,UAAU,UAAU,CAAC;AACvD,mBAAe,KAAK,MAAM,UAAU,WAAW,iBAAiB,SAAS,EAAE,QAAQ,MAAM,CAAC,CAAC;AAG3F,UAAM,qBAAqB,2BAA2B;AACtD,mBAAe;AAAA,MACb,MAAM,UAAU,WAAW,+BAA+B,oBAAoB,EAAE,QAAQ,MAAM,CAAC;AAAA,IACjG;AAGA,UAAM,sBAAsB,4BAA4B,EAAE,SAAS,CAAC;AACpE,mBAAe;AAAA,MACb,MAAM,UAAU,WAAW,mCAAmC,qBAAqB,EAAE,QAAQ,MAAM,CAAC;AAAA,IACtG;AAGA,UAAM,iBAAiB,uBAAuB;AAC9C,mBAAe;AAAA,MACb,MAAM,UAAU,WAAW,qCAAqC,gBAAgB,EAAE,QAAQ,MAAM,CAAC;AAAA,IACnG;AAGA,UAAM,cAAc,oBAAoB,EAAE,UAAU,UAAU,CAAC;AAC/D,mBAAe,KAAK,MAAM,UAAU,WAAW,oBAAoB,aAAa,EAAE,QAAQ,MAAM,CAAC,CAAC;AAGlG,UAAM,SAAS,wBAAwB,EAAE,UAAU,CAAC;AACpD,mBAAe,KAAK,MAAM,UAAU,WAAW,2BAA2B,QAAQ,EAAE,QAAQ,MAAM,CAAC,CAAC;AAGpG,UAAM,SAAS,+BAA+B;AAC9C,mBAAe,KAAK,MAAM,UAAU,WAAW,kCAAkC,QAAQ,EAAE,QAAQ,MAAM,CAAC,CAAC;AAG3G,UAAM,SAAS,2BAA2B;AAC1C,mBAAe,KAAK,MAAM,UAAU,WAAW,8BAA8B,QAAQ,EAAE,QAAQ,MAAM,CAAC,CAAC;AAAA,EAEzG,SAAS,OAAO;AACd,QAAI,iBAAiB,OAAO;AAC1B,cAAQ,MAAMA,OAAM,IAAI,QAAQ,GAAG,MAAM,OAAO;AAAA,IAClD;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,UAAQ,IAAI;AACZ,UAAQ,IAAIA,OAAM,KAAK,kBAAkB,CAAC;AAC1C,aAAW,QAAQ,gBAAgB;AACjC,YAAQ,IAAI,KAAKA,OAAM,MAAM,QAAG,CAAC,IAAI,IAAI,EAAE;AAAA,EAC7C;AAGA,UAAQ,IAAI;AACZ,UAAQ,IAAIA,OAAM,KAAK,aAAa,CAAC;AACrC,UAAQ,IAAI,6BAA6BA,OAAM,KAAK,iCAAiC,CAAC;AACtF,UAAQ,IAAI,kDAAkD;AAC9D,UAAQ,IAAI,4BAA4BA,OAAM,KAAK,SAAS,GAAG,WAAW;AAC1E,UAAQ,IAAI;AACd;AAWA,eAAe,UACb,WACA,cACA,SACA,SACiB;AACjB,QAAM,WAAWE,MAAK,KAAK,WAAW,YAAY;AAElD,MAAI,QAAQ,QAAQ;AAClB,YAAQ,IAAIF,OAAM,IAAI,iBAAiB,YAAY,EAAE,CAAC;AACtD,WAAO;AAAA,EACT;AAGA,MAAI;AACF,UAAMG,IAAG,OAAO,QAAQ;AACxB,QAAI,CAAC,QAAQ,OAAO;AAClB,cAAQ,IAAIH,OAAM,OAAO,qBAAqB,YAAY,EAAE,CAAC;AAC7D,aAAO;AAAA,IACT;AAAA,EACF,QAAQ;AAAA,EAER;AAGA,QAAMG,IAAG,MAAMD,MAAK,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAG1D,QAAMC,IAAG,UAAU,UAAU,SAAS,MAAM;AAE5C,SAAO;AACT;AAMA,SAAS,iBAAiB,MAKf;AACT,SAAO;AAAA;AAAA;AAAA;AAAA,aAII,KAAK,QAAQ;AAAA,aACb,KAAK,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,eAyBX,KAAK,SAAS;AAAA;AAAA,iBAEZ,KAAK,SAAS;AAAA;AAE/B;AAEA,SAAS,kBAAkB,MAAuD;AAChF,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAQG,KAAK,QAAQ;AAAA,gBACT,KAAK,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAyB9B;AAEA,SAAS,oBAAoB,MAAqC;AAChE,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,gBASO,KAAK,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAS9B;AAEA,SAAS,gBAAgB,MAAuD;AAC9E,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA,gBAKO,KAAK,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA,eAKf,KAAK,QAAQ;AAAA;AAAA;AAAA,mBAGT,KAAK,QAAQ;AAAA;AAAA,yBAEP,KAAK,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA2BtC;AAEA,SAAS,6BAAqC;AAC5C,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAkCT;AAEA,SAAS,4BAA4B,MAAoC;AACvE,SAAO,qCAAqC,KAAK,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4D3D;AAEA,SAAS,oBAAoB,MAAuD;AAClF,SAAO,+BAA+B,KAAK,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAqBjC,KAAK,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAelC;AAEA,SAAS,yBAAiC;AACxC,SAAO;AACT;AAEA,SAAS,wBAAwB,MAAqC;AACpE,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBASW,KAAK,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAuElC;AAEA,SAAS,iCAAyC;AAChD,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAoHT;AAEA,SAAS,6BAAqC;AAC5C,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiIT;;;AEzyBA,OAAOC,SAAQ;AACf,OAAOC,WAAU;AACjB,OAAOC,YAAW;AAClB,OAAO,UAAU;AAkBjB,eAAsB,gBAAgB,SAAyC;AAC7E,QAAM,EAAE,SAAS,KAAK,MAAM,OAAO,UAAU,MAAM,IAAI;AAEvD,QAAM,YAAYD,MAAK,QAAQ,MAAM;AACrC,QAAM,SAASA,MAAK,KAAK,WAAW,MAAM;AAE1C,UAAQ,IAAIC,OAAM,KAAK,iCAAiC,CAAC;AACzD,UAAQ,IAAIA,OAAM,IAAI,cAAc,SAAS,EAAE,CAAC;AAChD,UAAQ,IAAI;AAGZ,MAAI;AACF,UAAMF,IAAG,OAAO,MAAM;AAAA,EACxB,QAAQ;AACN,YAAQ,IAAIE,OAAM,IAAI,0BAA0B,CAAC;AACjD,YAAQ,IAAI,OAAOA,OAAM,KAAK,UAAU,GAAG,8BAA8B;AACzE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,UAA8B,CAAC;AAGrC,UAAQ,KAAK,MAAM,iBAAiB,QAAQ,OAAO,CAAC;AACpD,UAAQ,KAAK,MAAM,kBAAkB,QAAQ,OAAO,CAAC;AACrD,UAAQ,KAAK,MAAM,oBAAoB,QAAQ,OAAO,CAAC;AACvD,UAAQ,KAAK,MAAM,gBAAgB,QAAQ,OAAO,CAAC;AAGnD,UAAQ,IAAIA,OAAM,KAAK,qBAAqB,CAAC;AAC7C,UAAQ,IAAI;AAEZ,MAAI,YAAY;AAChB,MAAI,cAAc;AAElB,aAAW,UAAU,SAAS;AAC5B,QAAI,CAAC,OAAO,SAAS,OAAO,OAAO,SAAS,GAAG;AAC7C,kBAAY;AACZ,cAAQ,IAAI,GAAGA,OAAM,IAAI,QAAG,CAAC,IAAI,OAAO,IAAI,EAAE;AAC9C,iBAAW,SAAS,OAAO,QAAQ;AACjC,gBAAQ,IAAI,OAAOA,OAAM,IAAI,QAAQ,CAAC,IAAI,KAAK,EAAE;AAAA,MACnD;AAAA,IACF,WAAW,OAAO,SAAS,SAAS,GAAG;AACrC,oBAAc;AACd,cAAQ,IAAI,GAAGA,OAAM,OAAO,GAAG,CAAC,IAAI,OAAO,IAAI,EAAE;AAAA,IACnD,OAAO;AACL,cAAQ,IAAI,GAAGA,OAAM,MAAM,QAAG,CAAC,IAAI,OAAO,IAAI,EAAE;AAAA,IAClD;AAEA,eAAW,WAAW,OAAO,UAAU;AACrC,cAAQ,IAAI,OAAOA,OAAM,OAAO,UAAU,CAAC,IAAI,OAAO,EAAE;AAAA,IAC1D;AAAA,EACF;AAEA,UAAQ,IAAI;AAEZ,MAAI,WAAW;AACb,YAAQ,IAAIA,OAAM,IAAI,gCAAgC,CAAC;AACvD,QAAI,KAAK;AACP,cAAQ,IAAIA,OAAM,OAAO,8DAA8D,CAAC;AAAA,IAC1F;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB,WAAW,aAAa;AACtB,YAAQ,IAAIA,OAAM,OAAO,kCAAkC,CAAC;AAAA,EAC9D,OAAO;AACL,YAAQ,IAAIA,OAAM,MAAM,oCAAoC,CAAC;AAAA,EAC/D;AACF;AAMA,eAAe,iBAAiB,QAAgB,SAA6C;AAC3F,QAAM,WAAWD,MAAK,KAAK,QAAQ,WAAW;AAC9C,QAAM,SAA2B;AAAA,IAC/B,MAAM;AAAA,IACN,OAAO;AAAA,IACP,QAAQ,CAAC;AAAA,IACT,UAAU,CAAC;AAAA,EACb;AAEA,MAAI;AACF,UAAM,UAAU,MAAMD,IAAG,SAAS,UAAU,MAAM;AAClD,UAAM,SAAS,KAAK,KAAK,OAAO;AAGhC,QAAI,CAAC,OAAO,WAAW;AACrB,aAAO,OAAO,KAAK,mCAAmC;AACtD,aAAO,QAAQ;AAAA,IACjB;AAEA,QAAI,CAAC,OAAO,WAAW;AACrB,aAAO,OAAO,KAAK,mCAAmC;AACtD,aAAO,QAAQ;AAAA,IACjB,WAAW,CAAC,eAAe,KAAK,OAAO,SAAmB,GAAG;AAC3D,aAAO,OAAO,KAAK,4DAA4D;AAC/E,aAAO,QAAQ;AAAA,IACjB;AAGA,QAAI,CAAC,OAAO,iBAAiB;AAC3B,aAAO,SAAS,KAAK,sDAAsD;AAAA,IAC7E;AAEA,QAAI,CAAC,OAAO,gBAAgB;AAC1B,aAAO,SAAS,KAAK,2BAA2B;AAAA,IAClD;AAEA,QAAI,SAAS;AACX,cAAQ,IAAIE,OAAM,IAAI,gBAAgB,OAAO,SAAS,EAAE,CAAC;AACzD,cAAQ,IAAIA,OAAM,IAAI,gBAAgB,OAAO,SAAS,EAAE,CAAC;AAAA,IAC3D;AAAA,EACF,SAAS,OAAO;AACd,QAAK,MAAgC,SAAS,UAAU;AACtD,aAAO,OAAO,KAAK,gBAAgB;AAAA,IACrC,OAAO;AACL,aAAO,OAAO,KAAK,gBAAiB,MAAgB,OAAO,EAAE;AAAA,IAC/D;AACA,WAAO,QAAQ;AAAA,EACjB;AAEA,SAAO;AACT;AAEA,eAAe,kBAAkB,QAAgB,SAA6C;AAC5F,QAAM,WAAWD,MAAK,KAAK,QAAQ,YAAY;AAC/C,QAAM,SAA2B;AAAA,IAC/B,MAAM;AAAA,IACN,OAAO;AAAA,IACP,QAAQ,CAAC;AAAA,IACT,UAAU,CAAC;AAAA,EACb;AAEA,MAAI;AACF,UAAM,UAAU,MAAMD,IAAG,SAAS,UAAU,MAAM;AAClD,UAAM,SAAS,KAAK,KAAK,OAAO;AAGhC,QAAI,OAAO,YAAY,QAAW;AAChC,aAAO,OAAO,KAAK,iCAAiC;AACpD,aAAO,QAAQ;AAAA,IACjB;AAEA,QAAI,CAAC,OAAO,UAAU;AACpB,aAAO,OAAO,KAAK,kCAAkC;AACrD,aAAO,QAAQ;AAAA,IACjB;AAGA,UAAM,QAAQ,OAAO;AACrB,QAAI,CAAC,SAAS,CAAC,MAAM,QAAQ,KAAK,GAAG;AACnC,aAAO,OAAO,KAAK,gCAAgC;AACnD,aAAO,QAAQ;AAAA,IACjB,OAAO;AAEL,YAAM,UAAU,MAAM,KAAK,CAAC,MAAM,EAAE,SAAS,MAAM;AACnD,UAAI,CAAC,SAAS;AACZ,eAAO,SAAS,KAAK,mCAAmC;AAAA,MAC1D;AAGA,eAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,cAAM,OAAO,MAAM,CAAC;AACpB,YAAI,CAAC,KAAK,MAAM;AACd,iBAAO,OAAO,KAAK,QAAQ,CAAC,gBAAgB;AAC5C,iBAAO,QAAQ;AAAA,QACjB;AACA,YAAI,CAAC,KAAK,MAAM;AACd,iBAAO,OAAO,KAAK,QAAQ,CAAC,gBAAgB;AAC5C,iBAAO,QAAQ;AAAA,QACjB;AACA,YAAI,CAAC,KAAK,MAAM;AACd,iBAAO,SAAS,KAAK,SAAS,KAAK,IAAI,wCAAwC;AAAA,QACjF;AAAA,MACF;AAEA,UAAI,SAAS;AACX,gBAAQ,IAAIE,OAAM,IAAI,KAAK,MAAM,MAAM,gBAAgB,CAAC;AAAA,MAC1D;AAAA,IACF;AAAA,EACF,SAAS,OAAO;AACd,QAAK,MAAgC,SAAS,UAAU;AACtD,aAAO,OAAO,KAAK,gBAAgB;AAAA,IACrC,OAAO;AACL,aAAO,OAAO,KAAK,gBAAiB,MAAgB,OAAO,EAAE;AAAA,IAC/D;AACA,WAAO,QAAQ;AAAA,EACjB;AAEA,SAAO;AACT;AAEA,eAAe,oBAAoB,QAAgB,SAA6C;AAC9F,QAAM,WAAWD,MAAK,KAAK,QAAQ,cAAc;AACjD,QAAM,SAA2B;AAAA,IAC/B,MAAM;AAAA,IACN,OAAO;AAAA,IACP,QAAQ,CAAC;AAAA,IACT,UAAU,CAAC;AAAA,EACb;AAEA,MAAI;AACF,UAAM,UAAU,MAAMD,IAAG,SAAS,UAAU,MAAM;AAClD,UAAM,SAAS,KAAK,KAAK,OAAO;AAGhC,QAAI,OAAO,YAAY,QAAW;AAChC,aAAO,OAAO,KAAK,iCAAiC;AACpD,aAAO,QAAQ;AAAA,IACjB;AAGA,QAAI,OAAO,WAAW,UAAa,OAAO,OAAO,WAAW,UAAU;AACpE,aAAO,OAAO,KAAK,0BAA0B;AAC7C,aAAO,QAAQ;AAAA,IACjB;AAEA,QAAI,OAAO,UAAU,UAAa,OAAO,OAAO,UAAU,UAAU;AAClE,aAAO,OAAO,KAAK,yBAAyB;AAC5C,aAAO,QAAQ;AAAA,IACjB;AAEA,QAAI,SAAS;AACX,YAAM,aAAa,OAAO,SAAS,OAAO,KAAK,OAAO,MAAgB,EAAE,SAAS;AACjF,YAAM,YAAY,OAAO,QAAQ,OAAO,KAAK,OAAO,KAAe,EAAE,SAAS;AAC9E,cAAQ,IAAIE,OAAM,IAAI,KAAK,UAAU,iBAAiB,SAAS,gBAAgB,CAAC;AAAA,IAClF;AAAA,EACF,SAAS,OAAO;AACd,QAAK,MAAgC,SAAS,UAAU;AACtD,aAAO,OAAO,KAAK,gBAAgB;AAAA,IACrC,OAAO;AACL,aAAO,OAAO,KAAK,gBAAiB,MAAgB,OAAO,EAAE;AAAA,IAC/D;AACA,WAAO,QAAQ;AAAA,EACjB;AAEA,SAAO;AACT;AAEA,eAAe,gBAAgB,QAAgB,SAA6C;AAC1F,QAAM,WAAWD,MAAK,KAAK,QAAQ,UAAU;AAC7C,QAAM,SAA2B;AAAA,IAC/B,MAAM;AAAA,IACN,OAAO;AAAA,IACP,QAAQ,CAAC;AAAA,IACT,UAAU,CAAC;AAAA,EACb;AAEA,MAAI;AACF,UAAM,UAAU,MAAMD,IAAG,SAAS,UAAU,MAAM;AAClD,UAAM,SAAS,KAAK,KAAK,OAAO;AAGhC,QAAI,OAAO,YAAY,QAAW;AAChC,aAAO,OAAO,KAAK,iCAAiC;AACpD,aAAO,QAAQ;AAAA,IACjB;AAGA,UAAM,SAAS,OAAO;AACtB,QAAI,CAAC,QAAQ;AACX,aAAO,OAAO,KAAK,kCAAkC;AACrD,aAAO,QAAQ;AAAA,IACjB,OAAO;AACL,UAAI,CAAC,OAAO,UAAU;AACpB,eAAO,SAAS,KAAK,4BAA4B;AAAA,MACnD;AACA,UAAI,CAAC,OAAO,SAAS;AACnB,eAAO,SAAS,KAAK,8CAA8C;AAAA,MACrE;AACA,UAAI,CAAC,OAAO,oBAAoB;AAC9B,eAAO,SAAS,KAAK,sCAAsC;AAAA,MAC7D;AAAA,IACF;AAGA,UAAM,QAAQ,OAAO;AACrB,QAAI,SAAS,OAAO,UAAU,UAAU;AACtC,YAAM,WAAW,OAAO,KAAK,KAAK;AAClC,UAAI,CAAC,SAAS,SAAS,MAAM,GAAG;AAC9B,eAAO,SAAS,KAAK,oCAAoC;AAAA,MAC3D;AAEA,UAAI,SAAS;AACX,gBAAQ,IAAIE,OAAM,IAAI,KAAK,SAAS,MAAM,mBAAmB,CAAC;AAAA,MAChE;AAAA,IACF;AAAA,EACF,SAAS,OAAO;AACd,QAAK,MAAgC,SAAS,UAAU;AACtD,aAAO,OAAO,KAAK,gBAAgB;AAAA,IACrC,OAAO;AACL,aAAO,OAAO,KAAK,gBAAiB,MAAgB,OAAO,EAAE;AAAA,IAC/D;AACA,WAAO,QAAQ;AAAA,EACjB;AAEA,SAAO;AACT;;;AC9TA,OAAOC,SAAQ;AACf,OAAOC,WAAU;AACjB,OAAOC,YAAW;AAClB,OAAOC,WAAU;AAUjB,eAAsB,aAAa,SAAsC;AACvE,QAAM,EAAE,SAAS,KAAK,QAAQ,MAAM,IAAI;AAExC,QAAM,YAAYF,MAAK,QAAQ,MAAM;AACrC,QAAM,SAASA,MAAK,KAAK,WAAW,MAAM;AAC1C,QAAM,WAAWA,MAAK,KAAK,WAAW,QAAQ;AAE9C,UAAQ,IAAIC,OAAM,KAAK,qCAAqC,CAAC;AAC7D,UAAQ,IAAIA,OAAM,IAAI,cAAc,SAAS,EAAE,CAAC;AAChD,UAAQ,IAAI;AAGZ,MAAI;AACF,UAAMF,IAAG,OAAO,MAAM;AAAA,EACxB,QAAQ;AACN,YAAQ,IAAIE,OAAM,IAAI,0BAA0B,CAAC;AACjD,YAAQ,IAAI,OAAOA,OAAM,KAAK,UAAU,GAAG,oCAAoC;AAC/E,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,MAAI,WAAW;AACf,MAAI,YAAY;AAChB,MAAI;AACF,UAAM,WAAW,MAAMF,IAAG,SAASC,MAAK,KAAK,QAAQ,WAAW,GAAG,MAAM;AACzE,UAAM,aAAaE,MAAK,KAAK,QAAQ;AACrC,eAAY,WAAW,aAAwB;AAC/C,UAAM,WAAW,WAAW;AAC5B,gBAAa,UAAU,aAAwB;AAAA,EACjD,QAAQ;AACN,YAAQ,IAAID,OAAM,OAAO,mCAAmC,CAAC;AAAA,EAC/D;AAGA,QAAM,QAAkD;AAAA,IACtD,EAAE,MAAM,oBAAoB,SAAS,kBAAkB,QAAQ,EAAE;AAAA,IACjE,EAAE,MAAM,iCAAiC,SAAS,wBAAwB,UAAU,SAAS,EAAE;AAAA,IAC/F,EAAE,MAAM,6BAA6B,SAAS,qBAAqB,QAAQ,EAAE;AAAA,IAC7E,EAAE,MAAM,mCAAmC,SAAS,2BAA2B,QAAQ,EAAE;AAAA,IACzF,EAAE,MAAM,2BAA2B,SAAS,sBAAsB,QAAQ,EAAE;AAAA,IAC5E,EAAE,MAAM,2BAA2B,SAAS,mBAAmB,QAAQ,EAAE;AAAA,EAC3E;AAGA,QAAMF,IAAG,MAAM,UAAU,EAAE,WAAW,KAAK,CAAC;AAG5C,QAAM,iBAA2B,CAAC;AAClC,aAAW,QAAQ,OAAO;AACxB,UAAM,WAAWC,MAAK,KAAK,UAAU,KAAK,IAAI;AAG9C,QAAI;AACF,YAAMD,IAAG,OAAO,QAAQ;AACxB,UAAI,CAAC,OAAO;AACV,gBAAQ,IAAIE,OAAM,OAAO,4BAA4B,KAAK,IAAI,EAAE,CAAC;AACjE;AAAA,MACF;AAAA,IACF,QAAQ;AAAA,IAER;AAEA,UAAMF,IAAG,UAAU,UAAU,KAAK,SAAS,MAAM;AACjD,mBAAe,KAAK,KAAK,IAAI;AAC7B,YAAQ,IAAI,GAAGE,OAAM,MAAM,QAAG,CAAC,WAAW,KAAK,IAAI,EAAE;AAAA,EACvD;AAEA,UAAQ,IAAI;AACZ,MAAI,eAAe,SAAS,GAAG;AAC7B,YAAQ,IAAIA,OAAM,MAAM,aAAa,eAAe,MAAM,cAAc,CAAC;AAAA,EAC3E,OAAO;AACL,YAAQ,IAAIA,OAAM,OAAO,kEAAkE,CAAC;AAAA,EAC9F;AACF;AAMA,SAAS,kBAAkB,UAA0B;AACnD,SAAO,oBAAoB,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA6DrC;AAEA,SAAS,wBAAwB,UAAkB,WAA2B;AAC5E,SAAO,mDAAmD,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqBlE,cAAc,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAepB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAeH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAqBD;AAEA,SAAS,qBAAqB,UAA0B;AACtD,SAAO,sCAAsC,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,gCA4BvB,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,8BA4BV,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oCAaF,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAa5C;AAEA,SAAS,2BAA2B,UAA0B;AAC5D,SAAO,4CAA4C,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiE7D;AAEA,SAAS,sBAAsB,UAA0B;AACvD,SAAO,6CAA6C,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA0E9D;AAEA,SAAS,mBAAmB,UAA0B;AACpD,SAAO,oCAAoC,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgFrD;;;ACrhBA,OAAOE,SAAQ;AACf,OAAOC,WAAU;AACjB,OAAOC,WAAU;AACjB,OAAOC,YAAW;AAClB,OAAOC,UAAS;AAgHhB,IAAM,8BAA8B,KAAK;AACzC,IAAM,wBAAwB;AAE9B,eAAe,qBAAqB,MAA2B;AAC7D,QAAM,KAAK,YAAY;AAAA,IACrB,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUX,CAAC,EAAE,MAAM,MAAM;AAAA,EAEf,CAAC;AACH;AAEA,eAAe,uBAAuB,MAA2B;AAC/D,QAAM,KAAK,SAAS,OAAO,cAAc;AACvC,UAAM,eAAe,OAAO,SAAS,OAAO,OAAO,SAAS,aACxD,SAAS,MAAM,MAAM,MAAM,MAAM,MAAS,IAC1C,QAAQ,QAAQ;AAEpB,UAAM,gBAAgB,MAAM,KAAK,SAAS,MAAM,EAAE,OAAO,CAAC,QAAQ;AAChE,YAAM,OAAO,IAAI,sBAAsB;AACvC,aAAO,KAAK,UAAU,QAAQ,KAAK,OAAO,OAAO,cAAc;AAAA,IACjE,CAAC;AAED,UAAM,gBAAgB,QAAQ,IAAI,cAAc,IAAI,CAAC,QAAQ;AAC3D,UAAI,IAAI,YAAY,IAAI,eAAe,GAAG;AACxC,eAAO,QAAQ,QAAQ;AAAA,MACzB;AACA,aAAO,IAAI,QAAc,CAACC,aAAY;AACpC,cAAM,OAAO,MAAMA,SAAQ;AAC3B,YAAI,iBAAiB,QAAQ,MAAM,EAAE,MAAM,KAAK,CAAC;AACjD,YAAI,iBAAiB,SAAS,MAAM,EAAE,MAAM,KAAK,CAAC;AAAA,MACpD,CAAC;AAAA,IACH,CAAC,CAAC;AAEF,UAAM,QAAQ,KAAK;AAAA,MACjB,QAAQ,IAAI,CAAC,cAAc,aAAa,CAAC;AAAA,MACzC,IAAI,QAAQ,CAACA,aAAY,OAAO,WAAWA,UAAS,SAAS,CAAC;AAAA,IAChE,CAAC;AAAA,EACH,GAAG,qBAAqB,EAAE,MAAM,CAAC,UAAU;AACzC,YAAQ,KAAK,oDAAoD,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAAA,EAC3H,CAAC;AACH;AAEA,eAAe,aAAa,MAAY,gBAAyC;AAC/E,MAAI,aAAa,MAAM,KAAK,SAAS,MAAM,SAAS,gBAAgB,YAAY;AAChF,QAAM,aAAa,KAAK,IAAI,gBAAgB,CAAC;AAE7C,WAAS,UAAU,GAAG,UAAU,YAAY,WAAW,YAAY;AACjE,UAAM,KAAK,SAAS,CAAC,MAAM,OAAO,SAAS,GAAG,CAAC,GAAG,OAAO;AACzD,UAAM,KAAK,eAAe,GAAG;AAAA,EAC/B;AAEA,QAAM,KAAK,eAAe,GAAG;AAC7B,eAAa,MAAM,KAAK,SAAS,MAAM,SAAS,gBAAgB,YAAY;AAE5E,SAAO;AACT;AAEA,eAAe,8BAA8B,MAA2B;AACtE,QAAM,KAAK,SAAS,MAAM;AACxB,WAAO,SAAS,GAAG,CAAC;AACpB,WAAO,cAAc,IAAI,MAAM,QAAQ,CAAC;AAAA,EAC1C,CAAC;AACD,QAAM,KAAK,gBAAgB,MAAM,KAAK,IAAI,OAAO,OAAO,IAAI,GAAG,QAAW,EAAE,SAAS,IAAK,CAAC,EAAE,MAAM,MAAM,MAAS;AAClH,QAAM,KAAK,SAAS,MAAM,IAAI,QAAQ,CAACA,aAAY,sBAAsB,MAAM,sBAAsBA,QAAO,CAAC,CAAC,CAAC,EAAE,MAAM,MAAM,MAAS;AACtI,QAAM,KAAK,eAAe,GAAG;AAC7B,QAAM,uBAAuB,IAAI;AACnC;AAEA,eAAe,0BAA6B,MAAY,SAAuC;AAC7F,QAAM,cAAc,MAAM,KAAK,YAAY;AAAA,IACzC,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqBX,CAAC,EAAE,MAAM,MAAM,IAAI;AAEnB,QAAM,KAAK,SAAS,MAAM;AACxB,UAAM,aAAa,MAAM,KAAK,SAAS;AAAA,MACrC;AAAA,IACF,CAAC;AACD,eAAW,QAAQ,CAAC,YAAY;AAC9B,YAAM,QAAQ,OAAO,iBAAiB,OAAO;AAC7C,YAAM,YAAY,mBAAmB,eAAe,OAAO,QAAQ,cAAc,WAAW,QAAQ,YAAY;AAChH,UAAI,MAAM,aAAa,WAAW,MAAM,aAAa,YAAY,qBAAqB,KAAK,SAAS,KAAK,QAAQ,UAAU,SAAS,OAAO,GAAG;AAC5I,gBAAQ,aAAa,8BAA8B,MAAM;AAAA,MAC3D;AAAA,IACF,CAAC;AACD,aAAS,gBAAgB,aAAa,+BAA+B,MAAM;AAC3E,WAAO,SAAS,GAAG,CAAC;AACpB,WAAO,cAAc,IAAI,MAAM,QAAQ,CAAC;AAAA,EAC1C,CAAC;AACD,QAAM,KAAK,SAAS,MAAM,IAAI,QAAQ,CAACA,aAAY,sBAAsB,MAAM,sBAAsBA,QAAO,CAAC,CAAC,CAAC,EAAE,MAAM,MAAM,MAAS;AAEtI,MAAI;AACF,WAAO,MAAM,QAAQ;AAAA,EACvB,UAAE;AACA,UAAM,aAAa,SAAS,CAAC,OAAO,GAAG,YAAY,YAAY,EAAE,CAAC,EAAE,MAAM,MAAM,MAAS;AACzF,UAAM,KAAK,SAAS,MAAM;AACxB,eAAS,gBAAgB,gBAAgB,6BAA6B;AACtE,eAAS,iBAAiB,8BAA8B,EAAE,QAAQ,CAAC,YAAY;AAC7E,gBAAQ,gBAAgB,4BAA4B;AAAA,MACtD,CAAC;AAAA,IACH,CAAC,EAAE,MAAM,MAAM,MAAS;AAAA,EAC1B;AACF;AAEA,eAAe,uBACb,MACA,YACA,UACe;AACf,QAAM,KAAK,gBAAgB,QAAQ;AACnC,QAAM,KAAK,SAAS,MAAM,OAAO,SAAS,GAAG,CAAC,CAAC;AAC/C,QAAM,KAAK,eAAe,GAAG;AAC7B,QAAM,uBAAuB,IAAI;AAEjC,QAAM,OAAO,MAAM,KAAK,SAAS,CAAC,gBAAgB;AAChD,UAAM,QAAQ,KAAK,IAAI,SAAS,gBAAgB,aAAa,OAAO,YAAY,CAAC;AACjF,UAAM,aAAa,KAAK,IAAI,SAAS,gBAAgB,cAAc,SAAS,MAAM,gBAAgB,GAAG,CAAC;AACtG,UAAM,SAAS,KAAK,IAAI,KAAK,MAAM,QAAQ,WAAW,GAAG,UAAU;AACnE,WAAO,EAAE,GAAG,GAAG,GAAG,GAAG,OAAO,OAAO;AAAA,EACrC,GAAG,2BAA2B;AAE9B,QAAM,KAAK,WAAW;AAAA,IACpB,MAAM;AAAA,IACN;AAAA,EACF,CAAC;AACH;AAMA,IAAM,oBAAoB;AAAA,EACxB,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,SAAS;AAAA,EACT,SAAS;AAAA,EACT,KAAK;AACP;AAMA,SAAS,gBAAgB,WAAgC;AACvD,QAAM,aAAaJ,MAAK,KAAK,WAAW,QAAQ,YAAY;AAC5D,MAAI,CAACD,IAAG,WAAW,UAAU,GAAG;AAC9B,UAAM,IAAI,MAAM,iCAAiC,UAAU,EAAE;AAAA,EAC/D;AACA,QAAM,UAAUA,IAAG,aAAa,YAAY,OAAO;AACnD,SAAOE,MAAK,KAAK,OAAO;AAC1B;AAEA,SAAS,kBAAkB,WAAyC;AAClE,QAAM,cAAcD,MAAK,KAAK,WAAW,QAAQ,cAAc;AAC/D,MAAI,CAACD,IAAG,WAAW,WAAW,GAAG;AAC/B,WAAO;AAAA,EACT;AACA,QAAM,UAAUA,IAAG,aAAa,aAAa,OAAO;AACpD,SAAOE,MAAK,KAAK,OAAO;AAC1B;AAEA,SAAS,4BAA4B,WAAmD;AACtF,QAAM,eAAeD,MAAK,KAAK,WAAW,QAAQ,yBAAyB;AAC3E,MAAI,CAACD,IAAG,WAAW,YAAY,GAAG;AAChC,WAAO;AAAA,EACT;AACA,QAAM,UAAUA,IAAG,aAAa,cAAc,OAAO;AACrD,SAAOE,MAAK,KAAK,OAAO;AAC1B;AAEA,SAAS,wBACP,eACA,UACqB;AACrB,QAAM,OAAO,oBAAI,IAAoB;AACrC,MAAI,CAAC,cAAe,QAAO;AAG3B,MAAI,cAAc,QAAQ;AACxB,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,cAAc,MAAM,GAAG;AAC/D,WAAK,IAAI,KAAK,OAAO,UAAU,WAAW,QAAQ,KAAK,UAAU,KAAK,CAAC;AAAA,IACzE;AAAA,EACF;AAGA,QAAM,cAAc,cAAc,QAAQ,QAAQ;AAClD,MAAI,aAAa;AACf,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,WAAW,GAAG;AACtD,YAAM,UAAU,GAAG,QAAQ,IAAI,GAAG;AAClC,WAAK,IAAI,SAAS,OAAO,UAAU,WAAW,QAAQ,KAAK,UAAU,KAAK,CAAC;AAAA,IAC7E;AAAA,EACF;AAEA,SAAO;AACT;AAYA,eAAe,gBAAgB,QAA8C;AAC3E,QAAMI,YAA2B,CAAC;AAElC,aAAW,QAAQ,OAAO,OAAO;AAC/B,QAAI,KAAK,SAAS,aAAa,KAAK,WAAW;AAE7C,iBAAW,YAAY,KAAK,WAAW;AACrC,cAAM,eAAe,KAAK,eACtB,KAAK,aAAa,QAAQ,UAAU,QAAQ,IAC5C,GAAG,KAAK,IAAI,IAAI,QAAQ;AAE5B,QAAAA,UAAS,KAAK;AAAA,UACZ,MAAM,GAAG,KAAK,IAAI,IAAI,QAAQ;AAAA,UAC9B,MAAM;AAAA,UACN,QAAQ,EAAE,GAAG,MAAM,MAAM,GAAG,KAAK,IAAI,IAAI,QAAQ,GAAG;AAAA,QACtD,CAAC;AAAA,MACH;AAAA,IACF,OAAO;AACL,MAAAA,UAAS,KAAK;AAAA,QACZ,MAAM,KAAK;AAAA,QACX,MAAM,KAAK;AAAA,QACX,QAAQ;AAAA,MACV,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAOA;AACT;AAMA,eAAe,WAAW,SAAyC;AACjE,SAAO,QAAQ,SAAS,CAAC,OAAgB;AACvC,UAAM,QAAkB,CAAC;AACzB,QAAI,UAA0B;AAE9B,WAAO,WAAW,YAAY,SAAS,MAAM;AAC3C,UAAI,WAAW,QAAQ,QAAQ,YAAY;AAE3C,UAAI,QAAQ,IAAI;AACd,oBAAY,IAAI,QAAQ,EAAE;AAAA,MAC5B,OAAO;AACL,cAAM,UAAU,MAAM,KAAK,QAAQ,SAAS,EACzC,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,+BAA+B,CAAC,EACvD,MAAM,GAAG,CAAC,EACV,KAAK,GAAG;AACX,YAAI,SAAS;AACX,sBAAY,IAAI,OAAO;AAAA,QACzB;AAAA,MACF;AAEA,YAAM,QAAQ,QAAQ;AACtB,gBAAU,QAAQ;AAAA,IACpB;AAEA,WAAO,MAAM,KAAK,KAAK;AAAA,EACzB,CAAC;AACH;AAEA,eAAe,2BACb,SACA,aACA,OACiB;AAEjB,QAAM,cAAc,MAAM,QAAQ,SAAS,CAAC,OAAgB;AAC1D,UAAM,UAAU,GAAG,cAAc,sDAAsD;AACvF,QAAI,WAAW,QAAQ,aAAa;AAClC,aAAO,QAAQ,YAAY,KAAK,EAAE,MAAM,GAAG,EAAE;AAAA,IAC/C;AACA,WAAO;AAAA,EACT,CAAC;AAED,MAAI,YAAa,QAAO;AAGxB,QAAM,aAAqC;AAAA,IACzC,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,SAAS;AAAA,IACT,SAAS;AAAA,IACT,KAAK;AAAA,EACP;AAEA,SAAO,WAAW,WAAW,KAAK,WAAW,QAAQ,CAAC;AACxD;AAMA,eAAe,eACb,OACA,gBACA,WACA,gBACwB;AACxB,QAAM,WAA0B,CAAC;AAGjC,QAAM,kBAAkB,MAAM,eAAe,GAAG,wDAAwD;AAExG,aAAW,WAAW,iBAAiB;AACrC,UAAM,UAAU,MAAM,QAAQ,SAAS,CAAC,OAAgB;AACtD,YAAM,OAAO,GAAG,sBAAsB;AACtC,YAAM,mBAAmB,GAAG,aAAa,oBAAoB;AAC7D,YAAM,WAAW,cAAc,mBAAmB,GAAG,cAAc,GAAG,MAAM;AAC5E,aAAO;AAAA,QACL,KAAK,GAAG,aAAa,eAAe,KAAK,GAAG,aAAa,eAAe,KAAK,GAAG,aAAa,oBAAoB,KAAK;AAAA,QACtH,aAAa,GAAG,QAAQ,YAAY;AAAA,QACpC,eAAe,oBAAoB,YAAY,GAAG,aAAa,KAAK,KAAK,IAAI,MAAM,GAAG,GAAG;AAAA,QACzF,QAAQ;AAAA,UACN,GAAG,KAAK,OAAO,OAAO;AAAA,UACtB,GAAG,KAAK,MAAM,OAAO;AAAA,UACrB,OAAO,KAAK;AAAA,UACZ,QAAQ,KAAK;AAAA,QACf;AAAA,MACF;AAAA,IACF,CAAC;AAED,QAAI,QAAQ,OAAO,CAAC,SAAS,KAAK,CAAC,YAAY,QAAQ,QAAQ,QAAQ,GAAG,GAAG;AAC3E,YAAM,UAAU,MAAM,WAAW,OAAO;AACxC,eAAS,KAAK;AAAA,QACZ,KAAK,QAAQ;AAAA,QACb,cAAc,QAAQ;AAAA,QACtB,aAAa,QAAQ;AAAA,QACrB;AAAA,QACA,QAAQ;AAAA,UACN,GAAG,KAAK,MAAM,QAAQ,OAAO,CAAC;AAAA,UAC9B,GAAG,KAAK,MAAM,QAAQ,OAAO,CAAC;AAAA,UAC9B,OAAO,KAAK,MAAM,QAAQ,OAAO,KAAK;AAAA,UACtC,QAAQ,KAAK,MAAM,QAAQ,OAAO,MAAM;AAAA,QAC1C;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AACT;AAMA,eAAe,8BACb,MACA,kBAC0B;AAE1B,QAAM,aAAa,MAAM,KAAK,GAAG,kEAAkE;AAEnG,QAAM,UAA2B,CAAC;AAClC,QAAM,iBAAiB,iBAAiB,IAAI,CAAC,MAAM,EAAE,MAAM;AAE3D,aAAW,aAAa,YAAY;AAClC,UAAM,SAAS,MAAM,UAAU,SAAS,CAAC,OAAgB;AACvD,YAAM,OAAO,GAAG,sBAAsB;AACtC,aAAO;AAAA,QACL,GAAG,KAAK,MAAM,OAAO;AAAA,QACrB,QAAQ,KAAK;AAAA,MACf;AAAA,IACF,CAAC;AAGD,QAAI,OAAO,SAAS,IAAK;AAEzB,UAAM,WAAW,eAAe;AAAA,MAC9B,CAAC,OACC,OAAO,IAAI,GAAG,IAAI,GAAG,UAAU,OAAO,IAAI,OAAO,SAAS,GAAG;AAAA,IACjE;AAEA,QAAI,CAAC,UAAU;AACb,cAAQ,KAAK,SAAS;AAAA,IACxB;AAAA,EACF;AAEA,SAAO;AACT;AAMA,eAAe,oBACb,SACA,UACA,gBACA,UACA,eACA,WACA,SACuB;AACvB,QAAM,UAAU,MAAM,QAAQ,WAAW;AAAA,IACvC,UAAU,eAAe;AAAA,IACzB,mBAAmB;AAAA,IACnB,eAAe;AAAA,EACjB,CAAC;AACD,QAAM,OAAO,MAAM,QAAQ,QAAQ;AAGnC,QAAM,SAAS,SAAS,KAAK,WAAW,MAAM,IAC1C,SAAS,OACT,GAAG,QAAQ,IAAI,iBAAiB,uBAAuB,GAAG,SAAS,IAAI;AAC3E,QAAM,UAAU,IAAI,IAAI,MAAM;AAC9B,UAAQ,aAAa,IAAI,mBAAmB,EAAE;AAC9C,QAAM,MAAM,QAAQ,SAAS;AAE7B,MAAI,SAAS;AACX,YAAQ,IAAI,gBAAgB,SAAS,IAAI,KAAK,GAAG,GAAG;AAAA,EACtD;AAEA,MAAI;AACF,UAAM,KAAK,KAAK,KAAK,EAAE,WAAW,oBAAoB,SAAS,IAAM,CAAC;AACtE,UAAM,qBAAqB,IAAI;AAC/B,UAAM,KAAK,iBAAiB,eAAe,EAAE,SAAS,IAAM,CAAC,EAAE,MAAM,MAAM;AACzE,UAAI,QAAS,SAAQ,IAAI,kDAAkD;AAAA,IAC7E,CAAC;AAAA,EACH,SAAS,OAAO;AACd,YAAQ,MAAM,uBAAuB,KAAK,EAAE;AAC5C,UAAM,QAAQ,MAAM;AACpB,UAAM;AAAA,EACR;AAEA,QAAM,KAAK,eAAe,eAAe,aAAa;AACtD,QAAM,uBAAuB,IAAI;AAGjC,QAAM,gBAAgBL,MAAK,KAAK,WAAW,UAAU,SAAS,IAAI;AAClE,EAAAD,IAAG,UAAUC,MAAK,KAAK,eAAe,UAAU,GAAG,EAAE,WAAW,KAAK,CAAC;AAEtE,QAAM,QAAQ,MAAM,KAAK,MAAM;AAG/B,MAAI,aAAa,MAAM,aAAa,MAAM,eAAe,SAAS,MAAM;AACxE,QAAM,8BAA8B,IAAI;AAGxC,QAAM,eAAeA,MAAK,KAAK,eAAe,eAAe;AAC7D,QAAM;AAAA,IAA0B;AAAA,IAAM,MACpC,KAAK,WAAW;AAAA,MACd,MAAM;AAAA,MACN,UAAU,eAAe;AAAA,IAC3B,CAAC;AAAA,EACH;AAGA,QAAM,gBAAgBA,MAAK,KAAK,eAAe,eAAe;AAC9D,QAAM,uBAAuB,MAAM,eAAe,eAAe,QAAQ;AAGzE,QAAM,KAAK,SAAS,MAAM,OAAO,SAAS,GAAG,CAAC,CAAC;AAC/C,QAAM,KAAK,eAAe,GAAG;AAG7B,QAAM,WAA0B,CAAC;AACjC,QAAM,eAAe,oBAAI,IAAY;AAKrC,QAAM,oBAAoB,OAAO,IAAmB,gBAAyB;AAC3E,WAAO,GAAG,SAAS,CAAC,SAAkB,SAA6B;AACjE,YAAM,OAAO,QAAQ,sBAAsB;AAC3C,YAAM,QAAQ,OAAO,iBAAiB,OAAO;AAC7C,YAAM,WAAW,MAAM;AACvB,YAAM,UAAU,aAAa,WAAW,aAAa;AAErD,UAAI,IAAI,KAAK,MAAM,OAAO;AAG1B,UAAI,YAAY,SAAS,YAAY,QAAQ,QAAQ,YAAY,MAAM,WAAW;AAChF,YAAI;AAAA,MACN,WAES,YAAY,SAAS,YAAY,QAAQ,QAAQ,YAAY,MAAM,WAAW;AACrF,YAAI,SAAS,gBAAgB,eAAe,KAAK;AAAA,MACnD;AAEA,aAAO;AAAA,QACL,GAAG,KAAK,OAAO,OAAO;AAAA,QACtB;AAAA,QACA,OAAO,KAAK;AAAA,QACZ,QAAQ,KAAK;AAAA,QACb;AAAA,MACF;AAAA,IACF,GAAG,WAAW;AAAA,EAChB;AAGA,QAAM,mBAAmB,MAAM,KAAK,GAAG,kBAAkB,QAAQ;AAEjE,WAAS,IAAI,GAAG,IAAI,iBAAiB,QAAQ,KAAK;AAChD,UAAM,UAAU,iBAAiB,CAAC;AAElC,UAAM,UAAU,MAAM,WAAW,OAAO;AACxC,QAAI,aAAa,IAAI,OAAO,EAAG;AAC/B,iBAAa,IAAI,OAAO;AAGxB,UAAM,gBAAgB,MAAM,QAAQ,SAAS,CAAC,OAAgB;AAC5D,aAAO;AAAA,QACL,WAAW,GAAG,aAAa,cAAc,KAAK;AAAA,QAC9C,cAAc,GAAG,aAAa,oBAAoB,KAAK;AAAA,QACvD,aAAa,GAAG,aAAa,mBAAmB,KAAK;AAAA,QACrD,WAAW,GAAG,aAAa,cAAc;AAAA,MAC3C;AAAA,IACF,CAAC;AAGD,QAAI,eAA2C;AAC/C,QAAI,cAAc,aAAa;AAC7B,qBAAe,cAAc;AAAA,IAC/B,WAAW,CAAC,UAAU,QAAQ,EAAE,SAAS,cAAc,SAAS,GAAG;AACjE,qBAAe,cAAc;AAAA,IAC/B,WAAW,cAAc,UAAU,SAAS,MAAM,GAAG;AACnD,qBAAe;AAAA,IACjB,WAAW,cAAc,UAAU,SAAS,SAAS,GAAG;AACtD,qBAAe;AAAA,IACjB;AAGA,UAAM,SAAS,MAAM,kBAAkB,SAAS,YAAY;AAE5D,QAAI,CAAC,UAAU,OAAO,SAAS,GAAI;AAEnC,UAAM,YAAY,cAAc,aAAa,WAAW,CAAC;AACzD,UAAM,cAAcA,MAAK,KAAK,eAAe,YAAY,GAAG,SAAS,MAAM;AAE3E,QAAI;AACF,YAAM,QAAQ,WAAW,EAAE,MAAM,YAAY,CAAC;AAAA,IAChD,QAAQ;AACN,UAAI,QAAS,SAAQ,IAAI,mCAAmC,SAAS,EAAE;AAAA,IACzE;AAEA,UAAM,mBAAmB,CAAC,UAAU,QAAQ,EAAE,SAAS,YAAY;AAGnE,QAAI,WAA0B,CAAC;AAC/B,QAAI,CAAC,kBAAkB;AACrB,iBAAW,MAAM,eAAe,MAAM,SAAS,SAAS,MAAM,aAAa;AAAA,IAC7E;AAEA,UAAM,aAAa,CAAC,oBAAoB,SAAS,SAAS;AAG1D,QAAI,cAAc,cAAc;AAChC,QAAI,CAAC,aAAa;AAChB,oBAAc,MAAM,2BAA2B,SAAS,cAAc,CAAC;AAAA,IACzE;AAEA,QAAI,SAAS;AACX,cAAQ;AAAA,QACN,gBAAgB,WAAW,KAAK,SAAS,UAAU,KAAK,MAAM,OAAO,CAAC,CAAC,GACrE,cAAc,YAAY,eAAe,EAC3C,MAAM,SAAS,MAAM;AAAA,MACvB;AAAA,IACF;AAEA,aAAS,KAAK;AAAA,MACZ;AAAA,MACA,aAAa;AAAA,MACb;AAAA,MACA;AAAA,MACA,QAAQ;AAAA,QACN,GAAG,KAAK,MAAM,OAAO,CAAC;AAAA,QACtB,GAAG,KAAK,MAAM,OAAO,CAAC;AAAA,QACtB,OAAO,KAAK,MAAM,OAAO,KAAK;AAAA,QAC9B,QAAQ,KAAK,MAAM,OAAO,MAAM;AAAA,MAClC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAGA,aAAW,CAAC,aAAa,QAAQ,KAAK,OAAO,QAAQ,iBAAiB,GAAG;AACvE,QAAI,gBAAgB,WAAY;AAEhC,UAAM,WAAW,MAAM,KAAK,GAAG,QAAQ;AAEvC,aAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACxC,YAAM,UAAU,SAAS,CAAC;AAE1B,YAAM,UAAU,MAAM,WAAW,OAAO;AACxC,UAAI,aAAa,IAAI,OAAO,EAAG;AAI/B,YAAM,0BAA0B,MAAM,QAAQ,SAAS,CAAC,OAAgB;AAEtE,YAAI,UAA8B,GAAG;AACrC,eAAO,SAAS;AACd,cAAI,QAAQ,SAAS,SAAS;AAC5B,mBAAO;AAAA,UACT;AACA,oBAAU,QAAQ;AAAA,QACpB;AACA,eAAO;AAAA,MACT,CAAC;AAED,UAAI,yBAAyB;AAC3B;AAAA,MACF;AAKA,YAAM,8BAA8B,MAAM,QAAQ,SAAS,CAAC,OAAgB;AAC1E,cAAM,mBAAmB,GAAG,iBAAiB,gBAAgB;AAC7D,eAAO,iBAAiB,SAAS;AAAA,MACnC,CAAC;AAED,UAAI,6BAA6B;AAC/B,YAAI,SAAS;AACX,kBAAQ,IAAI,gBAAgB,WAAW,yCAAyC;AAAA,QAClF;AACA;AAAA,MACF;AAEA,mBAAa,IAAI,OAAO;AAGxB,YAAM,SAAS,MAAM,kBAAkB,SAAS,WAAW;AAE3D,UAAI,CAAC,UAAU,OAAO,SAAS,GAAI;AAEnC,YAAM,YAAY,GAAG,WAAW,IAAI,CAAC;AACrC,YAAM,cAAcA,MAAK,KAAK,eAAe,YAAY,GAAG,SAAS,MAAM;AAE3E,UAAI;AACF,cAAM,QAAQ,WAAW,EAAE,MAAM,YAAY,CAAC;AAAA,MAChD,QAAQ;AACN,YAAI,QAAS,SAAQ,IAAI,mCAAmC,SAAS,EAAE;AAAA,MACzE;AAEA,YAAM,mBAAmB,CAAC,UAAU,QAAQ,EAAE,SAAS,WAAW;AAClE,YAAM,WAAW,mBACb,CAAC,IACD,MAAM,eAAe,MAAM,SAAS,SAAS,MAAM,aAAa;AAGpE,UAAI,gBAAgB,aAAa,SAAS,WAAW,GAAG;AACtD;AAAA,MACF;AAEA,YAAM,aAAa,CAAC,oBAAoB,SAAS,SAAS;AAC1D,YAAM,cAAc,MAAM,2BAA2B,SAAS,aAAa,CAAC;AAE5E,UAAI,SAAS;AACX,gBAAQ,IAAI,gBAAgB,WAAW,SAAS,KAAK,MAAM,OAAO,CAAC,CAAC,GAAG,OAAO,UAAU,aAAa,EAAE,EAAE;AAAA,MAC3G;AAEA,eAAS,KAAK;AAAA,QACZ;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,QAAQ;AAAA,UACN,GAAG,KAAK,MAAM,OAAO,CAAC;AAAA,UACtB,GAAG,KAAK,MAAM,OAAO,CAAC;AAAA,UACtB,OAAO,KAAK,MAAM,OAAO,KAAK;AAAA,UAC9B,QAAQ,KAAK,MAAM,OAAO,MAAM;AAAA,QAClC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAGA,QAAM,oBAAoB,SAAS,KAAK,CAAC,MAAM,EAAE,gBAAgB,SAAS;AAC1E,MAAI,CAAC,mBAAmB;AACtB,QAAI,QAAS,SAAQ,IAAI,6DAA6D;AAEtF,UAAM,mBAAmB,MAAM,8BAA8B,MAAM,QAAQ;AAE3E,aAAS,IAAI,GAAG,IAAI,iBAAiB,QAAQ,KAAK;AAChD,YAAM,UAAU,iBAAiB,CAAC;AAElC,YAAM,UAAU,MAAM,WAAW,OAAO;AACxC,UAAI,aAAa,IAAI,OAAO,EAAG;AAC/B,mBAAa,IAAI,OAAO;AAExB,YAAM,SAAS,MAAM,QAAQ,SAAS,CAAC,OAAgB;AACrD,cAAM,OAAO,GAAG,sBAAsB;AACtC,eAAO;AAAA,UACL,GAAG,KAAK,OAAO,OAAO;AAAA,UACtB,GAAG,KAAK,MAAM,OAAO;AAAA,UACrB,OAAO,KAAK;AAAA,UACZ,QAAQ,KAAK;AAAA,QACf;AAAA,MACF,CAAC;AAED,UAAI,CAAC,UAAU,OAAO,SAAS,GAAI;AAEnC,YAAM,YAAY,oBAAoB,CAAC;AACvC,YAAM,cAAcA,MAAK,KAAK,eAAe,YAAY,GAAG,SAAS,MAAM;AAE3E,UAAI;AACF,cAAM,QAAQ,WAAW,EAAE,MAAM,YAAY,CAAC;AAAA,MAChD,QAAQ;AACN,YAAI,QAAS,SAAQ,IAAI,4CAA4C,SAAS,EAAE;AAAA,MAClF;AAEA,YAAM,WAAW,MAAM,eAAe,MAAM,SAAS,SAAS,MAAM,aAAa;AACjF,YAAM,cAAc,MAAM,2BAA2B,SAAS,WAAW,CAAC;AAE1E,UAAI,SAAS;AACX,gBAAQ;AAAA,UACN,yBAAyB,WAAW,SAAS,KAAK,MAAM,OAAO,CAAC,CAAC,KAAK,SAAS,MAAM;AAAA,QACvF;AAAA,MACF;AAEA,eAAS,KAAK;AAAA,QACZ;AAAA,QACA,aAAa;AAAA,QACb;AAAA,QACA;AAAA,QACA,QAAQ;AAAA,UACN,GAAG,KAAK,MAAM,OAAO,CAAC;AAAA,UACtB,GAAG,KAAK,MAAM,OAAO,CAAC;AAAA,UACtB,OAAO,KAAK,MAAM,OAAO,KAAK;AAAA,UAC9B,QAAQ,KAAK,MAAM,OAAO,MAAM;AAAA,QAClC;AAAA,QACA;AAAA,QACA,YAAY,SAAS,SAAS;AAAA,QAC9B,kBAAkB;AAAA,MACpB,CAAC;AAAA,IACH;AAAA,EACF;AAEA,QAAM,QAAQ,MAAM;AAGpB,WAAS,KAAK,CAAC,GAAG,MAAM,EAAE,OAAO,IAAI,EAAE,OAAO,CAAC;AAE/C,SAAO;AAAA,IACL,UAAU,SAAS;AAAA,IACnB,MAAM,SAAS;AAAA,IACf,MAAM,SAAS,OAAO;AAAA,IACtB,WAAW,SAAS,OAAO;AAAA,IAC3B,OAAO,SAAS,SAAS,OAAO;AAAA,IAChC,aAAY,oBAAI,KAAK,GAAE,YAAY;AAAA,IACnC,YAAY;AAAA,IACZ,UAAU,eAAe;AAAA,IACzB;AAAA,IACA;AAAA,EACF;AACF;AAMA,eAAsB,wBAAwB,SAAiD;AAC7F,QAAM,EAAE,QAAQ,SAAS,QAAQ,SAAS,OAAO,cAAc,IAAI;AACnE,QAAM,YAAYA,MAAK,QAAQ,MAAM;AAErC,UAAQ,IAAIE,OAAM,KAAK,iCAA0B,CAAC;AAClD,UAAQ,IAAIA,OAAM,KAAK,0BAA0B,CAAC;AAClD,UAAQ,IAAIA,OAAM,KAAK,aAAa,SAAS,EAAE,CAAC;AAChD,UAAQ,IAAIA,OAAM,KAAK,aAAa,OAAO,EAAE,CAAC;AAG9C,UAAQ,IAAI,gBAAgB;AAG5B,MAAI;AACJ,MAAI;AACF,aAAS,gBAAgB,SAAS;AAAA,EACpC,SAAS,OAAO;AACd,YAAQ,MAAMA,OAAM,IAAI,QAAQ,GAAI,MAAgB,OAAO;AAC3D,YAAQ,IAAIA,OAAM,OAAO,6DAA6D,CAAC;AACvF,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,UAAQ,IAAIA,OAAM,KAAK,aAAa,OAAO,QAAQ,EAAE,CAAC;AAEtD,QAAM,YAAYF,MAAK,KAAK,WAAW,OAAO,SAAS,aAAa,gBAAgB;AAGpF,QAAM,gBAAgB,kBAAkB,SAAS;AACjD,MAAI,eAAe;AACjB,UAAM,YAAY,OAAO,KAAK,cAAc,SAAS,CAAC,CAAC,EAAE;AACzD,UAAM,cAAc,OAAO,KAAK,cAAc,UAAU,CAAC,CAAC,EAAE;AAC5D,YAAQ,IAAIE,OAAM,KAAK,oBAAoB,SAAS,WAAW,WAAW,cAAc,CAAC;AAAA,EAC3F;AAGA,QAAM,iBAAiB,4BAA4B,SAAS;AAC5D,QAAM,oBAAoB;AAAA,IACvB,iBAAiB,cAAc,SAAS,KACxC,gBAAgB,SAAS,cAAc,eAAe,MAAM,SAAS;AAAA,EACxE;AAEA,MAAI,gBAAgB,SAAS,QAAQ;AACnC,YAAQ,IAAI,EAAE;AACd,YAAQ,IAAIA,OAAM,OAAO,oCAA0B,CAAC;AACpD,YAAQ,IAAIA,OAAM,KAAK,cAAc,eAAe,MAAM,EAAE,CAAC;AAC7D,YAAQ,IAAIA,OAAM,KAAK,mCAAmC,CAAC;AAE3D,QAAI,CAAC,QAAQ;AACX,MAAAH,IAAG,UAAU,WAAW,EAAE,WAAW,KAAK,CAAC;AAC3C,YAAM,aAAa;AAAA,QACjB,SAAS;AAAA,QACT,QAAQ,eAAe;AAAA,QACvB,aAAa,eAAe;AAAA,QAC5B,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MACpC;AACA,MAAAA,IAAG,cAAcC,MAAK,KAAK,WAAW,cAAc,GAAG,KAAK,UAAU,YAAY,MAAM,CAAC,CAAC;AAAA,IAC5F;AAEA,YAAQ,IAAIE,OAAM,MAAM,kDAA6C,CAAC;AACtE;AAAA,EACF;AAGA,MAAI,iBAAiB,MAAM,gBAAgB,MAAM;AACjD,UAAQ,IAAIA,OAAM,KAAK,SAAS,eAAe,MAAM,+BAA+B,CAAC;AAGrF,MAAI,iBAAiB,cAAc,SAAS,GAAG;AAC7C,UAAM,gBAAgB,IAAI,IAAI,aAAa;AAC3C,UAAM,gBAAgB,eAAe;AACrC,qBAAiB,eAAe,OAAO,CAAC,MAAM,cAAc,IAAI,EAAE,IAAI,CAAC;AACvE,YAAQ,IAAI,EAAE;AACd,YAAQ,IAAIA,OAAM,KAAK,yCAAkC,eAAe,MAAM,OAAO,aAAa,QAAQ,CAAC;AAAA,EAC7G,WAES,gBAAgB,SAAS,cAAc,eAAe,MAAM,SAAS,GAAG;AAC/E,UAAM,gBAAgB,IAAI,IAAI,eAAe,KAAK;AAClD,UAAM,gBAAgB,eAAe;AACrC,qBAAiB,eAAe,OAAO,CAAC,MAAM,cAAc,IAAI,EAAE,IAAI,CAAC;AACvE,YAAQ,IAAI,EAAE;AACd,YAAQ,IAAIA,OAAM,KAAK,yCAAkC,eAAe,MAAM,OAAO,aAAa,QAAQ,CAAC;AAC3G,YAAQ,IAAIA,OAAM,KAAK,cAAc,eAAe,MAAM,EAAE,CAAC;AAAA,EAC/D;AAGA,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,mBAAmB;AAC/B,aAAW,KAAK,gBAAgB;AAC9B,YAAQ,IAAIA,OAAM,KAAK,OAAO,EAAE,IAAI,KAAK,EAAE,OAAO,IAAI,KAAK,EAAE,IAAI,EAAE,CAAC;AAAA,EACtE;AAEA,MAAI,eAAe,WAAW,GAAG;AAC/B,YAAQ,IAAIA,OAAM,OAAO,sCAA4B,CAAC;AACtD;AAAA,EACF;AAEA,MAAI,QAAQ;AACV,YAAQ,IAAIA,OAAM,OAAO,6CAA6C,CAAC;AACvE;AAAA,EACF;AAGA,QAAM,EAAE,SAAS,IAAI,MAAM,OAAO,YAAY;AAG9C,MAAIH,IAAG,WAAW,SAAS,GAAG;AAC5B,IAAAA,IAAG,OAAO,WAAW,EAAE,WAAW,KAAK,CAAC;AAAA,EAC1C;AACA,EAAAA,IAAG,UAAU,WAAW,EAAE,WAAW,KAAK,CAAC;AAE3C,QAAM,UAAUI,KAAI,sBAAsB,EAAE,MAAM;AAClD,QAAM,UAAU,MAAM,SAAS,OAAO;AACtC,UAAQ,QAAQ,kBAAkB;AAElC,QAAM,YAA4B,CAAC;AACnC,QAAM,WAAqB,CAAC;AAE5B,aAAW,YAAY,gBAAgB;AACrC,UAAM,cAAcA,KAAI,aAAa,SAAS,IAAI,KAAK,EAAE,MAAM;AAE/D,QAAI;AACF,YAAM,gBAAgB,wBAAwB,eAAe,SAAS,IAAI;AAC1E,YAAM,WAAW,MAAM;AAAA,QACrB;AAAA,QACA;AAAA,QACA,OAAO;AAAA,QACP,OAAO;AAAA,QACP;AAAA,QACA;AAAA,QACA,WAAW;AAAA,MACb;AACA,gBAAU,KAAK,QAAQ;AAGvB,YAAM,eAAeH,MAAK,KAAK,WAAW,OAAO,UAAU,SAAS,MAAM,eAAe;AACzF,MAAAD,IAAG,cAAc,cAAc,KAAK,UAAU,UAAU,MAAM,CAAC,CAAC;AAEhE,kBAAY,QAAQ,GAAG,SAAS,IAAI,KAAK,SAAS,SAAS,MAAM,oBAAoB;AAAA,IACvF,SAAS,OAAO;AACd,YAAM,UAAU,GAAG,SAAS,IAAI,KAAM,MAAgB,OAAO;AAC7D,eAAS,KAAK,OAAO;AACrB,kBAAY,KAAK,GAAG,SAAS,IAAI,cAAe,MAAgB,OAAO,EAAE;AAAA,IAC3E;AAAA,EACF;AAEA,QAAM,QAAQ,MAAM;AAGpB,QAAM,WAA6B;AAAA,IACjC,UAAU,OAAO;AAAA,IACjB,aAAY,oBAAI,KAAK,GAAE,YAAY;AAAA,IACnC,iBAAiB,QAAQ,IAAI;AAAA,IAC7B,eAAe,QAAQ,IAAI,cAAc;AAAA,IACzC,oBAAoB,OAAO;AAAA,IAC3B,OAAO,UAAU,IAAI,CAAC,OAAO;AAAA,MAC3B,UAAU,EAAE;AAAA,MACZ,UAAU,EAAE;AAAA,MACZ,UAAU,EAAE;AAAA,MACZ,WAAW,EAAE;AAAA,MACb,OAAO,EAAE;AAAA,MACT,YAAY,EAAE;AAAA,MACd,cAAc,EAAE,SAAS;AAAA,MACzB,aAAa;AAAA,IACf,EAAE;AAAA,EACJ;AAEA,QAAM,eAAeC,MAAK,KAAK,WAAW,OAAO,UAAU,eAAe;AAC1E,MAAI,mBAAmB;AACrB,YAAQ,IAAIE,OAAM,KAAK,kEAAkE,CAAC;AAAA,EAC5F,OAAO;AACL,IAAAH,IAAG,cAAc,cAAc,KAAK,UAAU,UAAU,MAAM,CAAC,CAAC;AAAA,EAClE;AAEA,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAIG,OAAM,MAAM,mCAA8B,CAAC;AACvD,UAAQ,IAAIA,OAAM,KAAK,eAAe,UAAU,MAAM,QAAQ,CAAC;AAC/D,UAAQ,IAAIA,OAAM,KAAK,iBAAiB,UAAU,OAAO,CAAC,MAAM,EAAE,SAAS,QAAQ,EAAE,MAAM,EAAE,CAAC;AAC9F,UAAQ,IAAIA,OAAM,KAAK,iBAAiB,UAAU,OAAO,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,MAAM,EAAE,CAAC;AAC7F,UAAQ,IAAIA,OAAM,KAAK,iBAAiB,UAAU,OAAO,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,MAAM,EAAE,CAAC;AAC/F,UAAQ,IAAIA,OAAM,KAAK,cAAc,SAAS,EAAE,CAAC;AAEjD,MAAI,SAAS,SAAS,GAAG;AACvB,UAAM,IAAI,MAAM,+BAA+B,SAAS,MAAM,aAAa,SAAS,KAAK,IAAI,CAAC,EAAE;AAAA,EAClG;AACF;;;AnBpkCA,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,KAAK,EACV,YAAY,4DAA4D,EACxE,QAAQ,OAAO;AAMlB,QACG,QAAQ,OAAO,EACf,YAAY,iDAAiD,EAC7D,OAAO,YAAY;AAClB,QAAM,aAAa;AACrB,CAAC;AAEH,QACG,QAAQ,QAAQ,EAChB,YAAY,0BAA0B,EACtC,OAAO,YAAY;AAClB,QAAM,cAAc;AACtB,CAAC;AAEH,QACG,QAAQ,QAAQ,EAChB,YAAY,oCAAoC,EAChD,OAAO,YAAY;AAClB,QAAM,cAAc;AACtB,CAAC;AAMH,IAAM,WAAW,QACd,QAAQ,OAAO,EACf,YAAY,uBAAuB;AAEtC,SACG,QAAQ,MAAM,EACd,YAAY,+BAA+B,EAC3C,OAAO,YAAY;AAClB,QAAM,iBAAiB;AACzB,CAAC;AAEH,SACG,QAAQ,aAAa,EACrB,YAAY,kCAAkC,EAC9C,OAAO,OAAO,SAAiB;AAC9B,QAAM,gBAAgB,IAAI;AAC5B,CAAC;AAMH,QACG,QAAQ,MAAM,EACd,YAAY,gDAAgD,EAC5D,eAAe,0BAA0B,gCAAgC,EACzE,eAAe,0BAA0B,0BAA0B,EACnE,OAAO,+BAA+B,+BAA+B,KAAK,EAC1E,OAAO,sBAAsB,oBAAoB,GAAG,EACpD,OAAO,aAAa,kDAAkD,EACtE,OAAO,WAAW,0BAA0B,EAC5C,OAAO,OAAO,YAAY;AACzB,QAAM,YAAY;AAAA,IAChB,UAAU,QAAQ;AAAA,IAClB,UAAU,QAAQ;AAAA,IAClB,WAAW,QAAQ;AAAA,IACnB,QAAQ,QAAQ;AAAA,IAChB,QAAQ,QAAQ;AAAA,IAChB,OAAO,QAAQ;AAAA,EACjB,CAAC;AACH,CAAC;AAMH,QACG,QAAQ,UAAU,EAClB,YAAY,mCAAmC,EAC/C,OAAO,sBAAsB,oBAAoB,GAAG,EACpD,OAAO,SAAS,8BAA8B,EAC9C,OAAO,iBAAiB,sBAAsB,EAC9C,OAAO,OAAO,YAAY;AACzB,QAAM,gBAAgB;AAAA,IACpB,QAAQ,QAAQ;AAAA,IAChB,KAAK,QAAQ;AAAA,IACb,SAAS,QAAQ;AAAA,EACnB,CAAC;AACH,CAAC;AAMH,QACG,QAAQ,OAAO,EACf,YAAY,gEAAgE,EAC5E,OAAO,sBAAsB,oBAAoB,GAAG,EACpD,OAAO,WAAW,+BAA+B,EACjD,OAAO,OAAO,YAAY;AACzB,QAAM,aAAa;AAAA,IACjB,QAAQ,QAAQ;AAAA,IAChB,OAAO,QAAQ;AAAA,EACjB,CAAC;AACH,CAAC;AAMH,QACG,QAAQ,mBAAmB,EAC3B,YAAY,mEAAmE,EAC/E,OAAO,sBAAsB,+CAA+C,GAAG,EAC/E,OAAO,wBAAwB,gCAAgC,uBAAuB,EACtF,OAAO,uBAAuB,+CAA+C,EAC7E,OAAO,aAAa,uDAAuD,EAC3E,OAAO,iBAAiB,sBAAsB,EAC9C,OAAO,OAAO,YAAY;AACzB,QAAM,QAAQ,QAAQ,QAAQ,QAAQ,MAAM,MAAM,GAAG,EAAE,IAAI,CAAC,MAAc,EAAE,KAAK,CAAC,IAAI;AACtF,QAAM,wBAAwB;AAAA,IAC5B,QAAQ,QAAQ;AAAA,IAChB,SAAS,QAAQ;AAAA,IACjB,QAAQ,QAAQ;AAAA,IAChB,SAAS,QAAQ;AAAA,IACjB;AAAA,EACF,CAAC;AACH,CAAC;AAMH,QAAQ,aAAa;AAErB,IAAI;AACF,QAAM,QAAQ,WAAW,QAAQ,IAAI;AACvC,SAAS,OAAO;AACd,MAAI,iBAAiB,OAAO;AAE1B,QAAK,MAA4B,SAAS,6BACrC,MAA4B,SAAS,qBAAqB;AAC7D,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,YAAQ,MAAMI,OAAM,IAAI,QAAQ,GAAG,MAAM,OAAO;AAChD,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;","names":["chalk","chalk","fs","override","base","fs","os","os","fs","EXT","fs","fs","EXT","resolve","data","chalk","chalk","DEFAULT_API_URL","path","chalk","fs","path","chalk","ora","chalk","ora","path","fs","fs","path","chalk","fs","path","chalk","yaml","fs","path","yaml","chalk","ora","resolve","resolved","chalk"]}