{"version":3,"file":"fly-commands.mjs","names":[],"sources":["../../../src/commands/fly-commands.ts"],"sourcesContent":["/**\n * Fly control commands — manage the Fly.io deployment from Telegram.\n *\n * /provider             — Show current LLM provider + switch options\n * /provider anthropic   — Switch to Anthropic (direct)\n * /provider bankr       — Switch to Bankr LLM Gateway\n * /provider openrouter  — Switch to OpenRouter\n *\n * /flykeys              — List configured secrets (names only, never values)\n * /flykeys set KEY val  — Set a secret\n * /flykeys rm KEY       — Remove a secret\n *\n * /flystatus            — Machine status, region, uptime\n *\n * /flyrestart           — Restart the machine (picks up new secrets)\n *\n * SECURITY:\n * - All commands require auth (requireAuth: true)\n * - Requires FLY_API_TOKEN set as a Fly secret\n * - Secret values are write-only (Fly never returns plaintext)\n * - The bot restarts after provider/secret changes (entrypoint.sh re-reads config)\n */\n\nimport {\n  isFlyControlAvailable,\n  getCurrentProvider,\n  isValidProvider,\n  setProvider,\n  scheduleRestart,\n  listSecrets,\n  setSecrets,\n  deleteSecret,\n  listMachines,\n  restartAllMachines,\n  FlyNotConfiguredError,\n  type LlmProvider,\n} from '../services/fly-control-service.js';\n\n// ─── Setup instructions (shared) ────────────────────────────────────────\n\nconst SETUP_TEXT = [\n  '**Fly Control not configured**',\n  '',\n  'To manage your deploy from Telegram, set a Fly API token:',\n  '',\n  '```',\n  'fly tokens create deploy -a openclawnch-tg',\n  '# Copy the token, then:',\n  'fly secrets set FLY_API_TOKEN=\"FlyV1 ...\" -a openclawnch-tg',\n  '```',\n  '',\n  'After that, /provider, /flykeys, /flystatus, and /flyrestart will work.',\n].join('\\n');\n\nfunction notConfigured(): { text: string } {\n  return { text: SETUP_TEXT };\n}\n\nfunction formatError(err: unknown): string {\n  if (err instanceof FlyNotConfiguredError) return SETUP_TEXT;\n  return err instanceof Error ? err.message : String(err);\n}\n\n// ─── /provider ──────────────────────────────────────────────────────────\n\nconst PROVIDER_LABELS: Record<string, string> = {\n  anthropic: 'Anthropic (direct)',\n  bankr: 'Bankr LLM Gateway (pay with crypto)',\n  openrouter: 'OpenRouter',\n  openai: 'OpenAI (direct)',\n};\n\nconst PROVIDER_KEYS: Record<string, string> = {\n  anthropic: 'ANTHROPIC_API_KEY',\n  bankr: 'BANKR_LLM_KEY',\n  openrouter: 'OPENROUTER_API_KEY',\n  openai: 'OPENAI_API_KEY',\n};\n\nexport const providerCommand = {\n  name: 'provider',\n  description: 'View current LLM provider and switch options',\n  acceptsArgs: false,\n  requireAuth: true,\n  handler: async (_ctx: any) => {\n    const current = getCurrentProvider();\n    const currentLabel = PROVIDER_LABELS[current] ?? current;\n\n    if (!isFlyControlAvailable()) {\n      return {\n        text: [\n          `**LLM Provider:** ${currentLabel}`,\n          '',\n          '**Switch provider:**',\n          '  /provider_anthropic — Claude direct',\n          '  /provider_bankr — Bankr Gateway (pay with crypto)',\n          '  /provider_openrouter — OpenRouter',\n          '  /provider_openai — OpenAI direct',\n          '',\n          'Switching requires Fly control. Set FLY_API_TOKEN first.',\n          '',\n          'Current model: /llm',\n        ].join('\\n'),\n      };\n    }\n\n    return {\n      text: [\n        `**LLM Provider:** ${currentLabel}`,\n        '',\n        '**Switch provider:**',\n        '  /provider_anthropic — Claude direct',\n        '  /provider_bankr — Bankr Gateway (pay with crypto)',\n        '  /provider_openrouter — OpenRouter (multi-model)',\n        '  /provider_openai — OpenAI direct',\n        '',\n        'Switching restarts the bot (~40s). Model resets to default for the new provider.',\n        '',\n        'Current model: /llm',\n      ].join('\\n'),\n    };\n  },\n};\n\n/** Shared handler for /provider_<name> commands. */\nasync function handleProviderSwitch(providerArg: LlmProvider): Promise<{ text: string }> {\n  if (!isFlyControlAvailable()) {\n    return notConfigured();\n  }\n\n  const current = getCurrentProvider();\n  const currentLabel = PROVIDER_LABELS[current] ?? current;\n\n  if (providerArg === current) {\n    return { text: `Already using ${currentLabel}. No change needed.` };\n  }\n\n  const requiredKey = PROVIDER_KEYS[providerArg];\n  if (requiredKey && !process.env[requiredKey]) {\n    // Key might be set as a Fly secret but not yet in process.env (requires restart).\n    // Check Fly secrets before rejecting.\n    let keyExistsInFly = false;\n    try {\n      const secrets = await listSecrets();\n      keyExistsInFly = secrets.some((s: any) => s.name === requiredKey);\n    } catch {\n      // Can't check Fly secrets — fall through to the error message\n    }\n\n    if (!keyExistsInFly) {\n      return {\n        text: [\n          `**Missing API key for ${PROVIDER_LABELS[providerArg] ?? providerArg}**`,\n          '',\n          `Set \\`${requiredKey}\\` first, then try again.`,\n          `(Use /flykeys to manage API keys)`,\n        ].join('\\n'),\n      };\n    }\n    // Key exists in Fly but not in process.env — it'll be picked up after restart\n  }\n\n  try {\n    const targetLabel = PROVIDER_LABELS[providerArg] ?? providerArg;\n    const secretsVersion = await setProvider(providerArg);\n    scheduleRestart(2000, secretsVersion);\n    return {\n      text: `Switching to **${targetLabel}**. Restarting agent, please wait...\\n\\nThe bot will be back in ~40 seconds.`,\n    };\n  } catch (err) {\n    return { text: `Failed to switch provider: ${formatError(err)}` };\n  }\n}\n\nexport const providerAnthropicCommand = {\n  name: 'provider_anthropic',\n  description: 'Switch LLM to Anthropic (direct)',\n  acceptsArgs: false,\n  requireAuth: true,\n  handler: async () => handleProviderSwitch('anthropic'),\n};\n\nexport const providerBankrCommand = {\n  name: 'provider_bankr',\n  description: 'Switch LLM to Bankr Gateway (pay with crypto)',\n  acceptsArgs: false,\n  requireAuth: true,\n  handler: async () => handleProviderSwitch('bankr'),\n};\n\nexport const providerOpenrouterCommand = {\n  name: 'provider_openrouter',\n  description: 'Switch LLM to OpenRouter',\n  acceptsArgs: false,\n  requireAuth: true,\n  handler: async () => handleProviderSwitch('openrouter'),\n};\n\nexport const providerOpenaiCommand = {\n  name: 'provider_openai',\n  description: 'Switch LLM to OpenAI (direct)',\n  acceptsArgs: false,\n  requireAuth: true,\n  handler: async () => handleProviderSwitch('openai' as LlmProvider),\n};\n\n// ─── /flykeys ───────────────────────────────────────────────────────────\n\n// Secrets that should never be shown or modified from Telegram\nconst PROTECTED_SECRETS = new Set([\n  'FLY_API_TOKEN',         // Modifying this would lock yourself out\n  'TELEGRAM_BOT_TOKEN',    // Breaking this kills the bot\n]);\n\n// Secrets that are safe to display and set from Telegram\nconst KNOWN_SECRETS: Record<string, string> = {\n  // LLM providers\n  ANTHROPIC_API_KEY: 'Anthropic direct key',\n  BANKR_LLM_KEY: 'Bankr LLM Gateway key',\n  BANKR_API_KEY: 'Bankr Agent API key',\n  OPENROUTER_API_KEY: 'OpenRouter key',\n  OPENAI_API_KEY: 'OpenAI direct key',\n  OPENCLAWNCH_LLM_PROVIDER: 'LLM provider (anthropic/bankr/openrouter)',\n  // Wallet\n  WALLETCONNECT_PROJECT_ID: 'WalletConnect project ID',\n  CLAWNCHER_PRIVATE_KEY: 'Private key (autosign)',\n  CLAWNCHER_RPC_URL: 'Custom RPC URL',\n  ALLOW_PRIVATE_KEY_MODE: 'Enable private key mode (true/false)',\n  // Block explorer & analytics\n  BASESCAN_API_KEY: 'Basescan API key',\n  ETHERSCAN_API_KEY: 'Etherscan API key',\n  ALCHEMY_API_KEY: 'Alchemy API key',\n  COINGECKO_API_KEY: 'CoinGecko API key',\n  // Integration services\n  HERD_ACCESS_TOKEN: 'Herd Intelligence token',\n  HUMMINGBOT_API_URL: 'Hummingbot API URL',\n  MOLTEN_API_KEY: 'Molten API key',\n  LIFI_API_KEY: 'LI.FI bridge API key',\n  // X/Twitter (ClawnX)\n  X_API_KEY: 'X/Twitter API key',\n  X_API_SECRET: 'X/Twitter API secret',\n  X_ACCESS_TOKEN: 'X/Twitter access token',\n  X_ACCESS_TOKEN_SECRET: 'X/Twitter access token secret',\n  X_BEARER_TOKEN: 'X/Twitter bearer token (optional)',\n};\n\nexport const flykeysCommand = {\n  name: 'flykeys',\n  description: 'List, set, or remove Fly secrets (e.g. /flykeys set KEY value)',\n  acceptsArgs: true,\n  requireAuth: true,\n  handler: async (ctx: any) => {\n    if (!isFlyControlAvailable()) return notConfigured();\n\n    const args = (ctx?.args ?? ctx?.text ?? '').trim();\n    const parts = args.replace(/^\\/flykeys\\s*/, '').trim().split(/\\s+/);\n    const action = parts[0]?.toLowerCase() ?? '';\n\n    // /flykeys — list\n    if (!action) {\n      try {\n        const secrets = await listSecrets();\n        if (secrets.length === 0) {\n          return { text: 'No secrets configured.' };\n        }\n\n        const lines = ['**Configured Secrets:**', ''];\n        for (const s of secrets) {\n          const label = KNOWN_SECRETS[s.name];\n          const protected_ = PROTECTED_SECRETS.has(s.name) ? ' (protected)' : '';\n          lines.push(`  \\`${s.name}\\`${label ? ` — ${label}` : ''}${protected_}`);\n        }\n\n        lines.push(\n          '',\n          '**Set a secret:**',\n          '  `/flykeys set KEY value`',\n          '',\n          '**Remove a secret:**',\n          '  `/flykeys rm KEY`',\n          '',\n          'Changes take effect after /flyrestart',\n        );\n        return { text: lines.join('\\n') };\n      } catch (err) {\n        return { text: `Failed to list secrets: ${formatError(err)}` };\n      }\n    }\n\n    // /flykeys set KEY value\n    if (action === 'set') {\n      const key = parts[1]?.toUpperCase();\n      // Value is everything after the key (may contain spaces)\n      const value = parts.slice(2).join(' ');\n\n      if (!key || !value) {\n        return {\n          text: [\n            '**Usage:** `/flykeys set KEY value`',\n            '',\n            '**Examples:**',\n            '  `/flykeys set BANKR_LLM_KEY bk_abc123`',\n            '  `/flykeys set ANTHROPIC_API_KEY sk-ant-abc123`',\n            '  `/flykeys set OPENCLAWNCH_LLM_PROVIDER bankr`',\n          ].join('\\n'),\n        };\n      }\n\n      if (PROTECTED_SECRETS.has(key)) {\n        return { text: `\\`${key}\\` is protected and cannot be modified from Telegram. Use the Fly CLI.` };\n      }\n\n      // H1 FIX: Allowlist — only permit known secret keys to prevent arbitrary env var injection\n      if (!KNOWN_SECRETS[key]) {\n        return {\n          text: `\\`${key}\\` is not a recognized secret. Only these keys can be set:\\n\\n${Object.entries(KNOWN_SECRETS).map(([k, v]) => `  \\`${k}\\` — ${v}`).join('\\n')}`,\n        };\n      }\n\n      try {\n        await setSecrets({ [key]: value });\n\n        const needsRestart = key !== 'OPENCLAWNCH_LLM_PROVIDER';\n        const hint = needsRestart\n          ? '\\n\\nRun /flyrestart to pick up the new value.'\n          : '\\n\\nUse /provider to switch (it auto-restarts).';\n\n        return { text: `Secret \\`${key}\\` set successfully.${hint}\\n\\n**Security:** Delete your message above — it contains the secret value in plaintext.` };\n      } catch (err) {\n        return { text: `Failed to set secret: ${formatError(err)}` };\n      }\n    }\n\n    // /flykeys rm KEY\n    if (action === 'rm' || action === 'remove' || action === 'delete') {\n      const key = parts[1]?.toUpperCase();\n\n      if (!key) {\n        return { text: '**Usage:** `/flykeys rm KEY`' };\n      }\n\n      if (PROTECTED_SECRETS.has(key)) {\n        return { text: `\\`${key}\\` is protected and cannot be removed from Telegram. Use the Fly CLI.` };\n      }\n\n      try {\n        await deleteSecret(key);\n        return { text: `Secret \\`${key}\\` removed.\\n\\nRun /flyrestart to pick up the change.` };\n      } catch (err) {\n        return { text: `Failed to remove secret: ${formatError(err)}` };\n      }\n    }\n\n    return {\n      text: [\n        '**Unknown action:** ' + action,\n        '',\n        '**Usage:**',\n        '  /flykeys — list all secrets',\n        '  `/flykeys set KEY value` — set a secret',\n        '  `/flykeys rm KEY` — remove a secret',\n      ].join('\\n'),\n    };\n  },\n};\n\n// ─── /flystatus ─────────────────────────────────────────────────────────\n\nexport const flystatusCommand = {\n  name: 'flystatus',\n  description: 'Show machine status, region, and uptime',\n  acceptsArgs: false,\n  requireAuth: true,\n  handler: async (_ctx: any) => {\n    if (!isFlyControlAvailable()) return notConfigured();\n\n    try {\n      const machines = await listMachines();\n\n      if (machines.length === 0) {\n        return { text: 'No machines found for this app.' };\n      }\n\n      const lines = [\n        `**OpenClawnch Deploy Status**`,\n        `App: \\`${process.env.FLY_APP_NAME}\\``,\n        '',\n      ];\n\n      for (const m of machines) {\n        const uptime = m.updatedAt ? timeSince(m.updatedAt) : 'unknown';\n        const stateEmoji = m.state === 'started' ? 'running' : m.state;\n\n        lines.push(\n          `**Machine:** \\`${m.id}\\``,\n          `  State: ${stateEmoji}`,\n          `  Region: ${m.region}`,\n          `  CPU: ${m.cpuKind} ${m.cpus}x / ${m.memoryMb}MB RAM`,\n          `  Last update: ${uptime} ago`,\n          '',\n        );\n      }\n\n      lines.push(\n        '**Quick actions:**',\n        '  /flyrestart — restart the bot',\n        '  /provider — switch LLM provider',\n        '  /flykeys — manage API keys',\n      );\n\n      return { text: lines.join('\\n') };\n    } catch (err) {\n      return { text: `Failed to get status: ${formatError(err)}` };\n    }\n  },\n};\n\n// ─── /flyrestart ────────────────────────────────────────────────────────\n\nexport const flyrestartCommand = {\n  name: 'flyrestart',\n  description: 'Restart the bot (picks up new secrets)',\n  acceptsArgs: false,\n  requireAuth: true,\n  handler: async (_ctx: any) => {\n    if (!isFlyControlAvailable()) return notConfigured();\n\n    // Schedule restart after response is delivered\n    scheduleRestart(2000);\n\n    return {\n      text: 'Restarting agent, please wait...\\n\\nThe bot will be back in ~40 seconds.',\n    };\n  },\n};\n\n// ─── Helpers ─────────────────────────────────────────────────────────────\n\nfunction timeSince(dateStr: string): string {\n  try {\n    const now = Date.now();\n    const then = new Date(dateStr).getTime();\n    const diff = now - then;\n\n    if (diff < 0) return 'just now';\n\n    const seconds = Math.floor(diff / 1000);\n    if (seconds < 60) return `${seconds}s`;\n\n    const minutes = Math.floor(seconds / 60);\n    if (minutes < 60) return `${minutes}m`;\n\n    const hours = Math.floor(minutes / 60);\n    if (hours < 24) return `${hours}h ${minutes % 60}m`;\n\n    const days = Math.floor(hours / 24);\n    return `${days}d ${hours % 24}h`;\n  } catch {\n    return 'unknown';\n  }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAwCA,MAAM,aAAa;CACjB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC,KAAK,KAAK;AAEZ,SAAS,gBAAkC;AACzC,QAAO,EAAE,MAAM,YAAY;;AAG7B,SAAS,YAAY,KAAsB;AACzC,KAAI,eAAe,sBAAuB,QAAO;AACjD,QAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;;AAKzD,MAAM,kBAA0C;CAC9C,WAAW;CACX,OAAO;CACP,YAAY;CACZ,QAAQ;CACT;AAED,MAAM,gBAAwC;CAC5C,WAAW;CACX,OAAO;CACP,YAAY;CACZ,QAAQ;CACT;AAED,MAAa,kBAAkB;CAC7B,MAAM;CACN,aAAa;CACb,aAAa;CACb,aAAa;CACb,SAAS,OAAO,SAAc;EAC5B,MAAM,UAAU,oBAAoB;EACpC,MAAM,eAAe,gBAAgB,YAAY;AAEjD,MAAI,CAAC,uBAAuB,CAC1B,QAAO,EACL,MAAM;GACJ,qBAAqB;GACrB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC,KAAK,KAAK,EACb;AAGH,SAAO,EACL,MAAM;GACJ,qBAAqB;GACrB;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC,KAAK,KAAK,EACb;;CAEJ;;AAGD,eAAe,qBAAqB,aAAqD;AACvF,KAAI,CAAC,uBAAuB,CAC1B,QAAO,eAAe;CAGxB,MAAM,UAAU,oBAAoB;CACpC,MAAM,eAAe,gBAAgB,YAAY;AAEjD,KAAI,gBAAgB,QAClB,QAAO,EAAE,MAAM,iBAAiB,aAAa,sBAAsB;CAGrE,MAAM,cAAc,cAAc;AAClC,KAAI,eAAe,CAAC,QAAQ,IAAI,cAAc;EAG5C,IAAI,iBAAiB;AACrB,MAAI;AAEF,qBADgB,MAAM,aAAa,EACV,MAAM,MAAW,EAAE,SAAS,YAAY;UAC3D;AAIR,MAAI,CAAC,eACH,QAAO,EACL,MAAM;GACJ,yBAAyB,gBAAgB,gBAAgB,YAAY;GACrE;GACA,SAAS,YAAY;GACrB;GACD,CAAC,KAAK,KAAK,EACb;;AAKL,KAAI;EACF,MAAM,cAAc,gBAAgB,gBAAgB;AAEpD,kBAAgB,KADO,MAAM,YAAY,YAAY,CAChB;AACrC,SAAO,EACL,MAAM,kBAAkB,YAAY,+EACrC;UACM,KAAK;AACZ,SAAO,EAAE,MAAM,8BAA8B,YAAY,IAAI,IAAI;;;AAIrE,MAAa,2BAA2B;CACtC,MAAM;CACN,aAAa;CACb,aAAa;CACb,aAAa;CACb,SAAS,YAAY,qBAAqB,YAAY;CACvD;AAED,MAAa,uBAAuB;CAClC,MAAM;CACN,aAAa;CACb,aAAa;CACb,aAAa;CACb,SAAS,YAAY,qBAAqB,QAAQ;CACnD;AAED,MAAa,4BAA4B;CACvC,MAAM;CACN,aAAa;CACb,aAAa;CACb,aAAa;CACb,SAAS,YAAY,qBAAqB,aAAa;CACxD;AAED,MAAa,wBAAwB;CACnC,MAAM;CACN,aAAa;CACb,aAAa;CACb,aAAa;CACb,SAAS,YAAY,qBAAqB,SAAwB;CACnE;AAKD,MAAM,oBAAoB,IAAI,IAAI,CAChC,iBACA,qBACD,CAAC;AAGF,MAAM,gBAAwC;CAE5C,mBAAmB;CACnB,eAAe;CACf,eAAe;CACf,oBAAoB;CACpB,gBAAgB;CAChB,0BAA0B;CAE1B,0BAA0B;CAC1B,uBAAuB;CACvB,mBAAmB;CACnB,wBAAwB;CAExB,kBAAkB;CAClB,mBAAmB;CACnB,iBAAiB;CACjB,mBAAmB;CAEnB,mBAAmB;CACnB,oBAAoB;CACpB,gBAAgB;CAChB,cAAc;CAEd,WAAW;CACX,cAAc;CACd,gBAAgB;CAChB,uBAAuB;CACvB,gBAAgB;CACjB;AAED,MAAa,iBAAiB;CAC5B,MAAM;CACN,aAAa;CACb,aAAa;CACb,aAAa;CACb,SAAS,OAAO,QAAa;AAC3B,MAAI,CAAC,uBAAuB,CAAE,QAAO,eAAe;EAGpD,MAAM,SADQ,KAAK,QAAQ,KAAK,QAAQ,IAAI,MAAM,CAC/B,QAAQ,iBAAiB,GAAG,CAAC,MAAM,CAAC,MAAM,MAAM;EACnE,MAAM,SAAS,MAAM,IAAI,aAAa,IAAI;AAG1C,MAAI,CAAC,OACH,KAAI;GACF,MAAM,UAAU,MAAM,aAAa;AACnC,OAAI,QAAQ,WAAW,EACrB,QAAO,EAAE,MAAM,0BAA0B;GAG3C,MAAM,QAAQ,CAAC,2BAA2B,GAAG;AAC7C,QAAK,MAAM,KAAK,SAAS;IACvB,MAAM,QAAQ,cAAc,EAAE;IAC9B,MAAM,aAAa,kBAAkB,IAAI,EAAE,KAAK,GAAG,iBAAiB;AACpE,UAAM,KAAK,OAAO,EAAE,KAAK,IAAI,QAAQ,MAAM,UAAU,KAAK,aAAa;;AAGzE,SAAM,KACJ,IACA,qBACA,8BACA,IACA,wBACA,uBACA,IACA,wCACD;AACD,UAAO,EAAE,MAAM,MAAM,KAAK,KAAK,EAAE;WAC1B,KAAK;AACZ,UAAO,EAAE,MAAM,2BAA2B,YAAY,IAAI,IAAI;;AAKlE,MAAI,WAAW,OAAO;GACpB,MAAM,MAAM,MAAM,IAAI,aAAa;GAEnC,MAAM,QAAQ,MAAM,MAAM,EAAE,CAAC,KAAK,IAAI;AAEtC,OAAI,CAAC,OAAO,CAAC,MACX,QAAO,EACL,MAAM;IACJ;IACA;IACA;IACA;IACA;IACA;IACD,CAAC,KAAK,KAAK,EACb;AAGH,OAAI,kBAAkB,IAAI,IAAI,CAC5B,QAAO,EAAE,MAAM,KAAK,IAAI,yEAAyE;AAInG,OAAI,CAAC,cAAc,KACjB,QAAO,EACL,MAAM,KAAK,IAAI,gEAAgE,OAAO,QAAQ,cAAc,CAAC,KAAK,CAAC,GAAG,OAAO,OAAO,EAAE,OAAO,IAAI,CAAC,KAAK,KAAK,IAC7J;AAGH,OAAI;AACF,UAAM,WAAW,GAAG,MAAM,OAAO,CAAC;AAOlC,WAAO,EAAE,MAAM,YAAY,IAAI,sBALV,QAAQ,6BAEzB,kDACA,kDAEsD,2FAA2F;YAC9I,KAAK;AACZ,WAAO,EAAE,MAAM,yBAAyB,YAAY,IAAI,IAAI;;;AAKhE,MAAI,WAAW,QAAQ,WAAW,YAAY,WAAW,UAAU;GACjE,MAAM,MAAM,MAAM,IAAI,aAAa;AAEnC,OAAI,CAAC,IACH,QAAO,EAAE,MAAM,gCAAgC;AAGjD,OAAI,kBAAkB,IAAI,IAAI,CAC5B,QAAO,EAAE,MAAM,KAAK,IAAI,wEAAwE;AAGlG,OAAI;AACF,UAAM,aAAa,IAAI;AACvB,WAAO,EAAE,MAAM,YAAY,IAAI,wDAAwD;YAChF,KAAK;AACZ,WAAO,EAAE,MAAM,4BAA4B,YAAY,IAAI,IAAI;;;AAInE,SAAO,EACL,MAAM;GACJ,yBAAyB;GACzB;GACA;GACA;GACA;GACA;GACD,CAAC,KAAK,KAAK,EACb;;CAEJ;AAID,MAAa,mBAAmB;CAC9B,MAAM;CACN,aAAa;CACb,aAAa;CACb,aAAa;CACb,SAAS,OAAO,SAAc;AAC5B,MAAI,CAAC,uBAAuB,CAAE,QAAO,eAAe;AAEpD,MAAI;GACF,MAAM,WAAW,MAAM,cAAc;AAErC,OAAI,SAAS,WAAW,EACtB,QAAO,EAAE,MAAM,mCAAmC;GAGpD,MAAM,QAAQ;IACZ;IACA,UAAU,QAAQ,IAAI,aAAa;IACnC;IACD;AAED,QAAK,MAAM,KAAK,UAAU;IACxB,MAAM,SAAS,EAAE,YAAY,UAAU,EAAE,UAAU,GAAG;IACtD,MAAM,aAAa,EAAE,UAAU,YAAY,YAAY,EAAE;AAEzD,UAAM,KACJ,kBAAkB,EAAE,GAAG,KACvB,YAAY,cACZ,aAAa,EAAE,UACf,UAAU,EAAE,QAAQ,GAAG,EAAE,KAAK,MAAM,EAAE,SAAS,SAC/C,kBAAkB,OAAO,OACzB,GACD;;AAGH,SAAM,KACJ,sBACA,mCACA,qCACA,+BACD;AAED,UAAO,EAAE,MAAM,MAAM,KAAK,KAAK,EAAE;WAC1B,KAAK;AACZ,UAAO,EAAE,MAAM,yBAAyB,YAAY,IAAI,IAAI;;;CAGjE;AAID,MAAa,oBAAoB;CAC/B,MAAM;CACN,aAAa;CACb,aAAa;CACb,aAAa;CACb,SAAS,OAAO,SAAc;AAC5B,MAAI,CAAC,uBAAuB,CAAE,QAAO,eAAe;AAGpD,kBAAgB,IAAK;AAErB,SAAO,EACL,MAAM,4EACP;;CAEJ;AAID,SAAS,UAAU,SAAyB;AAC1C,KAAI;EAGF,MAAM,OAFM,KAAK,KAAK,GACT,IAAI,KAAK,QAAQ,CAAC,SAAS;AAGxC,MAAI,OAAO,EAAG,QAAO;EAErB,MAAM,UAAU,KAAK,MAAM,OAAO,IAAK;AACvC,MAAI,UAAU,GAAI,QAAO,GAAG,QAAQ;EAEpC,MAAM,UAAU,KAAK,MAAM,UAAU,GAAG;AACxC,MAAI,UAAU,GAAI,QAAO,GAAG,QAAQ;EAEpC,MAAM,QAAQ,KAAK,MAAM,UAAU,GAAG;AACtC,MAAI,QAAQ,GAAI,QAAO,GAAG,MAAM,IAAI,UAAU,GAAG;AAGjD,SAAO,GADM,KAAK,MAAM,QAAQ,GAAG,CACpB,IAAI,QAAQ,GAAG;SACxB;AACN,SAAO"}